diff --git a/.depcheckrc b/.depcheckrc index e9383a7985d..e71798a1ce0 100644 --- a/.depcheckrc +++ b/.depcheckrc @@ -1,18 +1,20 @@ ignores: [ # Dependencies that depcheck thinks are unused but are actually used - "@graphql-codegen/*", - "@commitlint/*", - "i18next", + '@graphql-codegen/*', + '@commitlint/*', + 'i18next', # Dependencies that depcheck thinks are missing but are actually present or never used - "@yarnpkg/core", - "@yarnpkg/cli", - "clipanion", - "@yarnpkg/fslib", - "bufferutil", - "utf-8-validate", - "@yarnpkg/parsers", - "@yarnpkg/plugin-git", - "semver", - "typanion", - "turbo-ignore", + '@yarnpkg/core', + '@yarnpkg/cli', + 'clipanion', + '@yarnpkg/fslib', + 'bufferutil', + 'utf-8-validate', + '@yarnpkg/parsers', + '@yarnpkg/plugin-git', + 'semver', + 'typanion', + 'turbo-ignore', + 'prettier', + 'prettier-plugin-organize-imports', ] diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000000..870998bf905 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,49 @@ +__generated__ +__mocks__ +.detoxrc.js +.eslintrc +.eslintrc.js +.prettierrc +.tamagui +.turbo +.turbo +.yarn +@types +*.graphql +*.html +*.inc +*.json +*.md +*.test.ts +*.yml +babel.config.js +build +craco.config.cjs +cypress +dist +jest-setup.js +jest.config.js +jest.config.js +metro.config.js +node_modules +tsconfig.json +types + +# app/package specific + +# mobile + +apps/mobile/ios +apps/mobile/android + +# extension + +apps/stretch/dev + +# packages + +packages/uniswap/codegen.ts + +packages/eslint-config/react.js +packages/eslint-config/restrictedImports.js +packages/eslint-config/native.js diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..bd9eac07dbf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "bracketSameLine": false, + "singleQuote": true, + "printWidth": 120, + "semi": false, + "plugins": [ + "prettier-plugin-organize-imports" + ] +} diff --git a/.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch b/.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch index f223709e81e..c2a68762c4e 100644 --- a/.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch +++ b/.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch @@ -1,8 +1,37 @@ diff --git a/android/src/main/java/com/mpiannucci/reactnativecontextmenu/ContextMenuView.java b/android/src/main/java/com/mpiannucci/reactnativecontextmenu/ContextMenuView.java -index 9fc8eeb9b6b66061fca818a17127ff6f21df3126..6a3c9f1addd4bd8bcea3afc2e60aca52cd4c8248 100644 +index 9fc8eeb9b6b66061fca818a17127ff6f21df3126..f975490e71a72967b112b7ef973119608d67fc68 100644 --- a/android/src/main/java/com/mpiannucci/reactnativecontextmenu/ContextMenuView.java +++ b/android/src/main/java/com/mpiannucci/reactnativecontextmenu/ContextMenuView.java -@@ -67,6 +67,14 @@ public class ContextMenuView extends ReactViewGroup implements View.OnCreateCont +@@ -37,6 +37,8 @@ public class ContextMenuView extends ReactViewGroup implements View.OnCreateCont + + boolean cancelled = true; + ++ int[] longPressStartLocation = new int[2]; ++ + protected boolean dropdownMenuMode = false; + + protected boolean disabled = false; +@@ -61,12 +63,40 @@ public class ContextMenuView extends ReactViewGroup implements View.OnCreateCont + + @Override + public void onLongPress(MotionEvent e) { ++ int[] location = new int[2]; ++ getLocationOnScreen(location); ++ ++ int dx = location[0] - longPressStartLocation[0]; ++ int dy = location[1] - longPressStartLocation[1]; ++ double distance = Math.sqrt(dx * dx + dy * dy); ++ // Cancel long press if the user moves their finger more than 10 pixels ++ // (e.g. the context menu is used inside the scrollable component and it ++ // moves as the user scrolls) ++ if (distance > 10) { ++ cancelled = true; ++ return; ++ } ++ + if (!dropdownMenuMode && !disabled) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + showContextMenu(e.getX(), e.getY()); } } } @@ -13,6 +42,12 @@ index 9fc8eeb9b6b66061fca818a17127ff6f21df3126..6a3c9f1addd4bd8bcea3afc2e60aca52 + showContextMenu(e.getX(), e.getY()); + } + return super.onSingleTapConfirmed(e); ++ } ++ ++ @Override ++ public boolean onDown(MotionEvent e) { ++ getLocationOnScreen(longPressStartLocation); ++ return super.onDown(e); + } }); } diff --git a/RELEASE b/RELEASE index ee2b8a0a64e..03953810774 100644 --- a/RELEASE +++ b/RELEASE @@ -1,6 +1,6 @@ IPFS hash of the deployment: -- CIDv0: `Qmf3UgVbYneiT58xJcLJYSpvoowcUmhgH1N21P68nkxpvn` -- CIDv1: `bafybeihygb3syfjqxcfwuu4ti6jbim4o2efhlbzg54qrk5feiwz4cqmvom` +- CIDv0: `QmagCqi7cKazCA4x7qyhwMvzpJPGHACvZGNZZ6LNLTS5La` +- CIDv1: `bafybeifxj2n7miiatvhem2m2ghihgdzuzdhqfatqdm3vle55dgb7lc65e4` The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org). @@ -10,15 +10,56 @@ You can also access the Uniswap Interface from an IPFS gateway. Your Uniswap settings are never remembered across different URLs. IPFS gateways: -- https://bafybeihygb3syfjqxcfwuu4ti6jbim4o2efhlbzg54qrk5feiwz4cqmvom.ipfs.dweb.link/ -- https://bafybeihygb3syfjqxcfwuu4ti6jbim4o2efhlbzg54qrk5feiwz4cqmvom.ipfs.cf-ipfs.com/ -- [ipfs://Qmf3UgVbYneiT58xJcLJYSpvoowcUmhgH1N21P68nkxpvn/](ipfs://Qmf3UgVbYneiT58xJcLJYSpvoowcUmhgH1N21P68nkxpvn/) +- https://bafybeifxj2n7miiatvhem2m2ghihgdzuzdhqfatqdm3vle55dgb7lc65e4.ipfs.dweb.link/ +- https://bafybeifxj2n7miiatvhem2m2ghihgdzuzdhqfatqdm3vle55dgb7lc65e4.ipfs.cf-ipfs.com/ +- [ipfs://QmagCqi7cKazCA4x7qyhwMvzpJPGHACvZGNZZ6LNLTS5La/](ipfs://QmagCqi7cKazCA4x7qyhwMvzpJPGHACvZGNZZ6LNLTS5La/) -### 5.37.1 (2024-07-02) +## 5.38.0 (2024-07-03) + + +### Features + +* **web:** 4212 fix wrap in multichain world (#8454) 9a3b933 +* **web:** add datadog logs/errors alongside sentry (#8682) a9fb607 +* **web:** add l2 nfts feature flag (#9042) b6b378a +* **web:** buy flow code cleanup / refactor (#8889) c1d14b7 +* **web:** buy flow errors states (#9206) 191b9a2 +* **web:** buy flow, connecting screen (#9173) 0dc94a7 +* **web:** complete in provider tab modal state (#9402) c276b4a +* **web:** Disable Clicking on L2 NFTs in MiniPort (#9324) 1083d81 +* **web:** fetch FOR quotes and show provider modal (#9030) 5c9bcee +* **web:** Have tooltip follow cursor (#9328) 68603c1 +* **web:** import Zora nfts into miniport and improve usNftBalance (#9043) f7f4539 +* **web:** kill useAutoRouterSupported hook (#9078) 74c0bcf +* **web:** nav bar polishes (#9163) 299fa69 +* **web:** styled app qr code (#9514) 215ac3f +* **web:** token selection for buy flow (#8869) 24ad20d +* **web:** use quick portfolio balances query (#9285) e2f0d72 ### Bug Fixes -* **web:** avalanche isToken check hotfix (#9790) a60c130 +* **web:** avalanche istoken check (staging) (#9789) 53d34cd +* **web:** check common bases in MiniPortfolio getCurrency function (#9632) f8be510 +* **web:** disable scroll when mobile drawer is open (#9335) c9ed125 +* **web:** fix broken link, translation, and importing v2 positions (#9435) 7c6c355 +* **web:** fix chain selector tests (#9376) 16fc2d9 +* **web:** fix multichain state when connected to unsupported chain (#9057) 7d9206e +* **web:** fix token safety popup on prefilled currencies (#9487) 8fb464c +* **web:** group hero title for better translations (#8729) 8962aa5 +* **web:** legacy nav should not transition out of view on scroll (#9542) 78313d5 +* **web:** prevent uniwallet displaying beneath wallet connect modal (#9798) 78eb31b +* **web:** skip token lookup if search query is not address (#9539) 23eec13 +* **web:** swap context chainId (multichain) (#9143) db3f7ff +* **web:** switch chain before confirm on limit orders (#8498) 30698e2 +* **web:** token URL switch on TDP should use correct chain (#8574) a62f72b +* **web:** use chainId from currency in useCurrencyBalance (#9139) 3da2eb9 +* **web:** use correct chainId in transaction adder & swap tax hooks (#9354) 953fe51 +* **web:** use rive wasm internally to avoid potential side load attack (#9569) 6d332fb + + +### Continuous Integration + +* **web:** update sitemaps d9e916f diff --git a/VERSION b/VERSION index 78b8ff19170..951e22060f5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -web/5.37.1 \ No newline at end of file +web/5.38.0 \ No newline at end of file diff --git a/apps/mobile/.prettierignore b/apps/mobile/.prettierignore deleted file mode 100644 index 2875313553b..00000000000 --- a/apps/mobile/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -ios -android -.eslintrc.js diff --git a/apps/mobile/README.md b/apps/mobile/README.md index 60640ccc8b5..95e7789aed8 100644 --- a/apps/mobile/README.md +++ b/apps/mobile/README.md @@ -190,6 +190,15 @@ Resolve this issue by navigating to the `ios/` directory and running `pod update - `Build target hermes-engine: Command PhaseScriptExecution failed with a nonzero exit code` Node isn't being located correctly during the build phase. Run `which node` and copy the resulting path into `.xcode.env.local`. More context [here](https://github.com/facebook/react-native/issues/42221). +- `CocoaPods could not find compatible versions for pod "hermes-engine"` +The following commands can help you fix these types of errors: + +`cd ios && pod install --repo-update` +`cd ios && pod repo update` +`cd ios && pod update hermes-engine --no-repo-update` + +Context: https://uniswapteam.slack.com/archives/C02GYG8TU12/p1692640189802989?thread_ts=1692635970.952869&cid=C02GYG8TU12 + ### Common fixes If something isn’t working the way it should or you’re getting a weird error when trying to run the app, try the following: diff --git a/apps/mobile/__mocks__/@shopify/react-native-skia.ts b/apps/mobile/__mocks__/@shopify/react-native-skia.ts index 1a3bab1e49d..766d3d19967 100644 --- a/apps/mobile/__mocks__/@shopify/react-native-skia.ts +++ b/apps/mobile/__mocks__/@shopify/react-native-skia.ts @@ -3,10 +3,7 @@ import { View, ViewProps } from 'react-native' // Source: https://github.com/Shopify/react-native-skia/issues/548#issuecomment-1157609472 -const PlainView = ({ - children, - ...props -}: PropsWithChildren): React.CElement => { +const PlainView = ({ children, ...props }: PropsWithChildren): React.CElement => { return React.createElement(View, props, children) } const noop = (): null => null diff --git a/apps/mobile/__mocks__/react-native-context-menu-view.ts b/apps/mobile/__mocks__/react-native-context-menu-view.ts index e1054143fb3..7987c3a4c3f 100644 --- a/apps/mobile/__mocks__/react-native-context-menu-view.ts +++ b/apps/mobile/__mocks__/react-native-context-menu-view.ts @@ -1,10 +1,7 @@ import React, { PropsWithChildren } from 'react' import { View, ViewProps } from 'react-native' -const PlainView = ({ - children, - ...props -}: PropsWithChildren): React.CElement => { +const PlainView = ({ children, ...props }: PropsWithChildren): React.CElement => { return React.createElement(View, props, children) } diff --git a/apps/mobile/__mocks__/react-native-fast-image.ts b/apps/mobile/__mocks__/react-native-fast-image.ts index 92f6067322c..d89ee717f21 100644 --- a/apps/mobile/__mocks__/react-native-fast-image.ts +++ b/apps/mobile/__mocks__/react-native-fast-image.ts @@ -1,10 +1,7 @@ import React, { PropsWithChildren } from 'react' import { Image, ImageProps } from 'react-native' -const PlainImage = ({ - children, - ...props -}: PropsWithChildren): React.CElement => { +const PlainImage = ({ children, ...props }: PropsWithChildren): React.CElement => { return React.createElement(Image, props, children) } diff --git a/apps/mobile/android/app/build.gradle b/apps/mobile/android/app/build.gradle index 92dbe1b82dd..f162a677cea 100644 --- a/apps/mobile/android/app/build.gradle +++ b/apps/mobile/android/app/build.gradle @@ -131,17 +131,17 @@ android { dev { isDefault(true) applicationIdSuffix ".dev" - versionName "1.30" + versionName "1.31" dimension "variant" } beta { applicationIdSuffix ".beta" - versionName "1.30" + versionName "1.31" dimension "variant" } prod { dimension "variant" - versionName "1.30" + versionName "1.31" } } diff --git a/apps/mobile/e2e/utils/fixtures.ts b/apps/mobile/e2e/utils/fixtures.ts index 1f8f2a49eac..c13cc6161ff 100644 --- a/apps/mobile/e2e/utils/fixtures.ts +++ b/apps/mobile/e2e/utils/fixtures.ts @@ -1,7 +1,6 @@ export const TestWallet = { name: 'Wallet 1', - recoveryPhrase: - 'oak reduce strong borrow control funny library disagree radio clarify degree pistol', + recoveryPhrase: 'oak reduce strong borrow control funny library disagree radio clarify degree pistol', } export const TestWatchedWallet = { diff --git a/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj b/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj index 78d57aa8749..6aea6512149 100644 --- a/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj +++ b/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj @@ -192,6 +192,9 @@ 9FEC9B8B2A858CF1003CD019 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC9B8A2A858CF1003CD019 /* AppDelegate.m */; }; A32F9FBD272343C9002CFCDB /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A32F9FBC272343C8002CFCDB /* GoogleService-Info.plist */; }; A3F0A5B1272B1DFA00895B25 /* KeychainSwiftDistrib.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3F0A5B0272B1DFA00895B25 /* KeychainSwiftDistrib.swift */; }; + A70E4DD42C25DA0A002D6D86 /* NetworkFee.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70E4DD32C25DA0A002D6D86 /* NetworkFee.graphql.swift */; }; + A70E4DD72C260416002D6D86 /* SwapOrderType.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70E4DD52C260416002D6D86 /* SwapOrderType.graphql.swift */; }; + A70E4DD82C260416002D6D86 /* SwapOrderStatus.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A70E4DD62C260416002D6D86 /* SwapOrderStatus.graphql.swift */; }; A7B8EFCB2BF68F0D00CA4A1C /* FeeData.graphql.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7B8EFCA2BF68F0D00CA4A1C /* FeeData.graphql.swift */; }; AC0EE0982BD826E700BCCF07 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = AC0EE0972BD826E700BCCF07 /* PrivacyInfo.xcprivacy */; }; AEE498F72A85AD86000DDF8E /* Basel-Book.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AEE498F52A85AD86000DDF8E /* Basel-Book.ttf */; }; @@ -506,6 +509,9 @@ 9FEC9B8A2A858CF1003CD019 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Uniswap/AppDelegate.m; sourceTree = ""; }; A32F9FBC272343C8002CFCDB /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; A3F0A5B0272B1DFA00895B25 /* KeychainSwiftDistrib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainSwiftDistrib.swift; sourceTree = ""; }; + A70E4DD32C25DA0A002D6D86 /* NetworkFee.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NetworkFee.graphql.swift; path = MobileSchema/Schema/Objects/NetworkFee.graphql.swift; sourceTree = ""; }; + A70E4DD52C260416002D6D86 /* SwapOrderType.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwapOrderType.graphql.swift; path = MobileSchema/Schema/Enums/SwapOrderType.graphql.swift; sourceTree = ""; }; + A70E4DD62C260416002D6D86 /* SwapOrderStatus.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwapOrderStatus.graphql.swift; path = MobileSchema/Schema/Enums/SwapOrderStatus.graphql.swift; sourceTree = ""; }; A7B8EFCA2BF68F0D00CA4A1C /* FeeData.graphql.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FeeData.graphql.swift; path = MobileSchema/Schema/Objects/FeeData.graphql.swift; sourceTree = ""; }; A7C9F415D0E128A43003E071 /* Pods-Uniswap.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap.debug.xcconfig"; path = "Target Support Files/Pods-Uniswap/Pods-Uniswap.debug.xcconfig"; sourceTree = ""; }; AC0EE0972BD826E700BCCF07 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Uniswap/PrivacyInfo.xcprivacy; sourceTree = ""; }; @@ -655,6 +661,9 @@ 072E23872A44D5BD006AD6C9 /* WidgetsCore */ = { isa = PBXGroup; children = ( + A70E4DD32C25DA0A002D6D86 /* NetworkFee.graphql.swift */, + A70E4DD62C260416002D6D86 /* SwapOrderStatus.graphql.swift */, + A70E4DD52C260416002D6D86 /* SwapOrderType.graphql.swift */, A7B8EFCA2BF68F0D00CA4A1C /* FeeData.graphql.swift */, 07F0C28E2A5F3E2E00D5353E /* Env.swift */, 073C67F42A5C8FBE00F6DAD8 /* MobileSchema */, @@ -1910,10 +1919,12 @@ 9F29D4ED2B47126D004D003A /* NftBalanceAssetInput.graphql.swift in Sources */, 074322102A83E3CA00F8518D /* NftAssetTrait.graphql.swift in Sources */, 074322382A83E3CA00F8518D /* NftActivityFilterInput.graphql.swift in Sources */, + A70E4DD82C260416002D6D86 /* SwapOrderStatus.graphql.swift in Sources */, 0743220D2A83E3CA00F8518D /* Image.graphql.swift in Sources */, 07F5CF752A7020FD00C648A5 /* Format.swift in Sources */, 0783F7B42A619E7C009ED617 /* UIComponents.swift in Sources */, 0743220B2A83E3CA00F8518D /* NftMarketplace.graphql.swift in Sources */, + A70E4DD72C260416002D6D86 /* SwapOrderType.graphql.swift in Sources */, 0DE251472C13B69D005F47F9 /* OnRampTransfer.graphql.swift in Sources */, 5EFB78362B1E585000E77EAC /* ConvertQuery.graphql.swift in Sources */, 074321F62A83E3CA00F8518D /* SearchTokensQuery.graphql.swift in Sources */, @@ -1947,6 +1958,7 @@ 0743221C2A83E3CA00F8518D /* TokenBalance.graphql.swift in Sources */, 9F00A43A2B33894C0088A0D0 /* ApplicationContract.graphql.swift in Sources */, 074321FD2A83E3CA00F8518D /* TransactionHistoryUpdaterQuery.graphql.swift in Sources */, + A70E4DD42C25DA0A002D6D86 /* NetworkFee.graphql.swift in Sources */, 07F136402A575EC00067004F /* DataQueries.swift in Sources */, 074322112A83E3CA00F8518D /* NftBalanceConnection.graphql.swift in Sources */, 0743221A2A83E3CA00F8518D /* TokenMarket.graphql.swift in Sources */, @@ -2522,7 +2534,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -2568,7 +2580,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets; @@ -2614,7 +2626,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.widgets; @@ -2660,7 +2672,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets; @@ -2702,7 +2714,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -2745,7 +2757,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension; @@ -2788,7 +2800,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.WidgetIntentExtension; @@ -2831,7 +2843,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension; @@ -2867,7 +2879,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -2905,7 +2917,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -3091,7 +3103,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -3135,7 +3147,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension; @@ -3239,7 +3251,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -3310,7 +3322,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension; @@ -3414,7 +3426,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -3485,7 +3497,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.30; + MARKETING_VERSION = 1.31; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.OneSignalNotificationServiceExtension; diff --git a/apps/mobile/jest-setup.js b/apps/mobile/jest-setup.js index b41fb54b1cf..084c8648abc 100644 --- a/apps/mobile/jest-setup.js +++ b/apps/mobile/jest-setup.js @@ -3,6 +3,7 @@ import 'core-js' // necessary so setImmediate works in tests import 'uniswap/src/i18n/i18n' // Uses real translations for tests +import 'utilities/src/logger/mocks' import mockRNCNetInfo from '@react-native-community/netinfo/jest/netinfo-mock.js' import { localizeMock as mockRNLocalize } from 'react-native-localize/mock' @@ -69,10 +70,6 @@ jest.mock('react-native', () => { return RN }) -jest.mock('expo-localization', () => ({ - getLocales: jest.fn(() => [{ languageCode: 'en', countryCode: 'US' }]), -})) - jest.mock('react-native-safe-area-context', () => ({ useSafeAreaInsets: jest.fn().mockImplementation(() => ({})), useSafeAreaFrame: jest.fn().mockImplementation(() => ({})), diff --git a/apps/mobile/package.json b/apps/mobile/package.json index df9c966b12f..4cc4190305a 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -33,7 +33,7 @@ "link:assets": "react-native-asset", "graphql:generate:swift": "cd ios && ./Pods/Apollo/apollo-ios-cli generate", "hardhat": "hardhat node", - "check:circular": "../../scripts/check-circular-imports.sh ./src/app/App.tsx 7", + "check:circular": "../../scripts/check-circular-imports.sh ./src/app/App.tsx 6", "ios": "yarn ios:prebuild && SKIP_BUNDLING=1 react-native run-ios", "ios:prebuild": "yarn graphql:generate:swift && cd ios/WidgetsCore/MobileSchema && rm -rf !(README.md) && cd ../../.. && yarn graphql:generate:swift && yarn env:local:copy:swift", "ios:smol": "SKIP_BUNDLING=1 react-native run-ios --simulator=\"iPhone SE (3rd generation)\"", @@ -41,6 +41,7 @@ "ios:beta": "react-native run-ios --configuration Beta", "ios:bundle": "react-native bundle --entry-file='index.js' --bundle-output='./ios/main.jsbundle' --dev=false --platform='ios' --assets-dest='./ios'", "ios:release": "react-native run-ios --configuration Release", + "format": "../../scripts/prettier.sh", "lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings=0", "lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx --fix", "start": "NODE_ENV=development react-native start", diff --git a/apps/mobile/src/app/App.tsx b/apps/mobile/src/app/App.tsx index 63b6d89efba..8e1520e9ac3 100644 --- a/apps/mobile/src/app/App.tsx +++ b/apps/mobile/src/app/App.tsx @@ -28,7 +28,6 @@ import { BiometricContextProvider } from 'src/features/biometrics/context' import { NotificationToastWrapper } from 'src/features/notifications/NotificationToastWrapper' import { initOneSignal } from 'src/features/notifications/Onesignal' import { shouldLogScreen } from 'src/features/telemetry/directLogScreens' -import { selectAllowAnalytics } from 'src/features/telemetry/selectors' import { selectCustomEndpoint } from 'src/features/tweaks/selectors' import { processWidgetEvents, @@ -37,11 +36,7 @@ import { setI18NUserDefaults, } from 'src/features/widgets/widgets' import { useAppStateTrigger } from 'src/utils/useAppStateTrigger' -import { - getSentryEnvironment, - getSentryTracesSamplingRate, - getStatsigEnvironmentTier, -} from 'src/utils/version' +import { getSentryEnvironment, getSentryTracesSamplingRate, getStatsigEnvironmentTier } from 'src/utils/version' import { flexStyles, useIsDarkMode } from 'ui/src' import { config } from 'uniswap/src/config' import { uniswapUrls } from 'uniswap/src/constants/urls' @@ -49,24 +44,20 @@ import { DUMMY_STATSIG_SDK_KEY, StatsigCustomAppValue } from 'uniswap/src/featur import { Experiments } from 'uniswap/src/features/gating/experiments' import { WALLET_FEATURE_FLAG_NAMES } from 'uniswap/src/features/gating/flags' import { loadStatsigOverrides } from 'uniswap/src/features/gating/overrides/customPersistedOverrides' -import { - Statsig, - StatsigOptions, - StatsigProvider, - StatsigUser, -} from 'uniswap/src/features/gating/sdk/statsig' +import { Statsig, StatsigOptions, StatsigProvider, StatsigUser } from 'uniswap/src/features/gating/sdk/statsig' import Trace from 'uniswap/src/features/telemetry/Trace' import { MobileEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { UnitagUpdaterContextProvider } from 'uniswap/src/features/unitags/context' import i18n from 'uniswap/src/i18n/i18n' import { CurrencyId } from 'uniswap/src/types/currency' -import { isDetoxBuild } from 'utilities/src/environment' +import { isDetoxBuild } from 'utilities/src/environment/constants' import { registerConsoleOverrides } from 'utilities/src/logger/console' import { logger } from 'utilities/src/logger/logger' import { useAsyncData } from 'utilities/src/react/hooks' import { AnalyticsNavigationContextProvider } from 'utilities/src/telemetry/trace/AnalyticsNavigationContext' import { ErrorBoundary } from 'wallet/src/components/ErrorBoundary/ErrorBoundary' +import { selectAllowAnalytics } from 'wallet/src/features/telemetry/selectors' // eslint-disable-next-line no-restricted-imports import { usePersistedApolloClient } from 'wallet/src/data/apollo/usePersistedApolloClient' import { initFirebaseAppCheck } from 'wallet/src/features/appCheck' @@ -173,7 +164,8 @@ function App(): JSX.Element | null { + useIsPartOfNavigationTree={useIsPartOfNavigationTree} + > @@ -195,7 +187,7 @@ function SentryTags({ children }: PropsWithChildren): JSX.Element { for (const experiment of Object.values(Experiments)) { Sentry.setTag( `experiment.${experiment}`, - Statsig.getExperimentWithExposureLoggingDisabled(experiment).getGroupName() + Statsig.getExperimentWithExposureLoggingDisabled(experiment).getGroupName(), ) } }, []) @@ -237,7 +229,8 @@ function AppOuter(): JSX.Element | null { { routingInstrumentation.registerNavigationContainer(navigationRef) - }}> + }} + > @@ -276,11 +269,7 @@ function AppInner(): JSX.Element { if (typeof res === 'string' && res === 'Success') { logger.debug('AppsFlyer', 'status', 'stopped') } else { - logger.warn( - 'AppsFlyer', - 'stop', - `Got an error when trying to stop the AppsFlyer SDK: ${res}` - ) + logger.warn('AppsFlyer', 'stop', `Got an error when trying to stop the AppsFlyer SDK: ${res}`) } }) } @@ -301,11 +290,7 @@ function AppInner(): JSX.Element { <> - + ) } diff --git a/apps/mobile/src/app/MobileWalletNavigationProvider.tsx b/apps/mobile/src/app/MobileWalletNavigationProvider.tsx index 1a19a459b35..f39d6ceb534 100644 --- a/apps/mobile/src/app/MobileWalletNavigationProvider.tsx +++ b/apps/mobile/src/app/MobileWalletNavigationProvider.tsx @@ -14,6 +14,7 @@ import { ShareableEntity } from 'uniswap/src/types/sharing' import { logger } from 'utilities/src/logger/logger' import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants' import { + NavigateToNftCollectionArgs, NavigateToNftItemArgs, NavigateToSendFlowArgs, NavigateToSwapFlowArgs, @@ -32,6 +33,7 @@ export function MobileWalletNavigationProvider({ children }: PropsWithChildren): const navigateToAccountActivityList = useNavigateToHomepageTab(HomeScreenTabIndex.Activity) const navigateToAccountTokenList = useNavigateToHomepageTab(HomeScreenTabIndex.Tokens) const navigateToBuyOrReceiveWithEmptyWallet = useNavigateToBuyOrReceiveWithEmptyWallet() + const navigateToNftCollection = useNavigateToNftCollection() const navigateToNftDetails = useNavigateToNftDetails() const navigateToReceive = useNavigateToReceive() const navigateToSend = useNavigateToSend() @@ -45,11 +47,13 @@ export function MobileWalletNavigationProvider({ children }: PropsWithChildren): navigateToAccountActivityList={navigateToAccountActivityList} navigateToAccountTokenList={navigateToAccountTokenList} navigateToBuyOrReceiveWithEmptyWallet={navigateToBuyOrReceiveWithEmptyWallet} + navigateToNftCollection={navigateToNftCollection} navigateToNftDetails={navigateToNftDetails} navigateToReceive={navigateToReceive} navigateToSend={navigateToSend} navigateToSwapFlow={navigateToSwapFlow} - navigateToTokenDetails={navigateToTokenDetails}> + navigateToTokenDetails={navigateToTokenDetails} + > {children} ) @@ -113,9 +117,7 @@ function useNavigateToReceive(): () => void { const dispatch = useAppDispatch() return useCallback((): void => { - dispatch( - openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }) - ) + dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })) }, [dispatch]) } @@ -127,7 +129,7 @@ function useNavigateToSend(): (args: NavigateToSendFlowArgs) => void { const initialSendState = getNavigateToSendFlowArgsInitialState(args) dispatch(openModal({ name: ModalName.Send, initialState: initialSendState })) }, - [dispatch] + [dispatch], ) } @@ -140,7 +142,7 @@ function useNavigateToSwapFlow(): (args: NavigateToSwapFlowArgs) => void { dispatch(closeModal({ name: ModalName.Swap })) dispatch(openModal({ name: ModalName.Swap, initialState })) }, - [dispatch] + [dispatch], ) } @@ -155,7 +157,7 @@ function useNavigateToTokenDetails(): (currencyId: string) => void { appNavigation.navigate(MobileScreens.TokenDetails, { currencyId }) } }, - [appNavigation] + [appNavigation], ) } @@ -172,7 +174,20 @@ function useNavigateToNftDetails(): (args: NavigateToNftItemArgs) => void { fallbackData, }) }, - [navigation] + [navigation], + ) +} + +function useNavigateToNftCollection(): (args: NavigateToNftCollectionArgs) => void { + const navigation = useAppStackNavigation() + + return useCallback( + ({ collectionAddress }: NavigateToNftCollectionArgs): void => { + navigation.navigate(MobileScreens.NFTCollection, { + collectionAddress, + }) + }, + [navigation], ) } @@ -195,7 +210,7 @@ function useNavigateToBuyOrReceiveWithEmptyWallet(): () => void { openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr, - }) + }), ) } }, [dispatch, forAggregatorEnabled, moonpayFiatOnRampEligible]) diff --git a/apps/mobile/src/app/hooks.ts b/apps/mobile/src/app/hooks.ts index 87f3bcc5dbc..050c29e024b 100644 --- a/apps/mobile/src/app/hooks.ts +++ b/apps/mobile/src/app/hooks.ts @@ -46,9 +46,7 @@ export function useShouldShowNativeKeyboard(): { const isLayoutPending = containerHeight === undefined || decimalPadY === undefined // If decimal pad renders below the input panel, we need to show the native keyboard - const showNativeKeyboard = isLayoutPending - ? false - : containerHeight + MIN_INPUT_DECIMAL_PAD_GAP > decimalPadY + const showNativeKeyboard = isLayoutPending ? false : containerHeight + MIN_INPUT_DECIMAL_PAD_GAP > decimalPadY return { onInputPanelLayout, @@ -56,8 +54,7 @@ export function useShouldShowNativeKeyboard(): { isLayoutPending, showNativeKeyboard, // can be used to imitate flexGrow=1 for the input panel - maxContentHeight: - isLayoutPending || showNativeKeyboard ? undefined : decimalPadY - MIN_INPUT_DECIMAL_PAD_GAP, + maxContentHeight: isLayoutPending || showNativeKeyboard ? undefined : decimalPadY - MIN_INPUT_DECIMAL_PAD_GAP, } } @@ -78,7 +75,7 @@ export function useNativeComponentKey(autoUpdate = true): { return } setKey(getNativeComponentKey()) - }, [autoUpdate]) + }, [autoUpdate]), ) const triggerUpdate = useCallback(() => { diff --git a/apps/mobile/src/app/migrations.test.ts b/apps/mobile/src/app/migrations.test.ts index 0bacd1c5dfa..82073bc0c06 100644 --- a/apps/mobile/src/app/migrations.test.ts +++ b/apps/mobile/src/app/migrations.test.ts @@ -76,7 +76,6 @@ import { initialBiometricsSettingsState } from 'src/features/biometrics/slice' import { initialCloudBackupState } from 'src/features/CloudBackup/cloudBackupSlice' import { initialPasswordLockoutState } from 'src/features/CloudBackup/passwordLockoutSlice' import { initialModalsState } from 'src/features/modals/modalSlice' -import { initialTelemetryState } from 'src/features/telemetry/slice' import { initialTweaksState } from 'src/features/tweaks/slice' import { initialWalletConnectState } from 'src/features/walletConnect/walletConnectSlice' import { ModalName } from 'uniswap/src/features/telemetry/constants' @@ -91,6 +90,7 @@ import { initialFiatCurrencyState } from 'wallet/src/features/fiatCurrency/slice import { initialLanguageState } from 'wallet/src/features/language/slice' import { initialNotificationsState } from 'wallet/src/features/notifications/slice' import { initialSearchHistoryState } from 'wallet/src/features/search/searchHistorySlice' +import { initialTelemetryState } from 'wallet/src/features/telemetry/slice' import { initialTokensState } from 'wallet/src/features/tokens/tokensSlice' import { initialTransactionsState } from 'wallet/src/features/transactions/slice' import { TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' diff --git a/apps/mobile/src/app/migrations.ts b/apps/mobile/src/app/migrations.ts index d9a62294bf0..12e3b5dd791 100644 --- a/apps/mobile/src/app/migrations.ts +++ b/apps/mobile/src/app/migrations.ts @@ -4,19 +4,15 @@ /* eslint-disable max-lines */ import dayjs from 'dayjs' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { ModalName } from 'uniswap/src/features/telemetry/constants' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' import { ExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/slice' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' import { initialFiatCurrencyState } from 'wallet/src/features/fiatCurrency/slice' import { initialLanguageState } from 'wallet/src/features/language/slice' import { getNFTAssetKey } from 'wallet/src/features/nfts/utils' import { TransactionStateMap } from 'wallet/src/features/transactions/slice' -import { - ChainIdToTxIdToDetails, - TransactionStatus, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { ChainIdToTxIdToDetails, TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice' import { @@ -273,7 +269,7 @@ export const migrations = { tempState.byChainId[chainId] = chainInfo return tempState }, - { byChainId: {} } + { byChainId: {} }, ) const blockState: any | undefined = newState?.blocks @@ -292,7 +288,7 @@ export const migrations = { tempState.byChainId[chainId] = blockInfo return tempState }, - { byChainId: {} } + { byChainId: {} }, ) const transactionState: TransactionStateMap | undefined = newState?.transactions @@ -303,28 +299,25 @@ export const migrations = { return tempState } - const newAddressTxState = Object.keys(txs).reduce( - (tempAddressState, chainIdString) => { - const chainId = toSupportedChainId(chainIdString) - if (!chainId) { - return tempAddressState - } - - const txInfo = txs[chainId] - if (!txInfo) { - return tempAddressState - } + const newAddressTxState = Object.keys(txs).reduce((tempAddressState, chainIdString) => { + const chainId = toSupportedChainId(chainIdString) + if (!chainId) { + return tempAddressState + } - tempAddressState[chainId] = txInfo + const txInfo = txs[chainId] + if (!txInfo) { return tempAddressState - }, - {} - ) + } + + tempAddressState[chainId] = txInfo + return tempAddressState + }, {}) tempState[address] = newAddressTxState return tempState }, - {} + {}, ) return { @@ -559,8 +552,7 @@ export const migrations = { newTransactionState[address] ??= {} newTransactionState[address][chainId] ??= {} newTransactionState[address][chainId][txId] = - txDetails.typeInfo.type === TransactionType.FiatPurchase && - txDetails.status === TransactionStatus.Failed + txDetails.typeInfo.type === TransactionType.FiatPurchase && txDetails.status === TransactionStatus.Failed ? { ...txDetails, typeInfo: { @@ -615,10 +607,7 @@ export const migrations = { const accountAddresses = Object.keys(state.favorites?.hiddenNfts ?? {}) - type AccountToNftData = Record< - Address, - Record - > + type AccountToNftData = Record> const nftsData: AccountToNftData = {} for (const accountAddress of accountAddresses) { @@ -830,35 +819,34 @@ export const migrations = { 61: function flattenTokenVisibility(state: any) { const newState = { ...state } - type AccountToNftData = Record< - Address, - Record - > + type AccountToNftData = Record> type NFTKeyToVisibility = Record type AccountToTokenVisibility = Record> type CurrencyIdToVisibility = Record const tokenVisibilityByAccount: AccountToTokenVisibility = state.favorites.tokensVisibility - const flattenedTokenVisibility: CurrencyIdToVisibility = Object.values( - tokenVisibilityByAccount - ).reduce((acc, currencyIdToVisibility) => ({ ...acc, ...currencyIdToVisibility }), {}) + const flattenedTokenVisibility: CurrencyIdToVisibility = Object.values(tokenVisibilityByAccount).reduce( + (acc, currencyIdToVisibility) => ({ ...acc, ...currencyIdToVisibility }), + {}, + ) const nftDataByAccount: AccountToNftData = state.favorites.nftsData const flattenedNFTData = Object.values(nftDataByAccount).reduce( (acc, nftIdToVisibility) => ({ ...acc, ...nftIdToVisibility }), - {} + {}, ) - const flattenedTransformedNFTData: NFTKeyToVisibility = Object.keys( - flattenedNFTData - ).reduce((acc, nftKey) => { - const { isHidden, isSpamIgnored } = flattenedNFTData[nftKey] ?? {} - return { - ...acc, - [nftKey]: { isVisible: isHidden === false || isSpamIgnored === true }, - } - }, {}) + const flattenedTransformedNFTData: NFTKeyToVisibility = Object.keys(flattenedNFTData).reduce( + (acc, nftKey) => { + const { isHidden, isSpamIgnored } = flattenedNFTData[nftKey] ?? {} + return { + ...acc, + [nftKey]: { isVisible: isHidden === false || isSpamIgnored === true }, + } + }, + {}, + ) newState.favorites = { ...state.favorites, diff --git a/apps/mobile/src/app/modals/AccountSwitcherModal.tsx b/apps/mobile/src/app/modals/AccountSwitcherModal.tsx index bc4f405bae5..8ff34f68d80 100644 --- a/apps/mobile/src/app/modals/AccountSwitcherModal.tsx +++ b/apps/mobile/src/app/modals/AccountSwitcherModal.tsx @@ -24,10 +24,7 @@ import { createOnboardingAccount } from 'wallet/src/features/onboarding/createOn import { AccountType, BackupType } from 'wallet/src/features/wallet/accounts/types' import { createAccountsActions } from 'wallet/src/features/wallet/create/createAccountsSaga' import { useActiveAccountAddress, useNativeAccountExists } from 'wallet/src/features/wallet/hooks' -import { - selectAllAccountsSorted, - selectSortedSignerMnemonicAccounts, -} from 'wallet/src/features/wallet/selectors' +import { selectAllAccountsSorted, selectSortedSignerMnemonicAccounts } from 'wallet/src/features/wallet/selectors' import { setAccountAsActive } from 'wallet/src/features/wallet/slice' import { openSettings } from 'wallet/src/utils/linking' @@ -39,7 +36,8 @@ export function AccountSwitcherModal(): JSX.Element { dispatch(closeModal({ name: ModalName.AccountSwitcher }))}> + onClose={(): Action => dispatch(closeModal({ name: ModalName.AccountSwitcher }))} + > { @@ -77,7 +75,7 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme dispatch(setAccountAsActive(address)) }) }, - [dispatch, onClose] + [dispatch, onClose], ) const onPressAddWallet = (): void => { @@ -109,7 +107,7 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme dispatch( createAccountsActions.trigger({ accounts: [newAccount], - }) + }), ) // Log analytics event @@ -182,7 +180,7 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme style: 'default', }, { text: t('account.cloud.error.unavailable.button.cancel'), style: 'cancel' }, - ] + ], ) return } @@ -200,11 +198,7 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme key: ElementName.CreateAccount, onPress: onPressCreateNewWallet, render: () => ( - + {t('account.wallet.button.create')} ), @@ -236,9 +230,7 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme render: () => ( - {isAndroid - ? t('account.cloud.button.restore.android') - : t('account.cloud.button.restore.ios')} + {isAndroid ? t('account.cloud.button.restore.android') : t('account.cloud.button.restore.ios')} ), @@ -250,15 +242,13 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme const accountsWithoutActive = accounts.filter((a) => a.address !== activeAccountAddress) - const isViewOnly = - accounts.find((a) => a.address === activeAccountAddress)?.type === AccountType.Readonly + const isViewOnly = accounts.find((a) => a.address === activeAccountAddress)?.type === AccountType.Readonly if (!activeAccountAddress) { return null } - const fullScreenContentHeight = - dimensions.fullHeight - insets.top - insets.bottom - spacing.spacing36 // approximate bottom sheet handle height + padding bottom + const fullScreenContentHeight = dimensions.fullHeight - insets.top - insets.bottom - spacing.spacing36 // approximate bottom sheet handle height + padding bottom return ( @@ -273,20 +263,12 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme variant="subheading1" /> - - + diff --git a/apps/mobile/src/app/modals/AppModals.tsx b/apps/mobile/src/app/modals/AppModals.tsx index 9894b83a816..ef5c37a6de0 100644 --- a/apps/mobile/src/app/modals/AppModals.tsx +++ b/apps/mobile/src/app/modals/AppModals.tsx @@ -16,7 +16,6 @@ import { ExchangeTransferModal } from 'src/features/fiatOnRamp/ExchangeTransferM import { FiatOnRampAggregatorModal } from 'src/features/fiatOnRamp/FiatOnRampAggregatorModal' import { FiatOnRampModal } from 'src/features/fiatOnRamp/FiatOnRampModal' import { closeModal } from 'src/features/modals/modalSlice' -import { ExtensionWaitlistModal } from 'src/features/scantastic/ExtensionWaitlistModal' import { ScantasticModal } from 'src/features/scantastic/ScantasticModal' import { ReceiveCryptoModal } from 'src/screens/ReceiveCryptoModal' import { SettingsFiatCurrencyModal } from 'src/screens/SettingsFiatCurrencyModal' @@ -57,10 +56,6 @@ export function AppModals(): JSX.Element { - - - - diff --git a/apps/mobile/src/app/modals/ExperimentsModal.tsx b/apps/mobile/src/app/modals/ExperimentsModal.tsx index aef0e5674aa..61107c204a3 100644 --- a/apps/mobile/src/app/modals/ExperimentsModal.tsx +++ b/apps/mobile/src/app/modals/ExperimentsModal.tsx @@ -34,7 +34,7 @@ export function ExperimentsModal(): JSX.Element { dispatch( setCustomEndpoint({ customEndpoint: { url, key }, - }) + }), ) } else { clearEndpoint() @@ -46,13 +46,15 @@ export function ExperimentsModal(): JSX.Element { fullScreen renderBehindBottomInset name={ModalName.Experiments} - onClose={(): Action => dispatch(closeModal({ name: ModalName.Experiments }))}> + onClose={(): Action => dispatch(closeModal({ name: ModalName.Experiments }))} + > + }} + > Server @@ -60,8 +62,8 @@ export function ExperimentsModal(): JSX.Element { - You will need to restart the application to pick up any changes in this section. - Beware of client side caching! + You will need to restart the application to pick up any changes in this section. Beware of client side + caching! @@ -90,10 +92,7 @@ export function ExperimentsModal(): JSX.Element { - diff --git a/apps/mobile/src/app/modals/ExploreModal.tsx b/apps/mobile/src/app/modals/ExploreModal.tsx index 7c89bb7068d..8b5229d0903 100644 --- a/apps/mobile/src/app/modals/ExploreModal.tsx +++ b/apps/mobile/src/app/modals/ExploreModal.tsx @@ -22,12 +22,10 @@ export function ExploreModal(): JSX.Element { renderBehindBottomInset renderBehindTopInset backgroundColor={colors.transparent.val} - // Don't dismiss on back press, as this modal is used for the ExploreStack navigation. - // (the modal should be dismissed only when the user navigates to the initial Explore screen) - dismissOnBackPress={false} hideHandlebar={true} name={ModalName.Explore} - onClose={onClose}> + onClose={onClose} + > ) diff --git a/apps/mobile/src/app/modals/ExtensionPromoModal.tsx b/apps/mobile/src/app/modals/ExtensionPromoModal.tsx index 6031fe09ff9..90892bb3e17 100644 --- a/apps/mobile/src/app/modals/ExtensionPromoModal.tsx +++ b/apps/mobile/src/app/modals/ExtensionPromoModal.tsx @@ -3,13 +3,27 @@ import { Trans, useTranslation } from 'react-i18next' import { StyleSheet } from 'react-native' import 'react-native-reanimated' import { Button, Flex, Image, Text, useIsDarkMode } from 'ui/src' -import { EXTENSION_PROMO_MODAL_DARK, EXTENSION_PROMO_MODAL_LIGHT } from 'ui/src/assets' +import { + EXTENSION_PROMO_BANNER_DARK, + EXTENSION_PROMO_BANNER_DARK_GA, + EXTENSION_PROMO_BANNER_LIGHT, + EXTENSION_PROMO_BANNER_LIGHT_GA, +} from 'ui/src/assets' import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal' +import { FeatureFlags } from 'uniswap/src/features/gating/flags' +import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { ModalName } from 'uniswap/src/features/telemetry/constants' export function ExtensionPromoModal({ onClose }: { onClose: () => void }): JSX.Element { const { t } = useTranslation() const isDarkMode = useIsDarkMode() + const isExtensionGAPromotionEnabled = useFeatureFlag(FeatureFlags.ExtensionPromotionGA) + + const bannerImageGA = isDarkMode ? EXTENSION_PROMO_BANNER_DARK_GA : EXTENSION_PROMO_BANNER_LIGHT_GA + + const bannerImageBeta = isDarkMode ? EXTENSION_PROMO_BANNER_DARK : EXTENSION_PROMO_BANNER_LIGHT + + const imageUri = isExtensionGAPromotionEnabled ? bannerImageGA : bannerImageBeta return ( @@ -18,26 +32,32 @@ export function ExtensionPromoModal({ onClose }: { onClose: () => void }): JSX.E position="absolute" resizeMode="contain" source={{ - uri: isDarkMode ? EXTENSION_PROMO_MODAL_DARK : EXTENSION_PROMO_MODAL_LIGHT, + uri: imageUri, }} style={ImageStyles.responsiveImage} /> - {t('home.modal.getExtension.title')} + + {isExtensionGAPromotionEnabled + ? t('home.modal.getExtension.ga.title') + : t('home.modal.getExtension.beta.title')} + , }} - i18nKey="home.modal.getExtension.step1" + i18nKey="home.modal.getExtension.ga.step1" /> - {t('home.modal.getExtension.step2')} + {t('home.modal.getExtension.ga.step2')} - {t('home.modal.getExtension.step3')} + {isExtensionGAPromotionEnabled + ? t('home.modal.getExtension.ga.step3') + : t('home.modal.getExtension.beta.step3')} diff --git a/apps/mobile/src/app/modals/SwapModal.tsx b/apps/mobile/src/app/modals/SwapModal.tsx index b2cf30f0470..581bf3fba20 100644 --- a/apps/mobile/src/app/modals/SwapModal.tsx +++ b/apps/mobile/src/app/modals/SwapModal.tsx @@ -1,11 +1,7 @@ import React, { useCallback, useEffect } from 'react' import { useAppDispatch, useAppSelector } from 'src/app/hooks' import { BiometricsIcon } from 'src/components/icons/BiometricsIcon' -import { - useBiometricAppSettings, - useBiometricPrompt, - useOsBiometricAuthEnabled, -} from 'src/features/biometrics/hooks' +import { useBiometricAppSettings, useBiometricPrompt, useOsBiometricAuthEnabled } from 'src/features/biometrics/hooks' import { closeModal } from 'src/features/modals/modalSlice' import { selectModalState } from 'src/features/modals/selectModalState' import { useWalletRestore } from 'src/features/wallet/hooks' diff --git a/apps/mobile/src/app/modals/TransferTokenModal.tsx b/apps/mobile/src/app/modals/TransferTokenModal.tsx index 7f134d51a24..a98009dd521 100644 --- a/apps/mobile/src/app/modals/TransferTokenModal.tsx +++ b/apps/mobile/src/app/modals/TransferTokenModal.tsx @@ -32,7 +32,8 @@ export function TransferTokenModal(): JSX.Element { renderBehindTopInset backgroundColor={colors.surface1.get()} name={ModalName.Send} - onClose={onClose}> + onClose={onClose} + > ) diff --git a/apps/mobile/src/app/modals/ViewOnlyExplainerModal.tsx b/apps/mobile/src/app/modals/ViewOnlyExplainerModal.tsx index bb20e62a357..0acabb272ce 100644 --- a/apps/mobile/src/app/modals/ViewOnlyExplainerModal.tsx +++ b/apps/mobile/src/app/modals/ViewOnlyExplainerModal.tsx @@ -53,12 +53,7 @@ export function ViewOnlyExplainerModal(): JSX.Element { - diff --git a/apps/mobile/src/app/modals/__snapshots__/AccountSwitcherModal.test.tsx.snap b/apps/mobile/src/app/modals/__snapshots__/AccountSwitcherModal.test.tsx.snap index 7d57c058fa5..7e6ba743ba0 100644 --- a/apps/mobile/src/app/modals/__snapshots__/AccountSwitcherModal.test.tsx.snap +++ b/apps/mobile/src/app/modals/__snapshots__/AccountSwitcherModal.test.tsx.snap @@ -258,8 +258,8 @@ exports[`AccountSwitcher renders correctly 1`] = ` ] } tintColor="#7D7D7D" - vbHeight={16} - vbWidth={15} + vbHeight={24} + vbWidth={24} > { Rendered , - { preloadedState: preloadedMobileState() } + { preloadedState: preloadedMobileState() }, ) expect(tree.toJSON()).toBeNull() @@ -28,7 +28,7 @@ describe(LazyModalRenderer, () => { [ModalName.Experiments]: { isOpen: true }, }), }), - } + }, ) expect(tree.toJSON()).toMatchInlineSnapshot(` diff --git a/apps/mobile/src/app/navigation/NavBar.tsx b/apps/mobile/src/app/navigation/NavBar.tsx index d24fe34dfb2..2a8183e0b6b 100644 --- a/apps/mobile/src/app/navigation/NavBar.tsx +++ b/apps/mobile/src/app/navigation/NavBar.tsx @@ -57,11 +57,7 @@ export function NavBar(): JSX.Element { return ( <> - + + style={{ paddingBottom: insets.bottom }} + > + pointerEvents="auto" + > @@ -119,7 +117,7 @@ const SwapFAB = memo(function _SwapFAB({ activeScale = 0.96 }: SwapTabBarButtonP openModal({ name: ModalName.Swap, initialState: prepareSwapFormState({ inputCurrencyId }), - }) + }), ) await HapticFeedback.impact() @@ -151,7 +149,8 @@ const SwapFAB = memo(function _SwapFAB({ activeScale = 0.96 }: SwapTabBarButtonP shadowOffset={SWAP_BUTTON_SHADOW_OFFSET} shadowOpacity={isDarkMode ? 0.6 : 0.4} shadowRadius={borderRadii.rounded20} - style={[animatedStyle]}> + style={[animatedStyle]} + > - + top={0} + > + - + {t('common.button.swap')} @@ -230,10 +220,9 @@ function ExploreTabBarButton({ activeScale = 0.98 }: ExploreTabBarButtonProps): hapticFeedback activeOpacity={1} style={[styles.searchBar, { borderRadius: borderRadii.roundedFull }]} - onPress={onPress}> - + onPress={onPress} + > + + shadowRadius={borderRadii.rounded20} + > + variant="body1" + > {t('common.input.search')} diff --git a/apps/mobile/src/app/navigation/NavigationContainer.tsx b/apps/mobile/src/app/navigation/NavigationContainer.tsx index 64d512834dc..1d46a5feb89 100644 --- a/apps/mobile/src/app/navigation/NavigationContainer.tsx +++ b/apps/mobile/src/app/navigation/NavigationContainer.tsx @@ -27,10 +27,7 @@ interface Props { export const navigationRef = createNavigationContainerRef() /** Wrapped `NavigationContainer` with telemetry tracing. */ -export const NavigationContainer: FC> = ({ - children, - onReady, -}: PropsWithChildren) => { +export const NavigationContainer: FC> = ({ children, onReady }: PropsWithChildren) => { const colors = useSporeColors() const [routeName, setRouteName] = useState() const [routeParams, setRouteParams] = useState | undefined>() @@ -58,8 +55,7 @@ export const NavigationContainer: FC> = ({ }} onStateChange={(): void => { const previousRouteName = routeName - const currentRouteName: MobileAppScreen = navigationRef.getCurrentRoute() - ?.name as MobileAppScreen + const currentRouteName: MobileAppScreen = navigationRef.getCurrentRoute()?.name as MobileAppScreen if ( currentRouteName && @@ -68,7 +64,7 @@ export const NavigationContainer: FC> = ({ ) { const currentRouteParams = getEventParams( currentRouteName, - navigationRef.getCurrentRoute()?.params as RootParamList[MobileAppScreen] + navigationRef.getCurrentRoute()?.params as RootParamList[MobileAppScreen], ) setLogImpression(true) setRouteName(currentRouteName) @@ -76,7 +72,8 @@ export const NavigationContainer: FC> = ({ } else { setLogImpression(false) } - }}> + }} + > {children} @@ -95,7 +92,7 @@ export const useManageDeepLinks = (): void => { // as then there is a change we dispatch `openDeepLink` action twice if app was lauched by a deep link await sleep(2000) // 2000 was chosen imperically const urlListener = Linking.addEventListener('url', (event: { url: string }) => - dispatch(openDeepLink({ url: event.url, coldStart: false })) + dispatch(openDeepLink({ url: event.url, coldStart: false })), ) return urlListener.remove diff --git a/apps/mobile/src/app/navigation/hooks.ts b/apps/mobile/src/app/navigation/hooks.ts index e3c850811ca..7a7b425ff18 100644 --- a/apps/mobile/src/app/navigation/hooks.ts +++ b/apps/mobile/src/app/navigation/hooks.ts @@ -25,12 +25,12 @@ export function useEagerActivityNavigation(): { }, }) }, - [load] + [load], ) const navigate = useCallback( () => navigation.navigate(MobileScreens.Home, { tab: HomeScreenTabIndex.Activity }), - [navigation] + [navigation], ) return { preload, navigate } @@ -52,14 +52,14 @@ export function useEagerExternalProfileNavigation(): { async (address: string) => { await load({ variables: { address } }) }, - [load] + [load], ) const navigate = useCallback( (address: string) => { navigation.navigate(MobileScreens.ExternalProfile, { address }) }, - [navigation] + [navigation], ) return { preload, navigate } @@ -79,7 +79,7 @@ export function useEagerExternalProfileRootNavigation(): { }, }) }, - [load] + [load], ) const navigate = useCallback(async (address: string, callback?: () => void) => { diff --git a/apps/mobile/src/app/navigation/navigation.tsx b/apps/mobile/src/app/navigation/navigation.tsx index c4c9b93a1cd..460483d4ff2 100644 --- a/apps/mobile/src/app/navigation/navigation.tsx +++ b/apps/mobile/src/app/navigation/navigation.tsx @@ -70,12 +70,7 @@ import { spacing } from 'ui/src/theme' import { FeatureFlags } from 'uniswap/src/features/gating/flags' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { OnboardingEntryPoint } from 'uniswap/src/types/onboarding' -import { - FiatOnRampScreens, - MobileScreens, - OnboardingScreens, - UnitagScreens, -} from 'uniswap/src/types/screens/mobile' +import { FiatOnRampScreens, MobileScreens, OnboardingScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile' import { OnboardingContextProvider } from 'wallet/src/features/onboarding/OnboardingContext' import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' import { selectFinishedOnboarding } from 'wallet/src/features/wallet/selectors' @@ -94,27 +89,19 @@ function SettingsStackGroup(): JSX.Element { ...navOptions.noHeader, fullScreenGestureEnabled: true, animation: 'slide_from_right', - }}> + }} + > - + - - + + - - - + + + ) } @@ -169,7 +147,8 @@ export function ExploreStackNavigator(): JSX.Element { border: 'transparent', notification: 'transparent', }, - }}> + }} + > + }} + > - + {(props): JSX.Element => } @@ -208,19 +187,14 @@ export function FiatOnRampStackNavigator(): JSX.Element { fullScreenGestureEnabled: true, gestureEnabled: true, animation: 'slide_from_right', - }}> - + }} + > + - + @@ -232,9 +206,7 @@ const renderHeaderBackButton = (): JSX.Element => + }} + > {isOnboardingKeyringEnabled && ( - - - - + + + + - + - + - - - + + + ) } -const renderHeaderBackImage = (): JSX.Element => ( - -) +const renderHeaderBackImage = (): JSX.Element => export function UnitagStackNavigator(): JSX.Element { const colors = useSporeColors() @@ -363,7 +307,8 @@ export function UnitagStackNavigator(): JSX.Element { headerLeftContainerStyle: { paddingLeft: spacing.spacing16 }, headerRightContainerStyle: { paddingRight: spacing.spacing16 }, ...TransitionPresets.SlideFromRightIOS, - }}> + }} + > - {finishedOnboarding && ( - - )} + }} + > + {finishedOnboarding && } = - undefined extends RootParamList[RouteName] - ? [RouteName] | [RouteName, RootParamList[RouteName]] - : [RouteName, RootParamList[RouteName]] +export type RootNavigationArgs = undefined extends RootParamList[RouteName] + ? [RouteName] | [RouteName, RootParamList[RouteName]] + : [RouteName, RootParamList[RouteName]] function isNavigationRefReady(): boolean { if (!navigationRef.isReady()) { @@ -18,9 +17,7 @@ function isNavigationRefReady(): boolean { return true } -export function navigate( - ...args: RootNavigationArgs -): void { +export function navigate(...args: RootNavigationArgs): void { const [routeName, params] = args if (!isNavigationRefReady()) { return @@ -42,7 +39,7 @@ export function goBack(): void { } export function dispatchNavigationAction( - action: NavigationAction | ((state: NavigationState) => NavigationAction) + action: NavigationAction | ((state: NavigationState) => NavigationAction), ): void { if (!isNavigationRefReady()) { return diff --git a/apps/mobile/src/app/navigation/types.ts b/apps/mobile/src/app/navigation/types.ts index 609a5b9a5b0..b56fef04414 100644 --- a/apps/mobile/src/app/navigation/types.ts +++ b/apps/mobile/src/app/navigation/types.ts @@ -8,12 +8,7 @@ import { NativeStackNavigationProp, NativeStackScreenProps } from '@react-naviga import { EducationContentType } from 'src/components/education' import { HomeScreenTabIndex } from 'src/screens/HomeScreenTabIndex' import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding' -import { - FiatOnRampScreens, - MobileScreens, - OnboardingScreens, - UnitagScreens, -} from 'uniswap/src/types/screens/mobile' +import { FiatOnRampScreens, MobileScreens, OnboardingScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile' import { NFTItem } from 'wallet/src/features/nfts/types' type NFTItemScreenParams = { @@ -71,10 +66,7 @@ export type OnboardingStackBaseParams = { entryPoint: OnboardingEntryPoint } -export type UnitagEntryPoint = - | OnboardingScreens.Landing - | MobileScreens.Home - | MobileScreens.Settings +export type UnitagEntryPoint = OnboardingScreens.Landing | MobileScreens.Home | MobileScreens.Settings export type SharedUnitagScreenParams = { [UnitagScreens.ClaimUnitag]: { @@ -168,16 +160,20 @@ export type SettingsStackNavigationProp = CompositeNavigationProp< AppStackNavigationProp > -export type SettingsStackScreenProp = - CompositeScreenProps, AppStackScreenProps> +export type SettingsStackScreenProp = CompositeScreenProps< + NativeStackScreenProps, + AppStackScreenProps +> export type OnboardingStackNavigationProp = CompositeNavigationProp< NativeStackNavigationProp, AppStackNavigationProp > -export type UnitagStackScreenProp = - NativeStackScreenProps +export type UnitagStackScreenProp = NativeStackScreenProps< + UnitagStackParamList, + Screen +> export type RootParamList = AppStackParamList & ExploreStackParamList & @@ -186,10 +182,8 @@ export type RootParamList = AppStackParamList & UnitagStackParamList & FiatOnRampStackParamList -export const useAppStackNavigation = (): AppStackNavigationProp => - useNavigation() -export const useExploreStackNavigation = (): ExploreStackNavigationProp => - useNavigation() +export const useAppStackNavigation = (): AppStackNavigationProp => useNavigation() +export const useExploreStackNavigation = (): ExploreStackNavigationProp => useNavigation() export const useSettingsStackNavigation = (): SettingsStackNavigationProp => useNavigation() export const useOnboardingStackNavigation = (): OnboardingStackNavigationProp => diff --git a/apps/mobile/src/app/reducer.ts b/apps/mobile/src/app/reducer.ts index 0611abd3c32..6dc6062bd28 100644 --- a/apps/mobile/src/app/reducer.ts +++ b/apps/mobile/src/app/reducer.ts @@ -4,7 +4,6 @@ import { cloudBackupReducer } from 'src/features/CloudBackup/cloudBackupSlice' import { passwordLockoutReducer } from 'src/features/CloudBackup/passwordLockoutSlice' import { biometricSettingsReducer } from 'src/features/biometrics/slice' import { modalsReducer } from 'src/features/modals/modalSlice' -import { telemetryReducer } from 'src/features/telemetry/slice' import { tweaksReducer } from 'src/features/tweaks/slice' import { walletConnectReducer } from 'src/features/walletConnect/walletConnectSlice' import { sharedReducers } from 'wallet/src/state/reducer' @@ -16,7 +15,6 @@ const reducers = { modals: modalsReducer, passwordLockout: passwordLockoutReducer, saga: monitoredSagaReducers, - telemetry: telemetryReducer, tweaks: tweaksReducer, walletConnect: walletConnectReducer, } as const diff --git a/apps/mobile/src/app/saga.ts b/apps/mobile/src/app/saga.ts index 0e5f13fa834..4aa307137ce 100644 --- a/apps/mobile/src/app/saga.ts +++ b/apps/mobile/src/app/saga.ts @@ -11,12 +11,7 @@ import { signWcRequestSaga } from 'src/features/walletConnect/signWcRequestSaga' import { call, delay, select, spawn } from 'typed-redux-saga' import { apolloClientRef } from 'wallet/src/data/apollo/usePersistedApolloClient' import { appLanguageWatcherSaga } from 'wallet/src/features/language/saga' -import { - swapActions, - swapReducer, - swapSaga, - swapSagaName, -} from 'wallet/src/features/transactions/swap/swapSaga' +import { swapActions, swapReducer, swapSaga, swapSagaName } from 'wallet/src/features/transactions/swap/swapSaga' import { tokenWrapActions, tokenWrapReducer, @@ -87,11 +82,7 @@ export const monitoredSagaReducers = getMonitoredSagaReducers(monitoredSagas) export function* mobileSaga() { // wait until redux-persist has finished rehydration while (true) { - if ( - yield* select( - (state: { _persist?: PersistState }): boolean | undefined => state._persist?.rehydrated - ) - ) { + if (yield* select((state: { _persist?: PersistState }): boolean | undefined => state._persist?.rehydrated)) { break } yield* delay(REHYDRATION_STATUS_POLLING_INTERVAL) diff --git a/apps/mobile/src/app/store.ts b/apps/mobile/src/app/store.ts index 9077169a0f8..7305e6bef0e 100644 --- a/apps/mobile/src/app/store.ts +++ b/apps/mobile/src/app/store.ts @@ -7,7 +7,7 @@ import { MOBILE_STATE_VERSION, migrations } from 'src/app/migrations' import { MobileState, ReducerNames, mobileReducer } from 'src/app/reducer' import { mobileSaga } from 'src/app/saga' import { fiatOnRampAggregatorApi as sharedFiatOnRampAggregatorApi } from 'uniswap/src/features/fiatOnRamp/api' -import { isNonJestDev } from 'utilities/src/environment' +import { isNonJestDev } from 'utilities/src/environment/constants' import { logger } from 'utilities/src/logger/logger' import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiatOnRamp/api' import { createStore } from 'wallet/src/state' @@ -57,7 +57,6 @@ const whitelist: Array = [ ...sharedPersistedStateWhitelist, 'biometricSettings', 'passwordLockout', - 'telemetry', 'tweaks', 'cloudBackup', ] @@ -94,7 +93,7 @@ if (isNonJestDev) { } export const setupStore = ( - preloadedState?: PreloadedState + preloadedState?: PreloadedState, // eslint-disable-next-line @typescript-eslint/explicit-function-return-type ) => { return createStore({ diff --git a/apps/mobile/src/components/NFT/NftView.tsx b/apps/mobile/src/components/NFT/NftView.tsx index 8cade9cab67..d7996c2a047 100644 --- a/apps/mobile/src/components/NFT/NftView.tsx +++ b/apps/mobile/src/components/NFT/NftView.tsx @@ -3,22 +3,11 @@ import { Flex, ImpactFeedbackStyle, TouchableArea } from 'ui/src' import { borderRadii } from 'ui/src/theme' import noop from 'utilities/src/react/noop' import { NFTViewer } from 'wallet/src/features/images/NFTViewer' -import { - ESTIMATED_NFT_LIST_ITEM_SIZE, - MAX_NFT_IMAGE_SIZE, -} from 'wallet/src/features/nfts/constants' +import { ESTIMATED_NFT_LIST_ITEM_SIZE, MAX_NFT_IMAGE_SIZE } from 'wallet/src/features/nfts/constants' import { NFTItem } from 'wallet/src/features/nfts/types' import { useNFTContextMenu } from 'wallet/src/features/nfts/useNftContextMenu' -export function NftView({ - owner, - item, - onPress, -}: { - owner: Address - item: NFTItem - onPress: () => void -}): JSX.Element { +export function NftView({ owner, item, onPress }: { owner: Address; item: NFTItem; onPress: () => void }): JSX.Element { const { menuActions, onContextMenuPress } = useNFTContextMenu({ contractAddress: item.contractAddress, tokenId: item.tokenId, @@ -32,21 +21,24 @@ export function NftView({ actions={menuActions} disabled={menuActions.length === 0} style={{ borderRadius: borderRadii.rounded16 }} - onPress={onContextMenuPress}> + onPress={onContextMenuPress} + > + onPress={onPress} + > + width="100%" + > number.formatted.value.split(separator)[0] || '', - [number, separator] - ) + const wholePart = useDerivedValue(() => number.formatted.value.split(separator)[0] || '', [number, separator]) const decimalPart = useDerivedValue( () => separator + (number.formatted.value.split(separator)[1] || ''), - [number, separator] + [number, separator], ) const wholeStyle = useMemo(() => { @@ -90,19 +87,9 @@ export const AnimatedDecimalNumber = memo(function AnimatedDecimalNumber( return ( - + {decimalPart.value !== separator && ( - + )} ) diff --git a/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx b/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx index de9443811a2..868c2ee927e 100644 --- a/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx +++ b/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx @@ -9,11 +9,7 @@ import { TimeRangeGroup } from 'src/components/PriceExplorer/TimeRangeGroup' import { CURSOR_INNER_SIZE, CURSOR_SIZE } from 'src/components/PriceExplorer/constants' import { useChartDimensions } from 'src/components/PriceExplorer/useChartDimensions' import { useLineChartPrice } from 'src/components/PriceExplorer/usePrice' -import { - PriceNumberOfDigits, - TokenSpotData, - useTokenPriceHistory, -} from 'src/components/PriceExplorer/usePriceHistory' +import { PriceNumberOfDigits, TokenSpotData, useTokenPriceHistory } from 'src/components/PriceExplorer/usePriceHistory' import { Loader } from 'src/components/loading' import { Flex, HapticFeedback } from 'ui/src' import { spacing } from 'ui/src/theme' @@ -36,11 +32,7 @@ function PriceTextSection({ loading, numberOfDigits, spotPrice }: PriceTextProps return ( - + @@ -68,9 +60,8 @@ export const PriceExplorer = memo(function PriceExplorer({ useTokenPriceHistory(currencyId) const { convertFiatAmount } = useLocalizationContext() - const conversionRate = convertFiatAmount().amount - const shouldShowAnimatedDot = - selectedDuration === HistoryDuration.Day || selectedDuration === HistoryDuration.Hour + const conversionRate = convertFiatAmount(1).amount + const shouldShowAnimatedDot = selectedDuration === HistoryDuration.Day || selectedDuration === HistoryDuration.Hour const additionalPadding = shouldShowAnimatedDot ? 40 : 0 const { lastPricePoint, convertedPriceHistory } = useMemo(() => { @@ -93,10 +84,7 @@ export const PriceExplorer = memo(function PriceExplorer({ ) }, [data, convertedSpotValue]) - if ( - !loading && - (!convertedPriceHistory || (!convertedSpot && selectedDuration === HistoryDuration.Day)) - ) { + if (!loading && (!convertedPriceHistory || (!convertedSpot && selectedDuration === HistoryDuration.Day))) { // Propagate retry up while refetching, if available const refetchAndRetry = (): void => { if (refetch) { @@ -127,9 +115,7 @@ export const PriceExplorer = memo(function PriceExplorer({ } return ( - + { if (index >= commaIndex && commaIndex > DEEMPHASIZED_DECIMALS_THRESHOLD) { return deemphasizedColor @@ -42,17 +42,9 @@ const getEmphasizedNumberColor = ( return emphasizedColor } -const shouldUseSeparator = ( - index: number, - commaIndex: number, - decimalPlaceIndex: number -): boolean => { +const shouldUseSeparator = (index: number, commaIndex: number, decimalPlaceIndex: number): boolean => { 'worklet' - return ( - (index - commaIndex) % 4 === 0 && - index - commaIndex < 0 && - index > commaIndex - decimalPlaceIndex - ) + return (index - commaIndex) % 4 === 0 && index - commaIndex < 0 && index > commaIndex - decimalPlaceIndex } const NumbersMain = ({ @@ -97,7 +89,8 @@ const NumbersMain = ({ }, animatedTextStyle, ]} - onLayout={onLayout}> + onLayout={onLayout} + > {NUMBER_ARRAY} ) @@ -126,12 +119,7 @@ const RollNumber = ({ currency: FiatCurrencyInfo }): JSX.Element => { const colors = useSporeColors() - const numberColor = getEmphasizedNumberColor( - index, - commaIndex, - colors.neutral1.val, - colors.neutral3.val - ) + const numberColor = getEmphasizedNumberColor(index, commaIndex, colors.neutral1.val, colors.neutral3.val) const animatedDigit = useDerivedValue(() => { const char = chars.value[index - (commaIndex - decimalPlace.value)] @@ -161,8 +149,7 @@ const RollNumber = ({ }, [animatedDigit, shouldAnimate]) const animatedWrapperStyle = useAnimatedStyle(() => { - const digitWidth = - animatedDigit.value !== undefined ? NUMBER_WIDTH_ARRAY[animatedDigit.value] ?? 0 : 0 + const digitWidth = animatedDigit.value !== undefined ? NUMBER_WIDTH_ARRAY[animatedDigit.value] ?? 0 : 0 const rowWidth = digitWidth + ADDITIONAL_WIDTH_FOR_ANIMATIONS - 7 return { @@ -184,8 +171,7 @@ const RollNumber = ({ } } - const digitWidth = - chars.value[index - (commaIndex - decimalPlace.value)] === currency.groupingSeparator ? 8 : 0 + const digitWidth = chars.value[index - (commaIndex - decimalPlace.value)] === currency.groupingSeparator ? 8 : 0 const rowWidth = Math.max(digitWidth, 0) @@ -207,7 +193,8 @@ const RollNumber = ({ animatedFontStyle, AnimatedFontStyles.fontStyle, { height: DIGIT_HEIGHT, backgroundColor: colors.surface1.val }, - ]}> + ]} + > {currency.decimalSeparator} ) @@ -222,7 +209,8 @@ const RollNumber = ({ animatedFontStyle, AnimatedFontStyles.fontStyle, { height: DIGIT_HEIGHT, backgroundColor: colors.surface1.val }, - ]}> + ]} + > {currency.groupingSeparator} @@ -236,12 +224,9 @@ const RollNumber = ({ { marginRight: -ADDITIONAL_WIDTH_FOR_ANIMATIONS, }, - ]}> - + ]} + > + ) } @@ -272,7 +257,8 @@ const Numbers = ({ (index) => ( + style={[{ height: DIGIT_HEIGHT }, AnimatedCharStyles.wrapperStyle]} + > - ) + ), ) } @@ -334,7 +320,7 @@ const PriceExplorerAnimatedNumber = ({ return Number(accumulator) + Number(NUMBER_WIDTH_ARRAY[Number(currentValue)]) } return accumulator - }) + }), ) }, (priceWidth: number) => { @@ -348,7 +334,7 @@ const PriceExplorerAnimatedNumber = ({ scale.value = withTiming(1) offset.value = withTiming(0) } - } + }, ) const hidePlaceholder = (): void => { @@ -358,7 +344,8 @@ const PriceExplorerAnimatedNumber = ({ const currencySymbol = ( + style={[AnimatedFontStyles.fontStyle, { height: DIGIT_HEIGHT, color: colors.neutral1.val }]} + > {currency.fullSymbol} ) @@ -366,22 +353,15 @@ const PriceExplorerAnimatedNumber = ({ const lessThanSymbol = ( + style={[AnimatedFontStyles.fontStyle, { height: DIGIT_HEIGHT, color: colors.neutral1.val }, lessThanStyle]} + > {'<'} ) const scaleWraper = useAnimatedStyle(() => { return { - transform: [ - { translateX: -SCREEN_WIDTH / 2 }, - { scale: scale.value }, - { translateX: SCREEN_WIDTH / 2 }, - ], + transform: [{ translateX: -SCREEN_WIDTH / 2 }, { scale: scale.value }, { translateX: SCREEN_WIDTH / 2 }], } }) diff --git a/apps/mobile/src/components/PriceExplorer/PriceExplorerError.tsx b/apps/mobile/src/components/PriceExplorer/PriceExplorerError.tsx index a18b71e8fab..779f4657129 100644 --- a/apps/mobile/src/components/PriceExplorer/PriceExplorerError.tsx +++ b/apps/mobile/src/components/PriceExplorer/PriceExplorerError.tsx @@ -27,7 +27,8 @@ export function PriceExplorerError({ borderRadius="$rounded16" height={chartHeight} justifyContent="center" - overflow="hidden"> + overflow="hidden" + > + 0 ? -1 : 1 }, ]} /> - + ) } diff --git a/apps/mobile/src/components/PriceExplorer/TimeRangeGroup.tsx b/apps/mobile/src/components/PriceExplorer/TimeRangeGroup.tsx index a97bb5a6ee0..38796adb87a 100644 --- a/apps/mobile/src/components/PriceExplorer/TimeRangeGroup.tsx +++ b/apps/mobile/src/components/PriceExplorer/TimeRangeGroup.tsx @@ -1,11 +1,6 @@ import React, { useState } from 'react' import { I18nManager, StyleSheet, View } from 'react-native' -import { - SharedValue, - interpolateColor, - useAnimatedStyle, - useSharedValue, -} from 'react-native-reanimated' +import { SharedValue, interpolateColor, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' import { TIME_RANGES } from 'src/components/PriceExplorer/constants' import { useChartDimensions } from 'src/components/PriceExplorer/useChartDimensions' import { Flex, TouchableArea, useSporeColors } from 'ui/src' @@ -31,11 +26,7 @@ export function TimeRangeLabel({ index, label, selectedIndex, transition }: Prop return { color: colors.neutral2.val } } - const color = interpolateColor( - transition.value, - [0, 1], - [colors.neutral2.val, colors.neutral1.val] - ) + const color = interpolateColor(transition.value, [0, 1], [colors.neutral2.val, colors.neutral1.val]) return { color } }) @@ -47,11 +38,7 @@ export function TimeRangeLabel({ index, label, selectedIndex, transition }: Prop ) } -export function TimeRangeGroup({ - setDuration, -}: { - setDuration: (newDuration: HistoryDuration) => void -}): JSX.Element { +export function TimeRangeGroup({ setDuration }: { setDuration: (newDuration: HistoryDuration) => void }): JSX.Element { const { chartWidth, buttonWidth, labelWidth } = useChartDimensions() const transition = useSharedValue(1) const previousIndex = useSharedValue(1) @@ -72,7 +59,7 @@ export function TimeRangeGroup({ }, ], }), - [adjustedLabelWidth, buttonWidth, currentIndex, isRTL] + [adjustedLabelWidth, buttonWidth, currentIndex, isRTL], ) return ( @@ -98,7 +85,8 @@ export function TimeRangeGroup({ transition.value = 0 currentIndex.value = index transition.value = 1 - }}> + }} + > adjustedLabelWidth) { setAdjustedLabelWidth(width) } - }}> - + }} + > + diff --git a/apps/mobile/src/components/PriceExplorer/constants.ts b/apps/mobile/src/components/PriceExplorer/constants.ts index 2b128b60a56..e725b225076 100644 --- a/apps/mobile/src/components/PriceExplorer/constants.ts +++ b/apps/mobile/src/components/PriceExplorer/constants.ts @@ -9,27 +9,11 @@ export const CURSOR_SIZE = CURSOR_INNER_SIZE + 6 export const LINE_WIDTH = 1 export const TIME_RANGES = [ - [ - HistoryDuration.Hour, - i18n.t('token.priceExplorer.timeRangeLabel.hour'), - ElementName.TimeFrame1H, - ], + [HistoryDuration.Hour, i18n.t('token.priceExplorer.timeRangeLabel.hour'), ElementName.TimeFrame1H], [HistoryDuration.Day, i18n.t('token.priceExplorer.timeRangeLabel.day'), ElementName.TimeFrame1D], - [ - HistoryDuration.Week, - i18n.t('token.priceExplorer.timeRangeLabel.week'), - ElementName.TimeFrame1W, - ], - [ - HistoryDuration.Month, - i18n.t('token.priceExplorer.timeRangeLabel.month'), - ElementName.TimeFrame1M, - ], - [ - HistoryDuration.Year, - i18n.t('token.priceExplorer.timeRangeLabel.year'), - ElementName.TimeFrame1Y, - ], + [HistoryDuration.Week, i18n.t('token.priceExplorer.timeRangeLabel.week'), ElementName.TimeFrame1W], + [HistoryDuration.Month, i18n.t('token.priceExplorer.timeRangeLabel.month'), ElementName.TimeFrame1M], + [HistoryDuration.Year, i18n.t('token.priceExplorer.timeRangeLabel.year'), ElementName.TimeFrame1Y], // TODO (MOB-3585): fix performance issue with All time range and re-enable // [HistoryDuration.Max, i18n.t('token.priceExplorer.timeRangeLabel.all'), ElementName.TimeFrameAll], ] as const diff --git a/apps/mobile/src/components/PriceExplorer/usePrice.tsx b/apps/mobile/src/components/PriceExplorer/usePrice.tsx index a9bc690bede..438b15b9625 100644 --- a/apps/mobile/src/components/PriceExplorer/usePrice.tsx +++ b/apps/mobile/src/components/PriceExplorer/usePrice.tsx @@ -1,14 +1,6 @@ import { useMemo } from 'react' -import { - SharedValue, - useAnimatedReaction, - useDerivedValue, - useSharedValue, -} from 'react-native-reanimated' -import { - useLineChart, - useLineChartPrice as useRNWagmiChartLineChartPrice, -} from 'react-native-wagmi-charts' +import { SharedValue, useAnimatedReaction, useDerivedValue, useSharedValue } from 'react-native-reanimated' +import { useLineChart, useLineChartPrice as useRNWagmiChartLineChartPrice } from 'react-native-wagmi-charts' import { numberToLocaleStringWorklet, numberToPercentWorklet } from 'src/utils/reanimated' import { useAppFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' import { useCurrentLocale } from 'wallet/src/features/language/hooks' @@ -26,9 +18,7 @@ export type ValueAndFormattedWithAnimation = ValueAndFormatted & { * Wrapper around react-native-wagmi-chart#useLineChartPrice * @returns latest price when not scrubbing and active price when scrubbing */ -export function useLineChartPrice( - currentSpot?: SharedValue -): ValueAndFormattedWithAnimation { +export function useLineChartPrice(currentSpot?: SharedValue): ValueAndFormattedWithAnimation { const { value: activeCursorPrice } = useRNWagmiChartLineChartPrice({ // do not round precision: 18, @@ -44,7 +34,7 @@ export function useLineChartPrice( if (previousValue && currentValue && shouldAnimate.value) { shouldAnimate.value = false } - } + }, ) const currencyInfo = useAppFiatCurrencyInfo() const locale = useCurrentLocale() @@ -68,7 +58,7 @@ export function useLineChartPrice( style: 'currency', currency: code, }, - symbol + symbol, ) }) @@ -78,7 +68,7 @@ export function useLineChartPrice( formatted: priceFormatted, shouldAnimate, }), - [price, priceFormatted, shouldAnimate] + [price, priceFormatted, shouldAnimate], ) } @@ -99,9 +89,7 @@ export function useLineChartRelativeChange(): ValueAndFormatted { // scrubbing: close price is active price // not scrubbing: close price is period end price - const closePrice = isActive.value - ? data[currentIndex.value]?.value - : data[data.length - 1]?.value + const closePrice = isActive.value ? data[currentIndex.value]?.value : data[data.length - 1]?.value if (openPrice === undefined || closePrice === undefined || openPrice === 0) { return 0 diff --git a/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts b/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts index e8a7ddcc5c5..94e7474609a 100644 --- a/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts +++ b/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts @@ -2,13 +2,13 @@ import { maxBy } from 'lodash' import { Dispatch, SetStateAction, useCallback, useMemo, useRef, useState } from 'react' import { SharedValue, useDerivedValue } from 'react-native-reanimated' import { TLineChartData } from 'react-native-wagmi-charts' +import { PollingInterval } from 'uniswap/src/constants/misc' import { HistoryDuration, TimestampedAmount, useTokenPriceHistoryQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GqlResult } from 'uniswap/src/data/types' -import { PollingInterval } from 'wallet/src/constants/misc' import { isError, isNonPollingRequestInFlight } from 'wallet/src/data/utils' import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' @@ -29,7 +29,7 @@ export type PriceNumberOfDigits = { export function useTokenPriceHistory( currencyId: string, onCompleted?: () => void, - initialDuration: HistoryDuration = HistoryDuration.Day + initialDuration: HistoryDuration = HistoryDuration.Day, ): Omit< GqlResult<{ priceHistory?: TLineChartData @@ -86,7 +86,7 @@ export function useTokenPriceHistory( relativeChange: spotRelativeChange, } : undefined, - [price, spotValue, spotRelativeChange] + [price, spotValue, spotRelativeChange], ) const formattedPriceHistory = useMemo(() => { @@ -102,7 +102,7 @@ export function useTokenPriceHistory( priceHistory: formattedPriceHistory, spot, }), - [formattedPriceHistory, spot] + [formattedPriceHistory, spot], ) const numberOfDigits = useMemo(() => { @@ -139,6 +139,6 @@ export function useTokenPriceHistory( numberOfDigits, onCompleted, }), - [data, duration, networkStatus, priceData, retry, onCompleted, numberOfDigits] + [data, duration, networkStatus, priceData, retry, onCompleted, numberOfDigits], ) } diff --git a/apps/mobile/src/components/QRCodeScanner/QRCodeScanner.tsx b/apps/mobile/src/components/QRCodeScanner/QRCodeScanner.tsx index 6ab4c88f278..b548fd02e67 100644 --- a/apps/mobile/src/components/QRCodeScanner/QRCodeScanner.tsx +++ b/apps/mobile/src/components/QRCodeScanner/QRCodeScanner.tsx @@ -62,7 +62,7 @@ export function QRCodeScanner(props: QRCodeScannerProps | WCScannerProps): JSX.E onScanCode(data) setIsReadingImageFile(false) }, - [onScanCode, shouldFreezeCamera] + [onScanCode, shouldFreezeCamera], ) const onPickImageFilePress = useCallback(async (): Promise => { @@ -84,9 +84,7 @@ export function QRCodeScanner(props: QRCodeScannerProps | WCScannerProps): JSX.E return } - const result = ( - await BarCodeScanner.scanFromURLAsync(uri, [BarCodeScanner.Constants.BarCodeType.qr]) - )[0] + const result = (await BarCodeScanner.scanFromURLAsync(uri, [BarCodeScanner.Constants.BarCodeType.qr]))[0] if (!result) { Alert.alert(t('qrScanner.error.none')) @@ -127,12 +125,7 @@ export function QRCodeScanner(props: QRCodeScannerProps | WCScannerProps): JSX.E const scannerSize = Math.min(overlayWidth, cameraWidth) * SCAN_ICON_WIDTH_RATIO return ( - + {permissionStatus === PermissionStatus.GRANTED && !isReadingImageFile && ( @@ -147,17 +140,14 @@ export function QRCodeScanner(props: QRCodeScannerProps | WCScannerProps): JSX.E )} - + setOverlayLayout(event.nativeEvent.layout)}> + onLayout={(event: LayoutChangeEvent): void => setOverlayLayout(event.nativeEvent.layout)} + > setInfoLayout(event.nativeEvent.layout)}> + onLayout={(event: LayoutChangeEvent): void => setInfoLayout(event.nativeEvent.layout)} + > {t('qrScanner.title')} {!shouldFreezeCamera ? ( // camera isn't frozen (after seeing barcode) — show the camera scan icon (the four white corners) - + ) : ( // camera has been frozen (has seen a barcode) — show the loading spinner and "Connecting..." or "Loading..." - + + top={scannerSize / 2 - LOADER_SIZE / 2} + > - {isWalletConnectModal - ? t('qrScanner.status.connecting') - : t('qrScanner.status.loading')} + {isWalletConnectModal ? t('qrScanner.status.connecting') : t('qrScanner.status.loading')} @@ -213,18 +193,15 @@ export function QRCodeScanner(props: QRCodeScannerProps | WCScannerProps): JSX.E {/* when in development mode AND there's no camera (using iOS Simulator), add a paste button */} {!shouldFreezeCamera ? ( - + + p="$spacing12" + > This paste button will only show up in development mode @@ -246,15 +223,15 @@ export function QRCodeScanner(props: QRCodeScannerProps | WCScannerProps): JSX.E }, ], }} - onLayout={(event: LayoutChangeEvent): void => - setBottomLayout(event.nativeEvent.layout) - }> + onLayout={(event: LayoutChangeEvent): void => setBottomLayout(event.nativeEvent.layout)} + > + onPress={onPickImageFilePress} + > {isReadingImageFile ? ( ) : ( @@ -267,7 +244,8 @@ export function QRCodeScanner(props: QRCodeScannerProps | WCScannerProps): JSX.E fontFamily="$body" icon={} theme="secondary" - onPress={props.onPressConnections}> + onPress={props.onPressConnections} + > {t('qrScanner.button.connections', { count: props.numConnections })} )} @@ -329,24 +307,17 @@ const GradientOverlay = memo(function GradientOverlay({ justifyContent="center" position="absolute" style={StyleSheet.absoluteFill} - onLayout={onLayout}> + onLayout={onLayout} + > - + - + {!shouldFreezeCamera ? ( diff --git a/apps/mobile/src/components/RecipientSelect/RecipientScanModal.tsx b/apps/mobile/src/components/RecipientSelect/RecipientScanModal.tsx index af72743da39..77ca49db9e4 100644 --- a/apps/mobile/src/components/RecipientSelect/RecipientScanModal.tsx +++ b/apps/mobile/src/components/RecipientSelect/RecipientScanModal.tsx @@ -24,9 +24,7 @@ export function RecipientScanModal({ onSelectRecipient, onClose }: Props): JSX.E const { t } = useTranslation() const colors = useSporeColors() const activeAddress = useAppSelector(selectActiveAccountAddress) - const [currentScreenState, setCurrentScreenState] = useState( - ScannerModalState.ScanQr - ) + const [currentScreenState, setCurrentScreenState] = useState(ScannerModalState.ScanQr) const [shouldFreezeCamera, setShouldFreezeCamera] = useState(false) const onScanCode = async (uri: string): Promise => { @@ -68,13 +66,12 @@ export function RecipientScanModal({ onSelectRecipient, onClose }: Props): JSX.E fullScreen backgroundColor={colors.surface1.get()} name={ModalName.WalletConnectScan} - onClose={onClose}> + onClose={onClose} + > {currentScreenState === ScannerModalState.ScanQr && ( )} - {currentScreenState === ScannerModalState.WalletQr && activeAddress && ( - - )} + {currentScreenState === ScannerModalState.WalletQr && activeAddress && } + onPress={onPressBottomToggle} + > {currentScreenState === ScannerModalState.ScanQr ? ( - + ) : ( - + )} {currentScreenState === ScannerModalState.ScanQr diff --git a/apps/mobile/src/components/RecipientSelect/RecipientSelect.tsx b/apps/mobile/src/components/RecipientSelect/RecipientSelect.tsx index 4573aea1fd5..e7da5872146 100644 --- a/apps/mobile/src/components/RecipientSelect/RecipientSelect.tsx +++ b/apps/mobile/src/components/RecipientSelect/RecipientSelect.tsx @@ -24,11 +24,7 @@ function QRScannerIconButton({ onPress }: { onPress: () => void }): JSX.Element return ( - + ) } @@ -56,13 +52,7 @@ export function _RecipientSelect({ return ( <> - + {t('qrScanner.recipient.label.send')} @@ -84,14 +74,10 @@ export function _RecipientSelect({ ) : ( // Show either suggested recipients or filtered sections based on query - isSheetReady && ( - - ) + isSheetReady && )} - {showQRScanner && ( - - )} + {showQRScanner && } ) } diff --git a/apps/mobile/src/components/RemoveWallet/AssociatedAccountsList.tsx b/apps/mobile/src/components/RemoveWallet/AssociatedAccountsList.tsx index d2550c2f796..5b594520f0e 100644 --- a/apps/mobile/src/components/RemoveWallet/AssociatedAccountsList.tsx +++ b/apps/mobile/src/components/RemoveWallet/AssociatedAccountsList.tsx @@ -44,7 +44,8 @@ function _AssociatedAccountsList({ accounts }: { accounts: Account[] }): JSX.Ele borderWidth={1} maxHeight={accountsScrollViewHeight} px="$spacing12" - width="100%"> + width="100%" + > {sortedAddressesByBalance.map(({ address, balance }, index) => ( + pb={index !== totalCount - 1 ? '$spacing16' : undefined} + > diff --git a/apps/mobile/src/components/RemoveWallet/RemoveLastMnemonicWalletFooter.tsx b/apps/mobile/src/components/RemoveWallet/RemoveLastMnemonicWalletFooter.tsx index 6d6178fa1e1..0c35cf3372c 100644 --- a/apps/mobile/src/components/RemoveWallet/RemoveLastMnemonicWalletFooter.tsx +++ b/apps/mobile/src/components/RemoveWallet/RemoveLastMnemonicWalletFooter.tsx @@ -17,12 +17,7 @@ export function RemoveLastMnemonicWalletFooter({ return ( <> - + : undefined} testID={ElementName.Confirm} theme="detrimental" - onPress={onPress}> + onPress={onPress} + > {!inProgress ? t('account.wallet.button.remove') : undefined} diff --git a/apps/mobile/src/components/RemoveWallet/RemoveWalletModal.tsx b/apps/mobile/src/components/RemoveWallet/RemoveWalletModal.tsx index 35a50e5dc3a..ffc9f058e04 100644 --- a/apps/mobile/src/components/RemoveWallet/RemoveWalletModal.tsx +++ b/apps/mobile/src/components/RemoveWallet/RemoveWalletModal.tsx @@ -20,10 +20,7 @@ import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding' import { MobileScreens, OnboardingScreens } from 'uniswap/src/types/screens/mobile' import { logger } from 'utilities/src/logger/logger' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { - EditAccountAction, - editAccountActions, -} from 'wallet/src/features/wallet/accounts/editAccountSaga' +import { EditAccountAction, editAccountActions } from 'wallet/src/features/wallet/accounts/editAccountSaga' import { useAccounts } from 'wallet/src/features/wallet/hooks' import { selectSignerMnemonicAccounts } from 'wallet/src/features/wallet/selectors' import { setFinishedOnboarding } from 'wallet/src/features/wallet/slice' @@ -48,12 +45,11 @@ export function RemoveWalletModal(): JSX.Element | null { const isRemovingLastMnemonic = isRemovingMnemonic && associatedAccounts.length === 1 const isRemovingRecoveryPhrase = isReplacing || isRemovingLastMnemonic - const hasAccountsLeft = - Object.keys(addressToAccount).length > (isReplacing ? associatedAccounts.length : 1) + const hasAccountsLeft = Object.keys(addressToAccount).length > (isReplacing ? associatedAccounts.length : 1) const [inProgress, setInProgress] = useState(false) const [currentStep, setCurrentStep] = useState( - isRemovingRecoveryPhrase ? RemoveWalletStep.Warning : RemoveWalletStep.Final + isRemovingRecoveryPhrase ? RemoveWalletStep.Warning : RemoveWalletStep.Final, ) const onClose = useCallback((): void => { @@ -87,7 +83,7 @@ export function RemoveWalletModal(): JSX.Element | null { editAccountActions.trigger({ type: EditAccountAction.Remove, accounts: accountsToRemove, - }) + }), ) }) .catch((error) => { @@ -101,7 +97,7 @@ export function RemoveWalletModal(): JSX.Element | null { editAccountActions.trigger({ type: EditAccountAction.Remove, accounts: accountsToRemove, - }) + }), ) } @@ -115,7 +111,7 @@ export function RemoveWalletModal(): JSX.Element | null { }, () => { setInProgress(false) - } + }, ) const { @@ -163,8 +159,7 @@ export function RemoveWalletModal(): JSX.Element | null { return null } - const { title, description, Icon, iconColorLabel, actionButtonTheme, actionButtonLabel } = - modalContent + const { title, description, Icon, iconColorLabel, actionButtonTheme, actionButtonLabel } = modalContent // TODO(MOB-1420): clean up types const labelColor: ThemeKeys = iconColorLabel @@ -173,7 +168,8 @@ export function RemoveWalletModal(): JSX.Element | null { + onClose={onClose} + > - + }} + > + @@ -226,7 +219,8 @@ export function RemoveWalletModal(): JSX.Element | null { testID={isRemovingRecoveryPhrase ? ElementName.Continue : ElementName.Remove} theme={actionButtonTheme} width="100%" - onPress={onPress}> + onPress={onPress} + > {inProgress ? undefined : actionButtonLabel} diff --git a/apps/mobile/src/components/RemoveWallet/useModalContent.tsx b/apps/mobile/src/components/RemoveWallet/useModalContent.tsx index 6f4f2f71533..3a0ec12bb2b 100644 --- a/apps/mobile/src/components/RemoveWallet/useModalContent.tsx +++ b/apps/mobile/src/components/RemoveWallet/useModalContent.tsx @@ -91,9 +91,7 @@ export const useModalContent = ({ description: ( - ), + highlight: , }} i18nKey="account.recoveryPhrase.remove.final.description" values={{ cloudProviderName: getCloudProviderName() }} @@ -107,10 +105,8 @@ export const useModalContent = ({ // removing mnemonic account if (account?.type === AccountType.SignerMnemonic && currentStep === RemoveWalletStep.Final) { const associatedAccountNames = concatStrings( - associatedAccounts - .filter((aa): aa is Account => aa.address !== account?.address) - .map((aa) => aa.name ?? ''), - t('common.endAdornment') + associatedAccounts.filter((aa): aa is Account => aa.address !== account?.address).map((aa) => aa.name ?? ''), + t('common.endAdornment'), ) return { @@ -162,13 +158,5 @@ export const useModalContent = ({ actionButtonTheme: 'secondary', } } - }, [ - account, - associatedAccounts, - currentStep, - displayName, - isRemovingRecoveryPhrase, - isReplacing, - t, - ]) + }, [account, associatedAccounts, currentStep, displayName, isRemovingRecoveryPhrase, isReplacing, t]) } diff --git a/apps/mobile/src/components/RemoveWallet/utils.ts b/apps/mobile/src/components/RemoveWallet/utils.ts index 811a6330160..df0cb6213aa 100644 --- a/apps/mobile/src/components/RemoveWallet/utils.ts +++ b/apps/mobile/src/components/RemoveWallet/utils.ts @@ -33,6 +33,6 @@ export function navigateToOnboardingImportMethod(): void { }, }, ], - }) + }), ) } diff --git a/apps/mobile/src/components/RestoreWalletModal/RestoreWalletModal.tsx b/apps/mobile/src/components/RestoreWalletModal/RestoreWalletModal.tsx index 30c0f27788c..40d8768ea4b 100644 --- a/apps/mobile/src/components/RestoreWalletModal/RestoreWalletModal.tsx +++ b/apps/mobile/src/components/RestoreWalletModal/RestoreWalletModal.tsx @@ -32,10 +32,7 @@ export function RestoreWalletModal(): JSX.Element | null { } return ( - + - + }} + > + {t('account.wallet.button.restore')} diff --git a/apps/mobile/src/components/Settings/BiometricAuthWarningModal.tsx b/apps/mobile/src/components/Settings/BiometricAuthWarningModal.tsx index 9c5867b08cc..c26a32da8ce 100644 --- a/apps/mobile/src/components/Settings/BiometricAuthWarningModal.tsx +++ b/apps/mobile/src/components/Settings/BiometricAuthWarningModal.tsx @@ -3,10 +3,7 @@ import { useTranslation } from 'react-i18next' import { useBiometricName } from 'src/features/biometrics/hooks' import { ModalName } from 'uniswap/src/features/telemetry/constants' import { isAndroid } from 'utilities/src/platform' -import { - WarningModal, - WarningModalProps, -} from 'wallet/src/components/modals/WarningModal/WarningModal' +import { WarningModal, WarningModalProps } from 'wallet/src/components/modals/WarningModal/WarningModal' import { WarningSeverity } from 'wallet/src/features/transactions/WarningModal/types' type Props = { @@ -15,11 +12,7 @@ type Props = { onClose: WarningModalProps['onClose'] } -export function BiometricAuthWarningModal({ - isTouchIdDevice, - onConfirm, - onClose, -}: Props): JSX.Element { +export function BiometricAuthWarningModal({ isTouchIdDevice, onConfirm, onClose }: Props): JSX.Element { const { t } = useTranslation() const biometricsMethod = useBiometricName(isTouchIdDevice) return ( diff --git a/apps/mobile/src/components/Settings/SettingsRow.tsx b/apps/mobile/src/components/Settings/SettingsRow.tsx index 593f62d77b6..b3895b5e547 100644 --- a/apps/mobile/src/components/Settings/SettingsRow.tsx +++ b/apps/mobile/src/components/Settings/SettingsRow.tsx @@ -88,12 +88,7 @@ export function SettingsRow({ return ( - + {icon} @@ -114,22 +109,12 @@ export function SettingsRow({ {currentSetting ? ( - + {currentSetting} ) : null} - + ) : externalLink ? ( diff --git a/apps/mobile/src/components/TokenBalanceList/TokenBalanceItemContextMenu.tsx b/apps/mobile/src/components/TokenBalanceList/TokenBalanceItemContextMenu.tsx index 22a0691c7df..f19d8be83bd 100644 --- a/apps/mobile/src/components/TokenBalanceList/TokenBalanceItemContextMenu.tsx +++ b/apps/mobile/src/components/TokenBalanceList/TokenBalanceItemContextMenu.tsx @@ -19,11 +19,7 @@ export const TokenBalanceItemContextMenu = memo(function _TokenBalanceItem({ const style = useMemo(() => ({ borderRadius: borderRadii.rounded16 }), []) return ( - + {children} ) diff --git a/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx b/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx index 83c252c98e4..6dba5b58aa6 100644 --- a/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx +++ b/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx @@ -8,11 +8,7 @@ import Animated, { FadeInDown, FadeOut } from 'react-native-reanimated' import { useAppStackNavigation } from 'src/app/navigation/types' import { TokenBalanceItemContextMenu } from 'src/components/TokenBalanceList/TokenBalanceItemContextMenu' import { useAdaptiveFooter } from 'src/components/home/hooks' -import { - TAB_BAR_HEIGHT, - TAB_VIEW_SCROLL_THROTTLE, - TabProps, -} from 'src/components/layout/TabHelpers' +import { TAB_BAR_HEIGHT, TAB_VIEW_SCROLL_THROTTLE, TabProps } from 'src/components/layout/TabHelpers' import { Flex, Loader, useDeviceInsets, useSporeColors } from 'ui/src' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { zIndices } from 'ui/src/theme' @@ -39,15 +35,9 @@ type TokenBalanceListProps = TabProps & { const ESTIMATED_TOKEN_ITEM_HEIGHT = 64 export const TokenBalanceList = forwardRef, TokenBalanceListProps>( - function _TokenBalanceList( - { owner, onPressToken, isExternalProfile = false, ...rest }, - ref - ): JSX.Element { + function _TokenBalanceList({ owner, onPressToken, isExternalProfile = false, ...rest }, ref): JSX.Element { return ( - + , TokenB /> ) - } + }, ) -export const TokenBalanceListInner = forwardRef< - FlatList, - TokenBalanceListProps ->(function _TokenBalanceListInner( - { - empty, - containerProps, - scrollHandler, - isExternalProfile = false, - renderedInModal = false, - refreshing, - headerHeight = 0, - onRefresh, - }, - ref -) { - const { t } = useTranslation() - const colors = useSporeColors() - const insets = useDeviceInsets() +export const TokenBalanceListInner = forwardRef, TokenBalanceListProps>( + function _TokenBalanceListInner( + { + empty, + containerProps, + scrollHandler, + isExternalProfile = false, + renderedInModal = false, + refreshing, + headerHeight = 0, + onRefresh, + }, + ref, + ) { + const { t } = useTranslation() + const colors = useSporeColors() + const insets = useDeviceInsets() - const { rows, balancesById, networkStatus, refetch } = useTokenBalanceListContext() - const hasError = isError(networkStatus, !!balancesById) + const { rows, balancesById, networkStatus, refetch } = useTokenBalanceListContext() + const hasError = isError(networkStatus, !!balancesById) - const { onContentSizeChange, adaptiveFooter } = useAdaptiveFooter( - containerProps?.contentContainerStyle - ) + const { onContentSizeChange, adaptiveFooter } = useAdaptiveFooter(containerProps?.contentContainerStyle) - // The following logic is meant to speed up the screen transition from the token details screen back to the home screen. - // When we call `navigation.goBack()`, a re-render is triggered *before* the animation begins. - // In order for that first re-render to be fast, we use `cachedData` so that it renders a memoized `FlatList` of tokens, - // (this `FlatList` is the most expensive component on this screen). - // After the transition ends, we set focus to `true` to trigger a re-render using the latest `data`. + // The following logic is meant to speed up the screen transition from the token details screen back to the home screen. + // When we call `navigation.goBack()`, a re-render is triggered *before* the animation begins. + // In order for that first re-render to be fast, we use `cachedData` so that it renders a memoized `FlatList` of tokens, + // (this `FlatList` is the most expensive component on this screen). + // After the transition ends, we set focus to `true` to trigger a re-render using the latest `data`. - const [isFocused, setIsFocused] = useState(true) - const [cachedRows, setCachedRows] = useState(null) + const [isFocused, setIsFocused] = useState(true) + const [cachedRows, setCachedRows] = useState(null) - const rowsRef = useRef(rows) - rowsRef.current = rows + const rowsRef = useRef(rows) + rowsRef.current = rows - useFocusEffect( - useCallback(() => { - return (): void => { - // We save the cached data to avoid a re-render when the user navigates back to it. - // This speeds up the animation while preserving the scroll position. - setCachedRows(rowsRef.current) - setIsFocused(false) - } - }, []) - ) + useFocusEffect( + useCallback(() => { + return (): void => { + // We save the cached data to avoid a re-render when the user navigates back to it. + // This speeds up the animation while preserving the scroll position. + setCachedRows(rowsRef.current) + setIsFocused(false) + } + }, []), + ) - const navigation = useAppStackNavigation() + const navigation = useAppStackNavigation() - useEffect(() => { - // We use this instead of relying on react-navigation's `useIsFocused` because we want to speed up the screen transition - // when the user goes from the token details screen back to the home screen, so we want this state to change *after* the animation is done instead of *before*. - const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', (e) => { - if (!e.data.closing) { - setIsFocused(true) - } - }) + useEffect(() => { + // We use this instead of relying on react-navigation's `useIsFocused` because we want to speed up the screen transition + // when the user goes from the token details screen back to the home screen, so we want this state to change *after* the animation is done instead of *before*. + const unsubscribeTransitionEnd = navigation.addListener('transitionEnd', (e) => { + if (!e.data.closing) { + setIsFocused(true) + } + }) - return (): void => unsubscribeTransitionEnd() - }, [navigation]) + return (): void => unsubscribeTransitionEnd() + }, [navigation]) - const refreshControl = useMemo(() => { - return ( - + const refreshControl = useMemo(() => { + return ( + + ) + }, [insets.top, headerHeight, refreshing, colors.neutral3, onRefresh]) + + // In order to avoid unnecessary re-renders of the entire FlatList, the `renderItem` function should never change. + // That's why we use a context provider so that each row can read from there instead of passing down new props every time the data changes. + const renderItem = useCallback( + ({ item }: { item: TokenBalanceListRow }): JSX.Element => , + [], ) - }, [insets.top, headerHeight, refreshing, colors.neutral3, onRefresh]) - // In order to avoid unnecessary re-renders of the entire FlatList, the `renderItem` function should never change. - // That's why we use a context provider so that each row can read from there instead of passing down new props every time the data changes. - const renderItem = useCallback( - ({ item }: { item: TokenBalanceListRow }): JSX.Element => , - [] - ) + const keyExtractor = useCallback((item: TokenBalanceListRow): string => item, []) - const keyExtractor = useCallback((item: TokenBalanceListRow): string => item, []) + const ListEmptyComponent = useMemo(() => { + if (hasError) { + return ( + + refetch?.()} + /> + + ) + } - const ListEmptyComponent = useMemo(() => { - if (hasError) { - return ( - - refetch?.()} - /> - - ) - } + if (isNonPollingRequestInFlight(networkStatus)) { + return ( + + + + ) + } - if (isNonPollingRequestInFlight(networkStatus)) { return ( - - + + {empty} ) - } - - return ( - - {empty} - - ) - }, [hasError, empty, t, networkStatus, refetch]) + }, [hasError, empty, t, networkStatus, refetch]) - const ListHeaderComponent = useMemo(() => { - return hasError ? ( - - - - ) : null - }, [hasError, refetch, t]) + const ListHeaderComponent = useMemo(() => { + return hasError ? ( + + + + ) : null + }, [hasError, refetch, t]) - // add negative z index to prevent footer from covering hidden tokens row when minimized - const ListFooterComponentStyle = useMemo(() => ({ zIndex: zIndices.negative }), []) + // add negative z index to prevent footer from covering hidden tokens row when minimized + const ListFooterComponentStyle = useMemo(() => ({ zIndex: zIndices.negative }), []) - const List = renderedInModal - ? BottomSheetFlatList - : Animated.FlatList + const List = renderedInModal ? BottomSheetFlatList : Animated.FlatList - const getItemLayout = useCallback( - ( - _: Maybe>, - index: number - ): { length: number; offset: number; index: number } => ({ - length: ESTIMATED_TOKEN_ITEM_HEIGHT, - offset: ESTIMATED_TOKEN_ITEM_HEIGHT * index, - index, - }), - [] - ) + const getItemLayout = useCallback( + (_: Maybe>, index: number): { length: number; offset: number; index: number } => ({ + length: ESTIMATED_TOKEN_ITEM_HEIGHT, + offset: ESTIMATED_TOKEN_ITEM_HEIGHT * index, + index, + }), + [], + ) - const data = balancesById ? (isFocused ? rows : cachedRows) : undefined + const data = balancesById ? (isFocused ? rows : cachedRows) : undefined - // Note: `PerformanceView` must wrap the entire return statement to properly track interactive states. - return ( - - - - ) -}) + // Note: `PerformanceView` must wrap the entire return statement to properly track interactive states. + return ( + + + + ) + }, +) -const TokenBalanceItemRow = memo(function TokenBalanceItemRow({ - item, -}: { - item: TokenBalanceListRow -}) { +const TokenBalanceItemRow = memo(function TokenBalanceItemRow({ item }: { item: TokenBalanceListRow }) { const { balancesById, hiddenTokensCount, diff --git a/apps/mobile/src/components/TokenDetails/LinkButton.tsx b/apps/mobile/src/components/TokenDetails/LinkButton.tsx index 3f5377df27a..a65e13490c8 100644 --- a/apps/mobile/src/components/TokenDetails/LinkButton.tsx +++ b/apps/mobile/src/components/TokenDetails/LinkButton.tsx @@ -42,7 +42,7 @@ export function LinkButton({ pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.Address, - }) + }), ) } @@ -63,18 +63,15 @@ export function LinkButton({ px="$spacing12" py="$spacing8" testID={element} - onPress={onPress}> + onPress={onPress} + > {Icon && } {label} {buttonType === LinkButtonType.Copy && ( - + )} diff --git a/apps/mobile/src/components/TokenDetails/SendButton.tsx b/apps/mobile/src/components/TokenDetails/SendButton.tsx index f6698be17b7..6439815e3e4 100644 --- a/apps/mobile/src/components/TokenDetails/SendButton.tsx +++ b/apps/mobile/src/components/TokenDetails/SendButton.tsx @@ -12,12 +12,7 @@ type Props = { export function SendButton({ onPress, color, size = iconSizes.icon24 }: Props): JSX.Element { return ( - + diff --git a/apps/mobile/src/components/TokenDetails/TokenBalances.tsx b/apps/mobile/src/components/TokenDetails/TokenBalances.tsx index a3b567c3465..e07f223e637 100644 --- a/apps/mobile/src/components/TokenDetails/TokenBalances.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenBalances.tsx @@ -45,7 +45,7 @@ export function TokenBalances({ preload(currencyId) navigateWithPop(currencyId) }, - [navigateWithPop, preload] + [navigateWithPop, preload], ) if (!hasCurrentChainBalances && !hasOtherChainBalances) { @@ -106,14 +106,10 @@ export function CurrentChainBalance({ - {isReadonly - ? t('token.balances.viewOnly', { ownerAddress: displayName }) - : t('token.balances.main')} + {isReadonly ? t('token.balances.viewOnly', { ownerAddress: displayName }) : t('token.balances.main')} - - {convertFiatAmountFormatted(balance.balanceUSD, NumberType.FiatTokenDetails)} - + {convertFiatAmountFormatted(balance.balanceUSD, NumberType.FiatTokenDetails)} {formatNumberOrString({ value: balance.quantity, type: NumberType.TokenNonTx })}{' '} {getSymbolDisplayText(balance.currencyInfo.currency.symbol)} diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsActionButtons.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsActionButtons.tsx index 9e483974c44..fc9aa3cb5ec 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsActionButtons.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsActionButtons.tsx @@ -30,7 +30,8 @@ function CTAButton({ // eslint-disable-next-line react/jsx-sort-props onPress={onPress} size="large" - backgroundColor={validColor(tokenColor) ?? '$accent1'}> + backgroundColor={validColor(tokenColor) ?? '$accent1'} + > {title} @@ -59,7 +60,8 @@ export function TokenDetailsActionButtons({ gap="$spacing8" pb="$spacing16" pt="$spacing12" - px="$spacing16"> + px="$spacing16" + > + ) diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsHeader.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsHeader.tsx index 85279662419..db8c958dd67 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsHeader.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsHeader.tsx @@ -1,12 +1,12 @@ import React from 'react' import { Flex, flexStyles, Text, TouchableArea } from 'ui/src' import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' +import WarningIcon from 'uniswap/src/components/icons/WarningIcon' import { SafetyLevel, TokenDetailsScreenQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import WarningIcon from 'wallet/src/components/icons/WarningIcon' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' export interface TokenDetailsHeaderProps { data?: TokenDetailsScreenQuery @@ -31,23 +31,14 @@ export function TokenDetailsHeader({ url={tokenProject?.logoUrl ?? undefined} /> - + {tokenProject?.name ?? '—'} {/* Suppress warning icon on low warning level */} {(tokenProject?.safetyLevel === SafetyLevel.StrongWarning || tokenProject?.safetyLevel === SafetyLevel.Blocked) && ( - + )} diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsLinks.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsLinks.tsx index d54f40ec787..9664fb54908 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsLinks.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsLinks.tsx @@ -10,11 +10,7 @@ import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { TokenDetailsScreenQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { ElementName } from 'uniswap/src/features/telemetry/constants' import { UniverseChainId } from 'uniswap/src/types/chains' -import { - currencyIdToAddress, - currencyIdToChain, - isDefaultNativeAddress, -} from 'wallet/src/utils/currencyId' +import { currencyIdToAddress, currencyIdToChain, isDefaultNativeAddress } from 'wallet/src/utils/currencyId' import { ExplorerDataType, getExplorerLink, getTwitterLink } from 'wallet/src/utils/linking' export function TokenDetailsLinks({ @@ -69,7 +65,7 @@ export function TokenDetailsLinks({ )} diff --git a/apps/mobile/src/components/TokenDetails/TokenDetailsStats.tsx b/apps/mobile/src/components/TokenDetails/TokenDetailsStats.tsx index fbd43c3cfa7..51e4f1ba391 100644 --- a/apps/mobile/src/components/TokenDetails/TokenDetailsStats.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenDetailsStats.tsx @@ -3,13 +3,7 @@ import { useTranslation } from 'react-i18next' import Animated, { FadeIn, FadeOut } from 'react-native-reanimated' import { LongText } from 'src/components/text/LongText' import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src' -import { - ChartBar, - ChartPie, - Language as LanguageIcon, - TrendDown, - TrendUp, -} from 'ui/src/components/icons' +import { ChartBar, ChartPie, Language as LanguageIcon, TrendDown, TrendUp } from 'ui/src/components/icons' import { iconSizes } from 'ui/src/theme' import { TokenDetailsScreenQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { NumberType } from 'utilities/src/format/types' @@ -65,35 +59,40 @@ export function TokenDetailsMarketData({ }> + statsIcon={} + > {convertFiatAmountFormatted(marketCap, NumberType.FiatTokenStats)} }> + statsIcon={} + > {convertFiatAmountFormatted(fullyDilutedValuation, NumberType.FiatTokenStats)} }> + statsIcon={} + > {convertFiatAmountFormatted(volume, NumberType.FiatTokenStats)} }> + statsIcon={} + > {convertFiatAmountFormatted(priceHight52W, NumberType.FiatTokenDetails)} }> + statsIcon={} + > {convertFiatAmountFormatted(priceLow52W, NumberType.FiatTokenDetails)} @@ -130,13 +129,10 @@ export function TokenDetailsStats({ const name = offChainData?.name ?? onChainData?.name const marketCap = offChainData?.markets?.[0]?.marketCap?.value const volume = onChainData?.market?.volume?.value - const priceHight52W = - offChainData?.markets?.[0]?.priceHigh52W?.value ?? onChainData?.market?.priceHigh52W?.value - const priceLow52W = - offChainData?.markets?.[0]?.priceLow52W?.value ?? onChainData?.market?.priceLow52W?.value + const priceHight52W = offChainData?.markets?.[0]?.priceHigh52W?.value ?? onChainData?.market?.priceHigh52W?.value + const priceLow52W = offChainData?.markets?.[0]?.priceLow52W?.value ?? onChainData?.market?.priceLow52W?.value const fullyDilutedValuation = offChainData?.markets?.[0]?.fullyDilutedValuation?.value - const currentDescription = - showTranslation && translatedDescription ? translatedDescription : description + const currentDescription = showTranslation && translatedDescription ? translatedDescription : description return ( @@ -157,14 +153,8 @@ export function TokenDetailsStats({ /> {currentLanguage !== Language.English && !!translatedDescription && ( - setShowTranslation(!showTranslation)}> - + setShowTranslation(!showTranslation)}> + {showTranslation ? ( diff --git a/apps/mobile/src/components/TokenDetails/hooks.ts b/apps/mobile/src/components/TokenDetails/hooks.ts index 64a465eab18..17347681a22 100644 --- a/apps/mobile/src/components/TokenDetails/hooks.ts +++ b/apps/mobile/src/components/TokenDetails/hooks.ts @@ -5,21 +5,17 @@ import { Chain, useTokenDetailsScreenLazyQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { CurrencyId } from 'uniswap/src/types/currency' import { MobileScreens } from 'uniswap/src/types/screens/mobile' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils' -import { - buildCurrencyId, - buildNativeCurrencyId, - currencyIdToChain, -} from 'wallet/src/utils/currencyId' +import { buildCurrencyId, buildNativeCurrencyId, currencyIdToChain } from 'wallet/src/utils/currencyId' /** Helper hook to retrieve balances across chains for a given currency, for the active account. */ export function useCrossChainBalances( currencyId: string, - bridgeInfo: Maybe<{ chain: Chain; address?: Maybe }[]> + bridgeInfo: Maybe<{ chain: Chain; address?: Maybe }[]>, ): { currentChainBalance: PortfolioBalance | null otherChainBalances: PortfolioBalance[] | null @@ -42,7 +38,7 @@ export function useCrossChainBalances( }) .filter((b): b is string => !!b), - [bridgeInfo, currentChainId] + [bridgeInfo, currentChainId], ) const otherChainBalances = useBalances(bridgedCurrencyIds) @@ -67,7 +63,7 @@ export function useTokenDetailsNavigation(): { variables: currencyIdToContractInput(currencyId), }) }, - [load] + [load], ) // the desired behavior is to push the new token details screen onto the stack instead of replacing it @@ -82,18 +78,15 @@ export function useTokenDetailsNavigation(): { } navigation.push(MobileScreens.TokenDetails, { currencyId }) }, - [navigation] + [navigation], ) const navigate = useCallback( (currencyId: CurrencyId): void => { navigation.navigate(MobileScreens.TokenDetails, { currencyId }) }, - [navigation] + [navigation], ) - return useMemo( - () => ({ preload, navigate, navigateWithPop }), - [navigate, navigateWithPop, preload] - ) + return useMemo(() => ({ preload, navigate, navigateWithPop }), [navigate, navigateWithPop, preload]) } diff --git a/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx b/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx index 634d57b364c..b7f196bbb98 100644 --- a/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx +++ b/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx @@ -31,7 +31,7 @@ function TokenOptionItemWrapper({ // we need to convert to TokenOption without quantity and balanceUSD // to use in Token Selector () => (currencyInfo ? { currencyInfo, quantity: 0, balanceUSD: 0 } : null), - [currencyInfo] + [currencyInfo], ) const onPress = useCallback(() => onSelectCurrency?.(currency), [currency, onSelectCurrency]) @@ -49,13 +49,7 @@ function TokenOptionItemWrapper({ ) } -function _TokenFiatOnRampList({ - onSelectCurrency, - error, - onRetry, - list, - loading, -}: Props): JSX.Element { +function _TokenFiatOnRampList({ onSelectCurrency, error, onRetry, list, loading }: Props): JSX.Element { const { t } = useTranslation() const flatListRef = useRef(null) @@ -64,7 +58,7 @@ function _TokenFiatOnRampList({ ({ item: currency }: ListRenderItemInfo) => { return }, - [onSelectCurrency] + [onSelectCurrency], ) if (error) { diff --git a/apps/mobile/src/components/Trace/TraceUserProperties.test.tsx b/apps/mobile/src/components/Trace/TraceUserProperties.test.tsx index 4b25bad611a..ba99f79913a 100644 --- a/apps/mobile/src/components/Trace/TraceUserProperties.test.tsx +++ b/apps/mobile/src/components/Trace/TraceUserProperties.test.tsx @@ -101,50 +101,26 @@ describe('TraceUserProperties', () => { // Check setUserProperty calls with correct values expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.AppVersion, '1.0.0.345', undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.DarkMode, true, undefined) - expect(mocked).toHaveBeenCalledWith( - MobileUserPropertyName.ActiveWalletAddress, - 'address', - undefined - ) - expect(mocked).toHaveBeenCalledWith( - MobileUserPropertyName.ActiveWalletType, - AccountType.SignerMnemonic, - undefined - ) + expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.ActiveWalletAddress, 'address', undefined) + expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.ActiveWalletType, AccountType.SignerMnemonic, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsCloudBackedUp, true, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsPushEnabled, true, undefined) - expect(mocked).toHaveBeenCalledWith( - MobileUserPropertyName.IsHideSmallBalancesEnabled, - false, - undefined - ) - expect(mocked).toHaveBeenCalledWith( - MobileUserPropertyName.IsHideSpamTokensEnabled, - true, - undefined - ) + expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsHideSmallBalancesEnabled, false, undefined) + expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.IsHideSpamTokensEnabled, true, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.WalletViewOnlyCount, 2, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.WalletSignerCount, 3, undefined) expect(mocked).toHaveBeenCalledWith( MobileUserPropertyName.WalletSignerAccounts, [address1, address2, address3], - undefined + undefined, ) expect(mocked).toHaveBeenCalledWith( MobileUserPropertyName.WalletSwapProtectionSetting, SwapProtectionSetting.On, - undefined - ) - expect(mocked).toHaveBeenCalledWith( - MobileUserPropertyName.AppOpenAuthMethod, - AuthMethod.FaceId, - undefined - ) - expect(mocked).toHaveBeenCalledWith( - MobileUserPropertyName.TransactionAuthMethod, - AuthMethod.FaceId, - undefined + undefined, ) + expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.AppOpenAuthMethod, AuthMethod.FaceId, undefined) + expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.TransactionAuthMethod, AuthMethod.FaceId, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.Language, 'English', undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.Currency, 'USD', undefined) @@ -185,16 +161,8 @@ describe('TraceUserProperties', () => { expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.DarkMode, true, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.WalletViewOnlyCount, 0, undefined) expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.WalletSignerCount, 0, undefined) - expect(mocked).toHaveBeenCalledWith( - MobileUserPropertyName.AppOpenAuthMethod, - AuthMethod.None, - undefined - ) - expect(mocked).toHaveBeenCalledWith( - MobileUserPropertyName.TransactionAuthMethod, - AuthMethod.None, - undefined - ) + expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.AppOpenAuthMethod, AuthMethod.None, undefined) + expect(mocked).toHaveBeenCalledWith(MobileUserPropertyName.TransactionAuthMethod, AuthMethod.None, undefined) expect(mocked).toHaveBeenCalledTimes(11) }) diff --git a/apps/mobile/src/components/Trace/TraceUserProperties.tsx b/apps/mobile/src/components/Trace/TraceUserProperties.tsx index 91d7de86c83..e1e3eb91999 100644 --- a/apps/mobile/src/components/Trace/TraceUserProperties.tsx +++ b/apps/mobile/src/components/Trace/TraceUserProperties.tsx @@ -1,16 +1,13 @@ import { useEffect } from 'react' import { NativeModules } from 'react-native' import { useAppSelector } from 'src/app/hooks' -import { - useBiometricAppSettings, - useDeviceSupportsBiometricAuth, -} from 'src/features/biometrics/hooks' -import { selectAllowAnalytics } from 'src/features/telemetry/selectors' +import { useBiometricAppSettings, useDeviceSupportsBiometricAuth } from 'src/features/biometrics/hooks' import { getAuthMethod } from 'src/features/telemetry/utils' import { getFullAppVersion } from 'src/utils/version' import { useIsDarkMode } from 'ui/src' import { MobileUserPropertyName, setUserProperty } from 'uniswap/src/features/telemetry/user' import { isAndroid } from 'utilities/src/platform' +import { selectAllowAnalytics } from 'wallet/src/features/telemetry/selectors' // eslint-disable-next-line no-restricted-imports import { analytics } from 'utilities/src/telemetry/analytics/analytics' import { useAppFiatCurrency } from 'wallet/src/features/fiatCurrency/hooks' @@ -75,7 +72,7 @@ export function TraceUserProperties(): null { setUserProperty(MobileUserPropertyName.WalletSignerCount, signerAccounts.length) setUserProperty( MobileUserPropertyName.WalletSignerAccounts, - signerAccounts.map((account) => account.address) + signerAccounts.map((account) => account.address), ) }, [allowAnalytics, signerAccounts]) @@ -89,14 +86,8 @@ export function TraceUserProperties(): null { } setUserProperty(MobileUserPropertyName.ActiveWalletAddress, activeAccount.address) setUserProperty(MobileUserPropertyName.ActiveWalletType, activeAccount.type) - setUserProperty( - MobileUserPropertyName.IsCloudBackedUp, - Boolean(activeAccount.backups?.includes(BackupType.Cloud)) - ) - setUserProperty( - MobileUserPropertyName.IsPushEnabled, - Boolean(activeAccount.pushNotificationsEnabled) - ) + setUserProperty(MobileUserPropertyName.IsCloudBackedUp, Boolean(activeAccount.backups?.includes(BackupType.Cloud))) + setUserProperty(MobileUserPropertyName.IsPushEnabled, Boolean(activeAccount.pushNotificationsEnabled)) setUserProperty(MobileUserPropertyName.IsHideSmallBalancesEnabled, hideSmallBalances) setUserProperty(MobileUserPropertyName.IsHideSpamTokensEnabled, hideSpamTokens) @@ -105,11 +96,11 @@ export function TraceUserProperties(): null { useEffect(() => { setUserProperty( MobileUserPropertyName.AppOpenAuthMethod, - getAuthMethod(biometricsAppSettingsState.requiredForAppAccess, touchId, faceId) + getAuthMethod(biometricsAppSettingsState.requiredForAppAccess, touchId, faceId), ) setUserProperty( MobileUserPropertyName.TransactionAuthMethod, - getAuthMethod(biometricsAppSettingsState.requiredForTransactions, touchId, faceId) + getAuthMethod(biometricsAppSettingsState.requiredForTransactions, touchId, faceId), ) }, [allowAnalytics, biometricsAppSettingsState, touchId, faceId]) diff --git a/apps/mobile/src/components/WalletConnect/ConnectedDapps/ConnectedDappsList.tsx b/apps/mobile/src/components/WalletConnect/ConnectedDapps/ConnectedDappsList.tsx index 5875d371855..d0259062591 100644 --- a/apps/mobile/src/components/WalletConnect/ConnectedDapps/ConnectedDappsList.tsx +++ b/apps/mobile/src/components/WalletConnect/ConnectedDapps/ConnectedDappsList.tsx @@ -7,10 +7,7 @@ import { DappConnectedNetworkModal } from 'src/components/WalletConnect/Connecte import { DappConnectionItem } from 'src/components/WalletConnect/ConnectedDapps/DappConnectionItem' import { BackButton } from 'src/components/buttons/BackButton' import { openModal } from 'src/features/modals/modalSlice' -import { - WalletConnectSession, - removePendingSession, -} from 'src/features/walletConnect/walletConnectSlice' +import { WalletConnectSession, removePendingSession } from 'src/features/walletConnect/walletConnectSlice' import { Flex, Text, TouchableArea } from 'ui/src' import { Edit, Scan } from 'ui/src/components/icons' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' @@ -34,20 +31,13 @@ export function ConnectedDappsList({ backButton, sessions }: ConnectedDappsProps const onPressScan = useCallback(() => { // in case we received a pending session from a previous scan after closing modal dispatch(removePendingSession()) - dispatch( - openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.ScanQr }) - ) + dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.ScanQr })) }, [dispatch]) return ( <> - + {backButton ?? } @@ -61,12 +51,9 @@ export function ConnectedDappsList({ backButton, sessions }: ConnectedDappsProps { setIsEditing(!isEditing) - }}> - {isEditing ? ( - - ) : ( - - )} + }} + > + {isEditing ? : } ) : ( @@ -102,7 +89,8 @@ export function ConnectedDappsList({ backButton, sessions }: ConnectedDappsProps px="$spacing24" style={{ paddingTop: fullHeight / 5, - }}> + }} + > {t('walletConnect.dapps.manage.empty.title')} @@ -113,10 +101,7 @@ export function ConnectedDappsList({ backButton, sessions }: ConnectedDappsProps )} {selectedSession && ( - setSelectedSession(undefined)} - /> + setSelectedSession(undefined)} /> )} ) diff --git a/apps/mobile/src/components/WalletConnect/ConnectedDapps/DappConnectedNetworksModal.tsx b/apps/mobile/src/components/WalletConnect/ConnectedDapps/DappConnectedNetworksModal.tsx index bd472393b1d..0409dd49391 100644 --- a/apps/mobile/src/components/WalletConnect/ConnectedDapps/DappConnectedNetworksModal.tsx +++ b/apps/mobile/src/components/WalletConnect/ConnectedDapps/DappConnectedNetworksModal.tsx @@ -23,10 +23,7 @@ interface DappConnectedNetworkModalProps { onClose: () => void } -export function DappConnectedNetworkModal({ - session, - onClose, -}: DappConnectedNetworkModalProps): JSX.Element { +export function DappConnectedNetworkModal({ session, onClose }: DappConnectedNetworkModalProps): JSX.Element { const { t } = useTranslation() const address = useActiveAccountAddressWithThrow() const dispatch = useAppDispatch() @@ -52,7 +49,7 @@ export function DappConnectedNetworkModal({ event: WalletConnectEvent.Disconnected, imageUrl: dapp.icon, hideDelay: 3 * ONE_SECOND_MS, - }) + }), ) onClose() } catch (error) { @@ -77,13 +74,7 @@ export function DappConnectedNetworkModal({ - + {session.chains.map((chainId) => ( diff --git a/apps/mobile/src/components/WalletConnect/ConnectedDapps/DappConnectionItem.tsx b/apps/mobile/src/components/WalletConnect/ConnectedDapps/DappConnectionItem.tsx index 46ed0509828..85dcf19a9c6 100644 --- a/apps/mobile/src/components/WalletConnect/ConnectedDapps/DappConnectionItem.tsx +++ b/apps/mobile/src/components/WalletConnect/ConnectedDapps/DappConnectionItem.tsx @@ -55,16 +55,14 @@ export function DappConnectionItem({ event: WalletConnectEvent.Disconnected, imageUrl: dapp.icon, hideDelay: 3 * ONE_SECOND_MS, - }) + }), ) } catch (error) { logger.error(error, { tags: { file: 'DappConnectionItem', function: 'onDisconnect' } }) } } - const menuActions = [ - { title: t('common.button.disconnect'), systemIcon: 'trash', destructive: true }, - ] + const menuActions = [{ title: t('common.button.disconnect'), systemIcon: 'trash', destructive: true }] const onPress = async (e: NativeSyntheticEvent): Promise => { if (e.nativeEvent.index === 0) { @@ -83,13 +81,15 @@ export function DappConnectionItem({ mb="$spacing12" pb="$spacing12" pt="$spacing24" - px="$spacing12"> + px="$spacing12" + > + zIndex="$tooltip" + > {isEditing ? ( + onPress={onDisconnect} + > ) : ( @@ -125,7 +126,8 @@ export function DappConnectionItem({ hapticStyle={ImpactFeedbackStyle.Medium} testID={ElementName.WCDappNetworks} onLongPress={disableOnPress} - onPress={(): void => onPressChangeNetwork(session)}> + onPress={(): void => onPressChangeNetwork(session)} + > -const isCloseToBottom = ({ - layoutMeasurement, - contentOffset, - contentSize, -}: NativeScrollEvent): boolean => { +const isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }: NativeScrollEvent): boolean => { return layoutMeasurement.height + contentOffset.y >= contentSize.height - spacing.spacing24 } @@ -72,7 +64,7 @@ export function ModalWithOverlay({ setConfirmationEnabled(true) } }, - [showOverlay] + [showOverlay], ) const handleScrollDown = useCallback(() => { @@ -111,7 +103,7 @@ export function ModalWithOverlay({ measureContent(parentHeight) }, MEASURE_LAYOUT_TIMEOUT) }, - [measureContent] + [measureContent], ) return ( @@ -121,12 +113,13 @@ export function ModalWithOverlay({ contentContainerStyle={ contentContainerStyle ?? { paddingHorizontal: spacing.spacing24, - paddingTop: spacing.spacing36, + paddingTop: spacing.spacing12, } } showsVerticalScrollIndicator={false} onLayout={handleScrollViewLayout} - onScroll={handleScroll}> + onScroll={handleScroll} + > {children} @@ -173,16 +166,13 @@ function ModalFooter({ () => Math.max(0, animatedContainerHeight.value - animatedPosition.value) - animatedFooterHeight.value - - animatedHandleHeight.value + animatedHandleHeight.value, ) return ( {showScrollDownOverlay && ( - + )} + px="$spacing24" + > - diff --git a/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay.tsx b/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay.tsx index 00b154b6d20..11d0bb05e52 100644 --- a/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay.tsx +++ b/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay.tsx @@ -13,10 +13,7 @@ type ScrollDownOverlayProps = { onScrollDownPress: () => void } -export function ScrollDownOverlay({ - onScrollDownPress, - scrollDownButonText, -}: ScrollDownOverlayProps): JSX.Element { +export function ScrollDownOverlay({ onScrollDownPress, scrollDownButonText }: ScrollDownOverlayProps): JSX.Element { const { t } = useTranslation() const { fullHeight, fullWidth } = useDeviceDimensions() const colors = useSporeColors() @@ -32,7 +29,8 @@ export function ScrollDownOverlay({ pb="$spacing24" pointerEvents="box-none" position="absolute" - width="100%"> + width="100%" + > diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/ClientDetails.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/ClientDetails.tsx index 6ac9f8bdf41..61a947265cc 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/ClientDetails.tsx +++ b/apps/mobile/src/components/WalletConnect/RequestModal/ClientDetails.tsx @@ -5,6 +5,7 @@ import { HeaderText } from 'src/components/WalletConnect/RequestModal/HeaderText import { WalletConnectRequest } from 'src/features/walletConnect/walletConnectSlice' import { Flex, useSporeColors } from 'ui/src' import { iconSizes } from 'ui/src/theme' +import { formatDappURL } from 'utilities/src/format/urls' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' export interface PermitInfo { @@ -25,28 +26,21 @@ export function ClientDetails({ const permitCurrencyInfo = useCurrencyInfo(permitInfo?.currencyId) return ( - + - - - - + + ) } diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/HeaderText.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/HeaderText.tsx index e1be6d6c140..fb50999b54e 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/HeaderText.tsx +++ b/apps/mobile/src/components/WalletConnect/RequestModal/HeaderText.tsx @@ -54,10 +54,7 @@ export function HeaderText({ ) } - const getReadableMethodName = ( - ethMethod: EthMethod | UwULinkMethod, - dappNameOrUrl: string - ): JSX.Element => { + const getReadableMethodName = (ethMethod: EthMethod | UwULinkMethod, dappNameOrUrl: string): JSX.Element => { switch (ethMethod) { case EthMethod.PersonalSign: case EthMethod.EthSign: diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/KidSuperCheckinModal.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/KidSuperCheckinModal.tsx index cd3cf9b8994..0e4b2ccef7d 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/KidSuperCheckinModal.tsx +++ b/apps/mobile/src/components/WalletConnect/RequestModal/KidSuperCheckinModal.tsx @@ -17,12 +17,7 @@ type Props = { request: SignRequest } -export function KidSuperCheckinModal({ - onClose, - onConfirm, - onReject, - request, -}: Props): JSX.Element { +export function KidSuperCheckinModal({ onClose, onConfirm, onReject, request }: Props): JSX.Element { const { t } = useTranslation() return ( @@ -36,7 +31,8 @@ export function KidSuperCheckinModal({ scrollDownButtonText={t('walletConnect.request.button.scrollDown')} onClose={onClose} onConfirm={onConfirm} - onReject={onReject}> + onReject={onReject} + > ) @@ -45,9 +41,7 @@ export function KidSuperCheckinModal({ function useUniswapCafeLogo(): string | undefined { const isDarkMode = useIsDarkMode() const uwuLinkContractAllowlist = useUwuLinkContractAllowlist() - const logos = uwuLinkContractAllowlist.tokenRecipients.find( - (recipient) => recipient.name === 'Uniswap Cafe' - )?.logo + const logos = uwuLinkContractAllowlist.tokenRecipients.find((recipient) => recipient.name === 'Uniswap Cafe')?.logo if (!logos) { return @@ -75,7 +69,8 @@ function KidSuperCheckinModalContent({ request }: { request: SignRequest }): JSX borderWidth={1} gap="$spacing12" px="$spacing24" - py="$spacing24"> + py="$spacing24" + > diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/RequestDetails.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/RequestDetails.tsx index 1e76f4a8147..78a2fb656ae 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/RequestDetails.tsx +++ b/apps/mobile/src/components/WalletConnect/RequestModal/RequestDetails.tsx @@ -3,22 +3,19 @@ import { Transaction, TransactionDescription } from 'no-yolo-signatures' import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { ScrollView } from 'react-native-gesture-handler' -import { SpendingDetails } from 'src/components/WalletConnect/RequestModal/SpendingDetails' import { LinkButton } from 'src/components/buttons/LinkButton' -import { - SignRequest, - WalletConnectRequest, - isTransactionRequest, -} from 'src/features/walletConnect/walletConnectSlice' +import { SignRequest, WalletConnectRequest, isTransactionRequest } from 'src/features/walletConnect/walletConnectSlice' import { useNoYoloParser } from 'src/utils/useNoYoloParser' import { Flex, Text, useSporeColors } from 'ui/src' import { TextVariantTokens, iconSizes } from 'ui/src/theme' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' import { EthMethod, EthTransaction } from 'uniswap/src/types/walletConnect' +import { getValidAddress, shortenAddress } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' import { useENS } from 'wallet/src/features/ens/useENS' -import { getValidAddress, shortenAddress } from 'wallet/src/utils/addresses' +import { ContentRow } from 'wallet/src/features/transactions/TransactionRequest/ContentRow' +import { SpendingDetails } from 'wallet/src/features/transactions/TransactionRequest/SpendingDetails' import { ExplorerDataType, getExplorerLink } from 'wallet/src/utils/linking' const getStrMessage = (request: WalletConnectRequest): string => { @@ -42,14 +39,10 @@ const AddressButton = ({ address, chainId, ...rest }: AddressButtonProps): JSX.E return ( @@ -83,12 +76,7 @@ const getParsedObjectDisplay = (chainId: number, obj: any, depth = 0): JSX.Eleme if (typeof childValue === 'string') { return ( - + {objKey} @@ -157,31 +145,25 @@ function TransactionDetails({ return ( - {value && !BigNumber.from(value).eq(0) ? ( - - ) : null} + {value && !BigNumber.from(value).eq(0) ? : null} {to ? ( - - - {t('walletConnect.request.details.label.recipient')} - + - + ) : null} - - - {t('walletConnect.request.details.label.function')} - + - + py="$spacing2" + > + {parsedData ? parsedData.name : t('common.text.unknown')} - + ) } @@ -212,11 +194,9 @@ export function RequestDetailsContent({ request }: Props): JSX.Element { } const message = getStrMessage(request) - return message ? ( - {message} - ) : ( - - {t('qrScanner.request.message.unavailable')} + return ( + + {message || t('qrScanner.request.message.unavailable')} ) } diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/UwULinkErc20SendModal.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/UwULinkErc20SendModal.tsx index 4f59e8da88e..d25fc089c3c 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/UwULinkErc20SendModal.tsx +++ b/apps/mobile/src/components/WalletConnect/RequestModal/UwULinkErc20SendModal.tsx @@ -10,6 +10,7 @@ import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { ModalName } from 'uniswap/src/features/telemetry/constants' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { NumberType } from 'utilities/src/format/types' import { NetworkFee } from 'wallet/src/components/network/NetworkFee' import { GasFeeResult } from 'wallet/src/features/gas/types' @@ -19,7 +20,6 @@ import { useOnChainCurrencyBalance } from 'wallet/src/features/portfolio/api' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' type Props = { onClose: () => void @@ -27,6 +27,7 @@ type Props = { onReject: () => void request: UwuLinkErc20Request hasSufficientGasFunds: boolean + confirmEnabled: boolean gasFee: GasFeeResult } @@ -36,6 +37,7 @@ export function UwULinkErc20SendModal({ onConfirm, onReject, request, + confirmEnabled, hasSufficientGasFunds, }: Props): JSX.Element { const { t } = useTranslation() @@ -54,12 +56,13 @@ export function UwULinkErc20SendModal({ paddingHorizontal: spacing.spacing24, paddingTop: spacing.spacing8, }} - disableConfirm={!hasSufficientTokenFunds || !hasSufficientGasFunds} + disableConfirm={!confirmEnabled || !hasSufficientTokenFunds || !hasSufficientGasFunds} name={ModalName.UwULinkErc20SendModal} scrollDownButtonText={t('walletConnect.request.button.scrollDown')} onClose={onClose} onConfirm={onConfirm} - onReject={onReject}> + onReject={onReject} + > - + {formatUnits(request.amount, decimals)} {symbol} diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModal.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModal.tsx index ab62f064966..07a5ca086ab 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModal.tsx +++ b/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModal.tsx @@ -17,23 +17,16 @@ import { returnToPreviousApp } from 'src/features/walletConnect/WalletConnect' import { wcWeb3Wallet } from 'src/features/walletConnect/saga' import { selectDidOpenFromDeepLink } from 'src/features/walletConnect/selectors' import { signWcRequestActions } from 'src/features/walletConnect/signWcRequestSaga' -import { - WalletConnectRequest, - isTransactionRequest, -} from 'src/features/walletConnect/walletConnectSlice' +import { WalletConnectRequest, isTransactionRequest } from 'src/features/walletConnect/walletConnectSlice' import { MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' -import { - EthMethod, - UwULinkMethod, - WCEventType, - WCRequestOutcome, -} from 'uniswap/src/types/walletConnect' +import { EthMethod, UwULinkMethod, WCEventType, WCRequestOutcome } from 'uniswap/src/types/walletConnect' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' +import { formatExternalTxnWithGasEstimates } from 'wallet/src/features/gas/formatExternalTxnWithGasEstimates' import { useTransactionGasFee } from 'wallet/src/features/gas/hooks' import { GasSpeed } from 'wallet/src/features/gas/types' import { useIsBlocked, useIsBlockedActiveAddress } from 'wallet/src/features/trm/hooks' import { useSignerAccounts } from 'wallet/src/features/wallet/hooks' -import { areAddressesEqual } from 'wallet/src/utils/addresses' interface Props { onClose: () => void @@ -64,9 +57,7 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem }, [chainId, request]) const signerAccounts = useSignerAccounts() - const signerAccount = signerAccounts.find((account) => - areAddressesEqual(account.address, request.account) - ) + const signerAccount = signerAccounts.find((account) => areAddressesEqual(account.address, request.account)) const gasFee = useTransactionGasFee(tx, GasSpeed.Urgent) const hasSufficientFunds = useHasSufficientFunds({ @@ -76,10 +67,8 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem value: isTransactionRequest(request) ? request.transaction.value : undefined, }) - const { isBlocked: isSenderBlocked, isBlockedLoading: isSenderBlockedLoading } = - useIsBlockedActiveAddress() - const { isBlocked: isRecipientBlocked, isBlockedLoading: isRecipientBlockedLoading } = - useIsBlocked(tx?.to) + const { isBlocked: isSenderBlocked, isBlockedLoading: isSenderBlockedLoading } = useIsBlockedActiveAddress() + const { isBlocked: isRecipientBlocked, isBlockedLoading: isRecipientBlockedLoading } = useIsBlocked(tx?.to) const isBlocked = isSenderBlocked ?? isRecipientBlocked const isBlockedLoading = isSenderBlockedLoading || isRecipientBlockedLoading @@ -98,7 +87,7 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem } if (methodCostsGas(request)) { - return !!(tx && hasSufficientFunds && gasFee.value) + return !!(tx && hasSufficientFunds && gasFee.value && !gasFee.error && !gasFee.loading) } if (isTransactionRequest(request)) { @@ -131,9 +120,7 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem rejectOnCloseRef.current = false sendAnalyticsEvent(MobileEventName.WalletConnectSheetCompleted, { - request_type: isTransactionRequest(request) - ? WCEventType.TransactionRequest - : WCEventType.SignRequest, + request_type: isTransactionRequest(request) ? WCEventType.TransactionRequest : WCEventType.SignRequest, eth_method: request.type, dapp_url: request.dapp.url, dapp_name: request.dapp.name, @@ -149,24 +136,27 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem } const onConfirm = async (): Promise => { - if (!confirmEnabled || !signerAccount) { + if (!confirmEnabled || !signerAccount || !tx) { return } + if (request.type === EthMethod.EthSendTransaction || request.type === UwULinkMethod.Erc20Send) { - if (!gasFee.params) { - return - } // appeasing typescript + const txnWithFormattedGasEstimates = formatExternalTxnWithGasEstimates({ + transaction: tx, + gasFeeResult: gasFee, + }) + dispatch( signWcRequestActions.trigger({ sessionId: request.sessionId, requestInternalId: request.internalId, method: EthMethod.EthSendTransaction, - transaction: { ...tx, ...gasFee.params }, + transaction: txnWithFormattedGasEstimates, account: signerAccount, dapp: request.dapp, chainId, request, - }) + }), ) } else { dispatch( @@ -179,16 +169,14 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem account: signerAccount, dapp: request.dapp, chainId, - }) + }), ) } rejectOnCloseRef.current = false sendAnalyticsEvent(MobileEventName.WalletConnectSheetCompleted, { - request_type: isTransactionRequest(request) - ? WCEventType.TransactionRequest - : WCEventType.SignRequest, + request_type: isTransactionRequest(request) ? WCEventType.TransactionRequest : WCEventType.SignRequest, eth_method: request.type, dapp_url: request.dapp.url, dapp_name: request.dapp.name, @@ -229,6 +217,7 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem if (request.type === UwULinkMethod.Erc20Send) { return ( + ) } return ( + onReject={onReject} + > - request.type !== EthMethod.PersonalSign +const isPotentiallyUnsafe = (request: WalletConnectRequest): boolean => request.type !== EthMethod.PersonalSign export const methodCostsGas = (request: WalletConnectRequest): request is TransactionRequest => request.type === EthMethod.EthSendTransaction @@ -86,64 +85,41 @@ export function WalletConnectRequestModalContent({ height: animatedFooterHeight.value, })) + const hasGasFee = methodCostsGas(request) + const gasFeeUSD = useUSDValue(chainId, gasFee?.value) + return ( <> - - + + {!permitInfo && ( - - - + )} - - {methodCostsGas(request) ? ( - - ) : ( - - - {t('walletConnect.request.label.network')} - - - - )} - + + + + + + + {!hasSufficientFunds && ( - - {!hasSufficientFunds && ( - - {t('walletConnect.request.error.insufficientFunds', { - currencySymbol: nativeCurrency?.symbol, - })} - - )} + + {t('walletConnect.request.error.insufficientFunds', { + currencySymbol: nativeCurrency?.symbol, + })} + - + )} {!netInfo.isInternetReachable ? ( + } textColor="$DEP_accentWarning" title={t('walletConnect.request.error.network')} @@ -192,20 +168,18 @@ function WarningSection({ return } - return ( - - - - {isTransactionRequest(request) - ? t('walletConnect.request.warning.general.transaction') - : t('walletConnect.request.warning.general.message')} - - - ) + if (!isTransactionRequest(request)) { + return ( + + + + {t('walletConnect.request.warning.general.message')} + + + ) + } + + return null } const requestMessageStyle: StyleProp = { diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/hooks.ts b/apps/mobile/src/components/WalletConnect/RequestModal/hooks.ts index d5b3806c7a5..f741c31bbde 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/hooks.ts +++ b/apps/mobile/src/components/WalletConnect/RequestModal/hooks.ts @@ -18,10 +18,7 @@ export function useHasSufficientFunds({ value?: string }): boolean { const nativeCurrency = NativeCurrency.onChain(chainId || UniverseChainId.Mainnet) - const { balance: nativeBalance } = useOnChainNativeCurrencyBalance( - chainId ?? UniverseChainId.Mainnet, - account - ) + const { balance: nativeBalance } = useOnChainNativeCurrencyBalance(chainId ?? UniverseChainId.Mainnet, account) const hasSufficientFunds = useMemo(() => { const transactionAmount = diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionModal.tsx b/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionModal.tsx index b9cbc140c10..bfd6ffaa114 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionModal.tsx +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionModal.tsx @@ -19,17 +19,18 @@ import { removePendingSession, } from 'src/features/walletConnect/walletConnectSlice' import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src' -import { Check, X } from 'ui/src/components/icons' +import { Check, RotatableChevron, X } from 'ui/src/components/icons' import { iconSizes } from 'ui/src/theme' import { ElementName, MobileEventName, ModalName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { WalletChainId } from 'uniswap/src/types/chains' import { WCEventType, WCRequestOutcome, WalletConnectEvent } from 'uniswap/src/types/walletConnect' +import { formatDappURL } from 'utilities/src/format/urls' import { ONE_SECOND_MS } from 'utilities/src/time/time' -import { AccountDetails } from 'wallet/src/components/accounts/AccountDetails' import { NetworkLogos } from 'wallet/src/components/network/NetworkLogos' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' +import { AddressFooter } from 'wallet/src/features/transactions/TransactionRequest/AddressFooter' import { useActiveAccountAddressWithThrow, useActiveAccountWithThrow, @@ -55,18 +56,15 @@ const SitePermissions = (): JSX.Element => { return ( + p="$spacing12" + > - + {t('walletConnect.permissions.title')} @@ -78,7 +76,8 @@ const SitePermissions = (): JSX.Element => { allowFontScaling={false} color="$neutral1" flexGrow={1} - variant={infoTextSize}> + variant={infoTextSize} + > {t('walletConnect.permissions.option.viewWalletAddress')} @@ -89,7 +88,8 @@ const SitePermissions = (): JSX.Element => { allowFontScaling={false} color="$neutral1" flexGrow={1} - variant={infoTextSize}> + variant={infoTextSize} + > {t('walletConnect.permissions.option.viewTokenBalances')} @@ -100,7 +100,8 @@ const SitePermissions = (): JSX.Element => { allowFontScaling={false} color="$neutral1" flexGrow={1} - variant={infoTextSize}> + variant={infoTextSize} + > {t('walletConnect.permissions.option.transferAssets')} @@ -113,25 +114,11 @@ const NetworksRow = ({ chains }: { chains: WalletChainId[] }): JSX.Element => { const { t } = useTranslation() return ( - - + + {t('walletConnect.permissions.networks')} - + ) } @@ -150,24 +137,11 @@ const SwitchAccountRow = ({ activeAddress, setModalState }: SwitchAccountProps): }, [setModalState]) return ( - - + + + + {accountIsSwitchable && } + ) } @@ -180,9 +154,7 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. const activeAccount = useActiveAccountWithThrow() const didOpenFromDeepLink = useAppSelector(selectDidOpenFromDeepLink) - const [modalState, setModalState] = useState( - PendingConnectionModalState.Hidden - ) + const [modalState, setModalState] = useState(PendingConnectionModalState.Hidden) const onPressSettleConnection = useCallback( async (approved: boolean) => { @@ -218,7 +190,7 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. namespaces, }, account: activeAddress, - }) + }), ) dispatch( @@ -229,7 +201,7 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. dappName: session.peer.metadata.name, imageUrl: session.peer.metadata.icons[0] ?? null, hideDelay: 3 * ONE_SECOND_MS, - }) + }), ) } else { await wcWeb3Wallet.rejectSession({ @@ -244,7 +216,7 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. returnToPreviousApp() } }, - [activeAddress, dispatch, onClose, pendingSession, didOpenFromDeepLink] + [activeAddress, dispatch, onClose, pendingSession, didOpenFromDeepLink], ) const dappName = pendingSession.dapp.name || pendingSession.dapp.url || '' @@ -257,7 +229,8 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. scrollDownButtonText={t('walletConnect.pending.button.scrollDown')} onClose={onClose} onConfirm={(): Promise => onPressSettleConnection(true)} - onReject={(): Promise => onPressSettleConnection(false)}> + onReject={(): Promise => onPressSettleConnection(false)} + > - + - + {t('walletConnect.pending.title', { dappName, - })}{' '} + })} - - + + diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionSwitchAccountModal.tsx b/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionSwitchAccountModal.tsx index f91feaace1a..a46e395af34 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionSwitchAccountModal.tsx +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionSwitchAccountModal.tsx @@ -13,11 +13,7 @@ type Props = { onClose: () => void } -export const PendingConnectionSwitchAccountModal = ({ - activeAccount, - onPressAccount, - onClose, -}: Props): JSX.Element => { +export const PendingConnectionSwitchAccountModal = ({ activeAccount, onPressAccount, onClose }: Props): JSX.Element => { const { t } = useTranslation() const signerAccounts = useSignerAccounts() @@ -30,7 +26,7 @@ export const PendingConnectionSwitchAccountModal = ({ render: () => , } }), - [signerAccounts, activeAccount, onPressAccount] + [signerAccounts, activeAccount, onPressAccount], ) return ( diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionSwitchNetworkModal.tsx b/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionSwitchNetworkModal.tsx index 1fb75c2db74..34fed7c78f2 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionSwitchNetworkModal.tsx +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionSwitchNetworkModal.tsx @@ -15,11 +15,7 @@ type Props = { onClose: () => void } -export const PendingConnectionSwitchNetworkModal = ({ - selectedChainId, - onPressChain, - onClose, -}: Props): JSX.Element => { +export const PendingConnectionSwitchNetworkModal = ({ selectedChainId, onPressChain, onClose }: Props): JSX.Element => { const colors = useSporeColors() const { t } = useTranslation() @@ -33,23 +29,14 @@ export const PendingConnectionSwitchNetworkModal = ({ render: () => ( <> - + {info.label} {chainId === selectedChainId && ( - + )} @@ -57,7 +44,7 @@ export const PendingConnectionSwitchNetworkModal = ({ ), } }), - [selectedChainId, onPressChain, colors.accent1] + [selectedChainId, onPressChain, colors.accent1], ) return ( diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/SwitchAccountOption.tsx b/apps/mobile/src/components/WalletConnect/ScanSheet/SwitchAccountOption.tsx index 5ed9f079064..ab322ee9d4a 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/SwitchAccountOption.tsx +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/SwitchAccountOption.tsx @@ -1,10 +1,10 @@ import React from 'react' import { Flex, Separator, Text, Unicon, useSporeColors } from 'ui/src' import Check from 'ui/src/assets/icons/check.svg' +import { shortenAddress } from 'uniswap/src/utils/addresses' import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText' import { Account } from 'wallet/src/features/wallet/accounts/types' import { useDisplayName } from 'wallet/src/features/wallet/hooks' -import { shortenAddress } from 'wallet/src/utils/addresses' type Props = { account: Account diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/WalletConnectModal.tsx b/apps/mobile/src/components/WalletConnect/ScanSheet/WalletConnectModal.tsx index 44c7554933c..67b1a9fc192 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/WalletConnectModal.tsx +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/WalletConnectModal.tsx @@ -50,8 +50,7 @@ export function WalletConnectModal({ const isDarkMode = useIsDarkMode() const activeAccount = useActiveAccount() const { sessions, hasPendingSessionError } = useWalletConnect(activeAccount?.address) - const [currentScreenState, setCurrentScreenState] = - useState(initialScreenState) + const [currentScreenState, setCurrentScreenState] = useState(initialScreenState) const [shouldFreezeCamera, setShouldFreezeCamera] = useState(false) const { preload, navigate } = useEagerExternalProfileRootNavigation() const dispatch = useAppDispatch() @@ -85,18 +84,14 @@ export function WalletConnectModal({ }) if (!supportedURI) { setShouldFreezeCamera(true) - Alert.alert( - t('walletConnect.error.unsupported.title'), - t('walletConnect.error.unsupported.message'), - [ - { - text: t('common.button.tryAgain'), - onPress: (): void => { - setShouldFreezeCamera(false) - }, + Alert.alert(t('walletConnect.error.unsupported.title'), t('walletConnect.error.unsupported.message'), [ + { + text: t('common.button.tryAgain'), + onPress: (): void => { + setShouldFreezeCamera(false) }, - ] - ) + }, + ]) return } @@ -109,18 +104,14 @@ export function WalletConnectModal({ if (supportedURI.type === URIType.WalletConnectURL) { setShouldFreezeCamera(true) - Alert.alert( - t('walletConnect.error.unsupportedV1.title'), - t('walletConnect.error.unsupportedV1.message'), - [ - { - text: t('common.button.ok'), - onPress: (): void => { - setShouldFreezeCamera(false) - }, + Alert.alert(t('walletConnect.error.unsupportedV1.title'), t('walletConnect.error.unsupportedV1.message'), [ + { + text: t('common.button.ok'), + onPress: (): void => { + setShouldFreezeCamera(false) }, - ] - ) + }, + ]) return } @@ -132,18 +123,14 @@ export function WalletConnectModal({ logger.error(error, { tags: { file: 'WalletConnectModal', function: 'onScanCode' }, }) - Alert.alert( - t('walletConnect.error.general.title'), - t('walletConnect.error.general.message'), - [ - { - text: t('common.button.ok'), - onPress: (): void => { - setShouldFreezeCamera(false) - }, + Alert.alert(t('walletConnect.error.general.title'), t('walletConnect.error.general.message'), [ + { + text: t('common.button.ok'), + onPress: (): void => { + setShouldFreezeCamera(false) }, - ] - ) + }, + ]) } } @@ -160,18 +147,14 @@ export function WalletConnectModal({ const parsedUwulinkRequest: UwULinkRequest = JSON.parse(supportedURI.value) const isAllowed = isAllowedUwuLinkRequest(parsedUwulinkRequest, uwuLinkContractAllowlist) if (!isAllowed) { - Alert.alert( - t('walletConnect.error.uwu.title'), - t('walletConnect.error.uwu.unsupported'), - [ - { - text: t('common.button.ok'), - onPress: (): void => { - setShouldFreezeCamera(false) - }, + Alert.alert(t('walletConnect.error.uwu.title'), t('walletConnect.error.uwu.unsupported'), [ + { + text: t('common.button.ok'), + onPress: (): void => { + setShouldFreezeCamera(false) }, - ] - ) + }, + ]) return } @@ -202,19 +185,16 @@ export function WalletConnectModal({ // `message` if it exists. so this is mostly to appease Typescript rawMessage: parsedUwulinkRequest.message, }, - }) + }), ) } else if (parsedUwulinkRequest.method === UwULinkMethod.Erc20Send) { const preparedTransaction = await toTokenTransferRequest( parsedUwulinkRequest, activeAccount, providerManager, - contractManager - ) - const tokenRecipient = findAllowedTokenRecipient( - parsedUwulinkRequest, - uwuLinkContractAllowlist + contractManager, ) + const tokenRecipient = findAllowedTokenRecipient(parsedUwulinkRequest, uwuLinkContractAllowlist) dispatch( addRequest({ @@ -235,7 +215,7 @@ export function WalletConnectModal({ ...preparedTransaction, }, }, - }) + }), ) } else { dispatch( @@ -249,7 +229,7 @@ export function WalletConnectModal({ ...parsedUwulinkRequest.value, }, }, - }) + }), ) } onClose() @@ -292,7 +272,7 @@ export function WalletConnectModal({ uwuLinkContractAllowlist, providerManager, contractManager, - ] + ], ) const onPressBottomToggle = (): void => { @@ -320,7 +300,8 @@ export function WalletConnectModal({ fullScreen backgroundColor={colors.surface1.get()} name={ModalName.WalletConnectScan} - onClose={onClose}> + onClose={onClose} + > <> {currentScreenState === ScannerModalState.ConnectedDapps && ( + onPress={onPressBottomToggle} + > {currentScreenState === ScannerModalState.ScanQr ? ( - + ) : ( - + )} {currentScreenState === ScannerModalState.ScanQr diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/util.ts b/apps/mobile/src/components/WalletConnect/ScanSheet/util.ts index 1e1b1650438..1eba555ae17 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/util.ts +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/util.ts @@ -16,6 +16,7 @@ import { UwULinkMethod, UwULinkRequest, } from 'uniswap/src/types/walletConnect' +import { areAddressesEqual, getValidAddress } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' import { AssetType } from 'wallet/src/entities/assets' import { ContractManager } from 'wallet/src/features/contracts/ContractManager' @@ -24,7 +25,6 @@ import { ScantasticParams, ScantasticParamsSchema } from 'wallet/src/features/sc import { getTokenTransferRequest } from 'wallet/src/features/transactions/transfer/hooks/useTransferTransactionRequest' import { TransferCurrencyParams } from 'wallet/src/features/transactions/transfer/types' import { Account } from 'wallet/src/features/wallet/accounts/types' -import { areAddressesEqual, getValidAddress } from 'wallet/src/utils/addresses' export enum URIType { WalletConnectURL = 'walletconnect', @@ -76,7 +76,7 @@ export const truncateQueryParams = (url: string): string => { export async function getSupportedURI( uri: string, - enabledFeatureFlags?: EnabledFeatureFlags + enabledFeatureFlags?: EnabledFeatureFlags, ): Promise { if (!uri) { return undefined @@ -128,10 +128,7 @@ export async function getSupportedURI( } } -async function getWcUriWithCustomPrefix( - uri: string, - prefix: string -): Promise<{ uri: string; type: URIType } | null> { +async function getWcUriWithCustomPrefix(uri: string, prefix: string): Promise<{ uri: string; type: URIType } | null> { if (uri.indexOf(prefix) !== 0) { return null } @@ -160,7 +157,7 @@ export function useUwuLinkContractAllowlist(): UwULinkAllowlist { export function findAllowedTokenRecipient( request: UwULinkRequest, - allowlist: UwULinkAllowlist + allowlist: UwULinkAllowlist, ): UwULinkAllowlistItem | undefined { if (request.method !== UwULinkMethod.Erc20Send) { return @@ -168,7 +165,7 @@ export function findAllowedTokenRecipient( const { chainId, recipient } = request return allowlist.tokenRecipients.find( - (item) => item.chainId === chainId && areAddressesEqual(item.address, recipient) + (item) => item.chainId === chainId && areAddressesEqual(item.address, recipient), ) } /** @@ -183,10 +180,7 @@ export function findAllowedTokenRecipient( * @param request parsed UwULinkRequest * @returns boolean for whether the UwULinkRequest is allowed */ -export function isAllowedUwuLinkRequest( - request: UwULinkRequest, - allowlist: UwULinkAllowlist -): boolean { +export function isAllowedUwuLinkRequest(request: UwULinkRequest, allowlist: UwULinkAllowlist): boolean { // token sends if (request.method === UwULinkMethod.Erc20Send) { return Boolean(findAllowedTokenRecipient(request, allowlist)) @@ -198,10 +192,8 @@ export function isAllowedUwuLinkRequest( // generic transactions const { to, value } = request.value - const belowMaximumValue = - !value || parseFloat(value) <= parseEther(UWULINK_MAX_TXN_VALUE).toNumber() - const isAllowedContractAddress = - to && allowlist.contracts.some((item) => areAddressesEqual(item.address, to)) + const belowMaximumValue = !value || parseFloat(value) <= parseEther(UWULINK_MAX_TXN_VALUE).toNumber() + const isAllowedContractAddress = to && allowlist.contracts.some((item) => areAddressesEqual(item.address, to)) if (!belowMaximumValue || !isAllowedContractAddress) { return false @@ -289,7 +281,7 @@ export async function toTokenTransferRequest( request: UwULinkErc20SendRequest, account: Account, providerManager: ProviderManager, - contractManager: ContractManager + contractManager: ContractManager, ): Promise { const provider = providerManager.getProvider(request.chainId, RPCType.Public) const params: TransferCurrencyParams = { diff --git a/apps/mobile/src/components/WalletConnect/WalletConnectModals.tsx b/apps/mobile/src/components/WalletConnect/WalletConnectModals.tsx index 9f47f3da266..46490cf4667 100644 --- a/apps/mobile/src/components/WalletConnect/WalletConnectModals.tsx +++ b/apps/mobile/src/components/WalletConnect/WalletConnectModals.tsx @@ -17,15 +17,11 @@ import { Flex, useSporeColors } from 'ui/src' import EyeIcon from 'ui/src/assets/icons/eye.svg' import { iconSizes } from 'ui/src/theme' import { ModalName } from 'uniswap/src/features/telemetry/constants' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { AccountDetails } from 'wallet/src/components/accounts/AccountDetails' import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal' import { WarningSeverity } from 'wallet/src/features/transactions/WarningModal/types' -import { - useActiveAccount, - useActiveAccountAddressWithThrow, - useSignerAccounts, -} from 'wallet/src/features/wallet/hooks' -import { areAddressesEqual } from 'wallet/src/utils/addresses' +import { useActiveAccount, useActiveAccountAddressWithThrow, useSignerAccounts } from 'wallet/src/features/wallet/hooks' export function WalletConnectModals(): JSX.Element { const activeAccount = useActiveAccount() @@ -69,10 +65,7 @@ export function WalletConnectModals(): JSX.Element { )} {pendingSession ? ( - + ) : null} {currRequest ? : null} @@ -92,13 +85,11 @@ function RequestModal({ currRequest }: RequestModalProps): JSX.Element { // TODO: Move returnToPreviousApp() call to onClose but ensure it is not called twice const onClose = (): void => { - dispatch( - removeRequest({ requestInternalId: currRequest.internalId, account: activeAccountAddress }) - ) + dispatch(removeRequest({ requestInternalId: currRequest.internalId, account: activeAccountAddress })) } const isRequestFromSignerAccount = signerAccounts.some((account) => - areAddressesEqual(account.address, currRequest.account) + areAddressesEqual(account.address, currRequest.account), ) if (!isRequestFromSignerAccount) { @@ -107,23 +98,15 @@ function RequestModal({ currRequest }: RequestModalProps): JSX.Element { caption={t('walletConnect.request.warning.message')} closeText={t('common.button.dismiss')} icon={ - + } modalName={ModalName.WCViewOnlyWarning} severity={WarningSeverity.None} title={t('walletConnect.request.warning.title')} onCancel={onClose} - onClose={onClose}> - + onClose={onClose} + > + diff --git a/apps/mobile/src/components/accounts/AccountCardItem.test.tsx b/apps/mobile/src/components/accounts/AccountCardItem.test.tsx index cd6431e1417..b31777f1576 100644 --- a/apps/mobile/src/components/accounts/AccountCardItem.test.tsx +++ b/apps/mobile/src/components/accounts/AccountCardItem.test.tsx @@ -48,24 +48,14 @@ describe(AccountCardItem, () => { describe('portfolio value', () => { it('displays loading shimmmer when portfolio value is loading', () => { const { rerender } = render( - + , ) // Select shimmer placeholder because the actual shimmer is rendered after onLayout // is fired and this logic is not a part of this test expect(screen.queryByTestId('shimmer-placeholder')).toBeTruthy() - rerender( - - ) + rerender() expect(screen.queryByTestId('shimmer-placeholder')).toBeFalsy() }) diff --git a/apps/mobile/src/components/accounts/AccountCardItem.tsx b/apps/mobile/src/components/accounts/AccountCardItem.tsx index de9aa9122a6..a82073c77fc 100644 --- a/apps/mobile/src/components/accounts/AccountCardItem.tsx +++ b/apps/mobile/src/components/accounts/AccountCardItem.tsx @@ -80,7 +80,7 @@ export function AccountCardItem({ pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.Address, - }) + }), ) } @@ -122,14 +122,16 @@ export function AccountCardItem({ if (e.nativeEvent.index === 2) { onPressRemoveWallet() } - }}> + }} + > onPress(address)}> + onPress={(): void => onPress(address)} + > { it('opens account switcher modal when account name is pressed', () => { const { store } = render(, { preloadedState }) - const displayNameText = within(screen.getByTestId('account-header/display-name')).getByText( - ACCOUNT.name - ) + const displayNameText = within(screen.getByTestId('account-header/display-name')).getByText(ACCOUNT.name) expect(isModalOpen(store.getState())).toBe(false) diff --git a/apps/mobile/src/components/accounts/AccountHeader.tsx b/apps/mobile/src/components/accounts/AccountHeader.tsx index 298d5366347..c2805febfd4 100644 --- a/apps/mobile/src/components/accounts/AccountHeader.tsx +++ b/apps/mobile/src/components/accounts/AccountHeader.tsx @@ -7,19 +7,16 @@ import { CopyAlt, Settings } from 'ui/src/components/icons' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import { MobileUserPropertyName, setUserProperty } from 'uniswap/src/features/telemetry/user' import { MobileScreens } from 'uniswap/src/types/screens/mobile' -import { isDevEnv } from 'uniswap/src/utils/env' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' +import { isDevEnv } from 'utilities/src/environment' import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' import { AnimatedUnitagDisplayName } from 'wallet/src/components/accounts/AnimatedUnitagDisplayName' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types' import { AccountType } from 'wallet/src/features/wallet/accounts/types' import { useAvatar, useDisplayName } from 'wallet/src/features/wallet/hooks' -import { - selectActiveAccount, - selectActiveAccountAddress, -} from 'wallet/src/features/wallet/selectors' +import { selectActiveAccount, selectActiveAccountAddress } from 'wallet/src/features/wallet/selectors' import { DisplayNameType } from 'wallet/src/features/wallet/types' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' import { setClipboard } from 'wallet/src/utils/clipboard' export function AccountHeader(): JSX.Element { @@ -60,7 +57,7 @@ export function AccountHeader(): JSX.Element { pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.Address, - }) + }), ) } } @@ -69,13 +66,7 @@ export function AccountHeader(): JSX.Element { const iconSize = 52 return ( - + {activeAddress && ( @@ -92,7 +83,8 @@ export function AccountHeader(): JSX.Element { dispatch(openModal({ name: ModalName.Experiments })) } }} - onPress={onPressAccountHeader}> + onPress={onPressAccountHeader} + > + onPress={onPressSettings} + > @@ -115,12 +108,9 @@ export function AccountHeader(): JSX.Element { alignItems="center" gap="$spacing8" justifyContent="space-between" - testID="account-header/display-name"> - + testID="account-header/display-name" + > + @@ -129,13 +119,10 @@ export function AccountHeader(): JSX.Element { hapticFeedback hitSlop={20} testID="account-header/address-only" - onPress={onPressCopyAddress}> + onPress={onPressCopyAddress} + > - + {sanitizeAddressText(shortenAddress(activeAddress))} diff --git a/apps/mobile/src/components/accounts/AccountList.graphql b/apps/mobile/src/components/accounts/AccountList.graphql index 0af77a14907..6f5fbfc7f52 100644 --- a/apps/mobile/src/components/accounts/AccountList.graphql +++ b/apps/mobile/src/components/accounts/AccountList.graphql @@ -4,7 +4,7 @@ query AccountList( ) { portfolios( ownerAddresses: $addresses - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST, ZORA, CELO, AVALANCHE, ZKSYNC] valueModifiers: $valueModifiers ) { id diff --git a/apps/mobile/src/components/accounts/AccountList.test.tsx b/apps/mobile/src/components/accounts/AccountList.test.tsx index dba6ade3c96..944bceb0b71 100644 --- a/apps/mobile/src/components/accounts/AccountList.test.tsx +++ b/apps/mobile/src/components/accounts/AccountList.test.tsx @@ -1,17 +1,11 @@ import { AccountList } from 'src/components/accounts/AccountList' import { cleanup, fireEvent, render, screen } from 'src/test/test-utils' import { ON_PRESS_EVENT_PAYLOAD } from 'uniswap/src/test/fixtures' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import { NumberType } from 'utilities/src/format/types' -import { - ACCOUNT, - amounts, - portfolio, - readOnlyAccount, - signerMnemonicAccount, -} from 'wallet/src/test/fixtures' +import { ACCOUNT, amounts, portfolio, readOnlyAccount, signerMnemonicAccount } from 'wallet/src/test/fixtures' import { mockLocalizedFormatter } from 'wallet/src/test/mocks' import { createArray, queryResolvers } from 'wallet/src/test/utils' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' const tokensTotalDenominatedValue = amounts.md() const { resolvers } = queryResolvers({ @@ -28,8 +22,8 @@ describe(AccountList, () => { value: tokensTotalDenominatedValue.value, type: NumberType.PortfolioBalance, currencyCode: 'usd', - }) - ) + }), + ), ).toBeDefined() expect(tree.toJSON()).toMatchSnapshot() }) @@ -46,8 +40,8 @@ describe(AccountList, () => { value: tokensTotalDenominatedValue.value, type: NumberType.PortfolioBalance, currencyCode: 'usd', - }) - ) + }), + ), ).toBeDefined() fireEvent.press(screen.getByTestId(`account-item/${ACCOUNT.address}`), ON_PRESS_EVENT_PAYLOAD) diff --git a/apps/mobile/src/components/accounts/AccountList.tsx b/apps/mobile/src/components/accounts/AccountList.tsx index 508a59aa792..acad9fce570 100644 --- a/apps/mobile/src/components/accounts/AccountList.tsx +++ b/apps/mobile/src/components/accounts/AccountList.tsx @@ -6,8 +6,8 @@ import { AccountCardItem } from 'src/components/accounts/AccountCardItem' import { VirtualizedList } from 'src/components/layout/VirtualizedList' import { Flex, Text, useSporeColors } from 'ui/src' import { opacify, spacing } from 'ui/src/theme' +import { PollingInterval } from 'uniswap/src/constants/misc' import { useAsyncData } from 'utilities/src/react/hooks' -import { PollingInterval } from 'wallet/src/constants/misc' import { isNonPollingRequestInFlight } from 'wallet/src/data/utils' import { useAccountList } from 'wallet/src/features/accounts/hooks' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' @@ -82,17 +82,13 @@ export function AccountList({ accounts, onPress, isVisible }: AccountListProps): }, [accounts, data, isPortfolioValueLoading]) const signerAccounts = useMemo(() => { - return accountsWithPortfolioValue.filter( - (account) => account.account.type === AccountType.SignerMnemonic - ) + return accountsWithPortfolioValue.filter((account) => account.account.type === AccountType.SignerMnemonic) }, [accountsWithPortfolioValue]) const hasSignerAccounts = signerAccounts.length > 0 const viewOnlyAccounts = useMemo(() => { - return accountsWithPortfolioValue.filter( - (account) => account.account.type === AccountType.Readonly - ) + return accountsWithPortfolioValue.filter((account) => account.account.type === AccountType.Readonly) }, [accountsWithPortfolioValue]) const hasViewOnlyAccounts = viewOnlyAccounts.length > 0 @@ -108,7 +104,7 @@ export function AccountList({ accounts, onPress, isVisible }: AccountListProps): onPress={onPress} /> ), - [onPress] + [onPress], ) return ( @@ -123,7 +119,8 @@ export function AccountList({ accounts, onPress, isVisible }: AccountListProps): = MIN_ACCOUNTS_TO_ENABLE_SCROLL} - showsVerticalScrollIndicator={false}> + showsVerticalScrollIndicator={false} + > {hasSignerAccounts && ( <> diff --git a/apps/mobile/src/components/accounts/__snapshots__/AccountHeader.test.tsx.snap b/apps/mobile/src/components/accounts/__snapshots__/AccountHeader.test.tsx.snap index b8a3d5cbc94..3c1b670cc09 100644 --- a/apps/mobile/src/components/accounts/__snapshots__/AccountHeader.test.tsx.snap +++ b/apps/mobile/src/components/accounts/__snapshots__/AccountHeader.test.tsx.snap @@ -330,6 +330,7 @@ exports[`AccountHeader renders correctly 1`] = ` style={ { "flexDirection": "column", + "position": "absolute", } } > diff --git a/apps/mobile/src/components/banners/BottomBanner.tsx b/apps/mobile/src/components/banners/BottomBanner.tsx index ecde333368b..2d60025ded3 100644 --- a/apps/mobile/src/components/banners/BottomBanner.tsx +++ b/apps/mobile/src/components/banners/BottomBanner.tsx @@ -12,12 +12,7 @@ export type BottomBannerProps = { translateY?: number } -export function BottomBanner({ - text, - icon, - backgroundColor, - translateY, -}: BottomBannerProps): JSX.Element { +export function BottomBanner({ text, icon, backgroundColor, translateY }: BottomBannerProps): JSX.Element { const animatedStyle = useAnimatedStyle(() => ({ transform: [ { @@ -51,7 +46,8 @@ export function BottomBanner({ position="absolute" right={0} style={animatedStyle} - zIndex="$modal"> + zIndex="$modal" + > {icon} {text} diff --git a/apps/mobile/src/components/banners/ExtensionPromoBanner.tsx b/apps/mobile/src/components/banners/ExtensionPromoBanner.tsx index 87e0367f872..cd32ed0c249 100644 --- a/apps/mobile/src/components/banners/ExtensionPromoBanner.tsx +++ b/apps/mobile/src/components/banners/ExtensionPromoBanner.tsx @@ -1,24 +1,15 @@ import { useTranslation } from 'react-i18next' import { Keyboard, StyleProp, ViewStyle } from 'react-native' import { useAppDispatch } from 'src/app/hooks' -import { - Flex, - Image, - Text, - TouchableArea, - useIsDarkMode, - useIsShortMobileDevice, - useSporeColors, -} from 'ui/src' +import { Flex, Image, Text, TouchableArea, useIsDarkMode, useIsShortMobileDevice, useSporeColors } from 'ui/src' import { EXTENSION_PROMO_BANNER_DARK, EXTENSION_PROMO_BANNER_LIGHT } from 'ui/src/assets' import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' import { borderRadii, iconSizes, spacing } from 'ui/src/theme' +import { FeatureFlags } from 'uniswap/src/features/gating/flags' +import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { MobileEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' -import { - ExtensionOnboardingState, - setExtensionOnboardingState, -} from 'wallet/src/features/behaviorHistory/slice' +import { ExtensionOnboardingState, setExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/slice' const IMAGE_ASPECT_RATIO = 0.69 const IMAGE_SCREEN_WIDTH_PROPORTION = 0.3 @@ -38,7 +29,9 @@ export function ExtensionPromoBanner({ const imageWidth = IMAGE_SCREEN_WIDTH_PROPORTION * fullWidth const imageHeight = imageWidth / IMAGE_ASPECT_RATIO - const onPressClaimNow = (): void => { + const isGAEnabled = useFeatureFlag(FeatureFlags.ExtensionPromotionGA) + + const onPressJoin = (): void => { Keyboard.dismiss() sendAnalyticsEvent(MobileEventName.ExtensionPromoBannerActionTaken, { action: 'join', @@ -75,7 +68,8 @@ export function ExtensionPromoBanner({ pl="$spacing16" shadowColor="$neutral3" shadowOpacity={0.4} - shadowRadius="$spacing4"> + shadowRadius="$spacing4" + > @@ -83,7 +77,7 @@ export function ExtensionPromoBanner({ {!isShortDevice && ( - {t('home.banner.extension.message')} + {isGAEnabled ? t('home.banner.extension.message.default') : t('home.banner.extension.message.beta')} )} @@ -94,9 +88,10 @@ export function ExtensionPromoBanner({ ...baseButtonStyle, backgroundColor: colors.neutral1.get(), }} - onPress={onPressClaimNow}> + onPress={onPressJoin} + > - {t('home.banner.extension.confirm')} + {isGAEnabled ? t('home.banner.extension.confirm.default') : t('home.banner.extension.confirm.beta')} + onPress={onPressMaybeLater} + > {t('common.button.later')} diff --git a/apps/mobile/src/components/banners/OfflineBanner.tsx b/apps/mobile/src/components/banners/OfflineBanner.tsx index 0589572f627..1def59e1975 100644 --- a/apps/mobile/src/components/banners/OfflineBanner.tsx +++ b/apps/mobile/src/components/banners/OfflineBanner.tsx @@ -33,13 +33,7 @@ export function OfflineBanner(): JSX.Element | null { return showBanner ? ( - } + icon={} text={t('home.banner.offline')} translateY={BANNER_HEIGHT - EXTRA_MARGIN} /> diff --git a/apps/mobile/src/components/buttons/BackButton.tsx b/apps/mobile/src/components/buttons/BackButton.tsx index 04c448ee978..adef1898145 100644 --- a/apps/mobile/src/components/buttons/BackButton.tsx +++ b/apps/mobile/src/components/buttons/BackButton.tsx @@ -10,13 +10,7 @@ type Props = { onPressBack?: () => void } & TouchableAreaProps -export function BackButton({ - onPressBack, - size, - color, - showButtonLabel, - ...rest -}: Props): JSX.Element { +export function BackButton({ onPressBack, size, color, showButtonLabel, ...rest }: Props): JSX.Element { const navigation = useNavigation() const goBack = onPressBack @@ -31,7 +25,8 @@ export function BackButton({ hitSlop={24} testID="buttons/back-button" onPress={goBack} - {...rest}> + {...rest} + > ) diff --git a/apps/mobile/src/components/buttons/CopyTextButton.tsx b/apps/mobile/src/components/buttons/CopyTextButton.tsx index ae610352a75..88c95621e18 100644 --- a/apps/mobile/src/components/buttons/CopyTextButton.tsx +++ b/apps/mobile/src/components/buttons/CopyTextButton.tsx @@ -21,9 +21,7 @@ export function CopyTextButton({ copyText }: Props): JSX.Element { const [isCopied, setIsCopied] = useState(false) const copyIcon = - const copiedIcon = ( - - ) + const copiedIcon = const onPress = async (): Promise => { if (copyText) { diff --git a/apps/mobile/src/components/buttons/LinkButton.test.tsx b/apps/mobile/src/components/buttons/LinkButton.test.tsx index bf069787cc9..d7bee3d2088 100644 --- a/apps/mobile/src/components/buttons/LinkButton.test.tsx +++ b/apps/mobile/src/components/buttons/LinkButton.test.tsx @@ -32,7 +32,7 @@ describe(LinkButton, () => { label="link text" openExternalBrowser={openExternalBrowser} url="https://example.com" - /> + />, ) const button = getByText('link text') @@ -41,7 +41,7 @@ describe(LinkButton, () => { expect(require('wallet/src/utils/linking').openUri).toHaveBeenCalledWith( 'https://example.com', openExternalBrowser, - isSafeUri + isSafeUri, ) }) }) diff --git a/apps/mobile/src/components/buttons/LinkButton.tsx b/apps/mobile/src/components/buttons/LinkButton.tsx index 858d9f4ccdb..6132dec98ac 100644 --- a/apps/mobile/src/components/buttons/LinkButton.tsx +++ b/apps/mobile/src/components/buttons/LinkButton.tsx @@ -38,9 +38,7 @@ export function LinkButton({ }, [color]) return ( - => openUri(url, openExternalBrowser, isSafeUri)} - {...rest}> + => openUri(url, openExternalBrowser, isSafeUri)} {...rest}> {label} diff --git a/apps/mobile/src/components/buttons/utils.ts b/apps/mobile/src/components/buttons/utils.ts index 2f64ab7fbc7..6792a73ef48 100644 --- a/apps/mobile/src/components/buttons/utils.ts +++ b/apps/mobile/src/components/buttons/utils.ts @@ -2,11 +2,8 @@ import { withSequence, withSpring, WithSpringConfig } from 'react-native-reanima export function pulseAnimation( activeScale: number, - spingAnimationConfig: WithSpringConfig = { damping: 1, stiffness: 200 } + spingAnimationConfig: WithSpringConfig = { damping: 1, stiffness: 200 }, ): number { 'worklet' - return withSequence( - withSpring(activeScale, spingAnimationConfig), - withSpring(1, spingAnimationConfig) - ) + return withSequence(withSpring(activeScale, spingAnimationConfig), withSpring(1, spingAnimationConfig)) } diff --git a/apps/mobile/src/components/carousel/Indicator.tsx b/apps/mobile/src/components/carousel/Indicator.tsx index 0c6630551db..9dd6022a963 100644 --- a/apps/mobile/src/components/carousel/Indicator.tsx +++ b/apps/mobile/src/components/carousel/Indicator.tsx @@ -4,23 +4,12 @@ import { Flex } from 'ui/src' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' -export function Indicator({ - stepCount, - currentStep, -}: { - stepCount: number - currentStep: number -}): JSX.Element { +export function Indicator({ stepCount, currentStep }: { stepCount: number; currentStep: number }): JSX.Element { const { fullWidth } = useDeviceDimensions() const indicatorWidth = (200 / 375) * fullWidth return ( - + {[...Array(stepCount)].map((_, i) => ( -}): JSX.Element { +function AnimatedIndicatorPill({ index, scroll }: { index: number; scroll: SharedValue }): JSX.Element { const { fullWidth } = useDeviceDimensions() const style = useAnimatedStyle(() => { const inputRange = [(index - 1) * fullWidth, index * fullWidth, (index + 1) * fullWidth] diff --git a/apps/mobile/src/components/education/SeedPhrase.tsx b/apps/mobile/src/components/education/SeedPhrase.tsx index 9845aa3bd9d..36e4b516deb 100644 --- a/apps/mobile/src/components/education/SeedPhrase.tsx +++ b/apps/mobile/src/components/education/SeedPhrase.tsx @@ -10,13 +10,7 @@ import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName' -function Page({ - text, - params, -}: { - text: ReactNode - params: OnboardingStackBaseParams -}): JSX.Element { +function Page({ text, params }: { text: ReactNode; params: OnboardingStackBaseParams }): JSX.Element { const { t } = useTranslation() const { fullWidth } = useDeviceDimensions() const { goToPrev, goToNext } = useContext(CarouselContext) @@ -35,7 +29,7 @@ function Page({ runOnJS(goToNext)() } }), - [goToPrev, goToNext, fullWidth] + [goToPrev, goToNext, fullWidth], ) const dismissGesture = useMemo( @@ -43,19 +37,14 @@ function Page({ Gesture.Tap().onEnd(() => { runOnJS(onDismiss)() }), - [onDismiss] + [onDismiss], ) return ( - + {t('onboarding.tooltip.recoveryPhrase.trigger')} @@ -78,31 +67,16 @@ export const SeedPhraseEducationContent = (params: OnboardingStackBaseParams): J const highlightComponent = const pageContentList = [ - , - , - , + , + , + , , - , - , + , + , ] return pageContentList.map((content) => ( diff --git a/apps/mobile/src/components/explore/ExploreSections.tsx b/apps/mobile/src/components/explore/ExploreSections.tsx index d071dde20d2..d6dae1f4b92 100644 --- a/apps/mobile/src/components/explore/ExploreSections.tsx +++ b/apps/mobile/src/components/explore/ExploreSections.tsx @@ -18,23 +18,20 @@ import { import { usePollOnFocusOnly } from 'src/utils/hooks' import { Flex, Loader, Text, useDeviceInsets } from 'ui/src' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' +import { getWrappedNativeAddress } from 'uniswap/src/constants/addresses' +import { PollingInterval } from 'uniswap/src/constants/misc' import { Chain, ExploreTokensTabQuery, useExploreTokensTabQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getWrappedNativeAddress } from 'wallet/src/constants/addresses' -import { PollingInterval } from 'wallet/src/constants/misc' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' +import { buildCurrencyId, buildNativeCurrencyId } from 'uniswap/src/utils/currencyId' import { usePersistedError } from 'wallet/src/features/dataApi/utils' -import { - selectHasFavoriteTokens, - selectHasWatchedWallets, -} from 'wallet/src/features/favorites/selectors' +import { selectHasFavoriteTokens, selectHasWatchedWallets } from 'wallet/src/features/favorites/selectors' import { selectTokensOrderBy } from 'wallet/src/features/wallet/selectors' -import { areAddressesEqual } from 'wallet/src/utils/addresses' -import { buildCurrencyId, buildNativeCurrencyId } from 'wallet/src/utils/currencyId' type ExploreSectionsProps = { listRef: React.MutableRefObject @@ -86,8 +83,7 @@ export function ExploreSections({ listRef }: ExploreSectionsProps): JSX.Element return } - const isWeth = - areAddressesEqual(token.address, wethAddress) && token?.chain === Chain.Ethereum + const isWeth = areAddressesEqual(token.address, wethAddress) && token?.chain === Chain.Ethereum // manually replace weth with eth given backend only returns eth data as a proxy for eth if (isWeth && eth) { @@ -109,20 +105,13 @@ export function ExploreSections({ listRef }: ExploreSectionsProps): JSX.Element const renderItem: ListRenderItem = useCallback( ({ item, index }: ListRenderItemInfo) => { - return ( - - ) + return }, - [tokenMetadataDisplayType] + [tokenMetadataDisplayType], ) // Don't want to show full screen loading state when changing tokens sort, which triggers NetworkStatus.setVariable request - const isLoading = - networkStatus === NetworkStatus.loading || networkStatus === NetworkStatus.refetch + const isLoading = networkStatus === NetworkStatus.loading || networkStatus === NetworkStatus.refetch const hasAllData = !!data?.topTokens const error = usePersistedError(requestLoading, requestError) @@ -161,7 +150,8 @@ export function ExploreSections({ listRef }: ExploreSectionsProps): JSX.Element }, }): void => { visibleListHeight.value = height - }}> + }} + > + pl="$spacing4" + > {t('explore.tokens.top.title')} @@ -211,13 +202,11 @@ export function ExploreSections({ listRef }: ExploreSectionsProps): JSX.Element } const tokenKey = (token: TokenItemData): string => { - return token.address - ? buildCurrencyId(token.chainId, token.address) - : buildNativeCurrencyId(token.chainId) + return token.address ? buildCurrencyId(token.chainId, token.address) : buildNativeCurrencyId(token.chainId) } function gqlTokenToTokenItemData( - token: Maybe[0]>> + token: Maybe[0]>>, ): TokenItemData | null { if (!token || !token.project) { return null @@ -256,13 +245,7 @@ function FavoritesSection(props: FavoritesSectionProps): JSX.Element | null { } return ( - + {hasFavoritedTokens && } {hasFavoritedWallets && } diff --git a/apps/mobile/src/components/explore/FavoriteHeaderRow.tsx b/apps/mobile/src/components/explore/FavoriteHeaderRow.tsx index 681f0f48e96..2964215086e 100644 --- a/apps/mobile/src/components/explore/FavoriteHeaderRow.tsx +++ b/apps/mobile/src/components/explore/FavoriteHeaderRow.tsx @@ -17,28 +17,13 @@ export function FavoriteHeaderRow({ }): JSX.Element { const { t } = useTranslation() return ( - + {isEditing ? editingTitle : title} {!isEditing ? ( - - + + ) : ( diff --git a/apps/mobile/src/components/explore/FavoriteTokenCard.test.tsx b/apps/mobile/src/components/explore/FavoriteTokenCard.test.tsx index 51b99702271..2fa25c8f4d5 100644 --- a/apps/mobile/src/components/explore/FavoriteTokenCard.test.tsx +++ b/apps/mobile/src/components/explore/FavoriteTokenCard.test.tsx @@ -6,13 +6,7 @@ import { ON_PRESS_EVENT_PAYLOAD } from 'uniswap/src/test/fixtures' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' import { FiatCurrency } from 'wallet/src/features/fiatCurrency/constants' import { Language } from 'wallet/src/features/language/constants' -import { - SAMPLE_CURRENCY_ID_1, - amount, - ethToken, - tokenProject, - tokenProjectMarket, -} from 'wallet/src/test/fixtures' +import { SAMPLE_CURRENCY_ID_1, amount, ethToken, tokenProject, tokenProjectMarket } from 'wallet/src/test/fixtures' import { queryResolvers } from 'wallet/src/test/utils' const mockedNavigation = { diff --git a/apps/mobile/src/components/explore/FavoriteTokenCard.tsx b/apps/mobile/src/components/explore/FavoriteTokenCard.tsx index 5025a1b61c8..e1ec81dc633 100644 --- a/apps/mobile/src/components/explore/FavoriteTokenCard.tsx +++ b/apps/mobile/src/components/explore/FavoriteTokenCard.tsx @@ -14,15 +14,15 @@ import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { borderRadii, imageSizes } from 'ui/src/theme' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' +import { PollingInterval } from 'uniswap/src/constants/misc' import { useFavoriteTokenCardQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { SectionName } from 'uniswap/src/features/telemetry/constants' import { UniverseChainId } from 'uniswap/src/types/chains' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' import { NumberType } from 'utilities/src/format/types' import { RelativeChange } from 'wallet/src/components/text/RelativeChange' -import { PollingInterval } from 'wallet/src/constants/misc' import { isNonPollingRequestInFlight } from 'wallet/src/data/utils' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils' import { removeFavoriteToken } from 'wallet/src/features/favorites/slice' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' @@ -63,10 +63,7 @@ function FavoriteTokenCard({ // Mirror behavior in top tokens list, use first chain the token is on for the symbol const chainId = fromGraphQLChain(token?.chain) ?? UniverseChainId.Mainnet - const price = convertFiatAmountFormatted( - token?.project?.markets?.[0]?.price?.value, - NumberType.FiatTokenPrice - ) + const price = convertFiatAmountFormatted(token?.project?.markets?.[0]?.price?.value, NumberType.FiatTokenPrice) const pricePercentChange = token?.project?.markets?.[0]?.pricePercentChange24h?.value const onRemove = useCallback(() => { @@ -107,7 +104,8 @@ function FavoriteTokenCard({ disabled={isEditing} style={{ borderRadius: borderRadii.rounded16 }} onPress={onContextMenuPress} - {...rest}> + {...rest} + > + onPress={onPress} + > diff --git a/apps/mobile/src/components/explore/FavoriteTokensGrid.tsx b/apps/mobile/src/components/explore/FavoriteTokensGrid.tsx index 8afe8d3e960..f9e46081ec2 100644 --- a/apps/mobile/src/components/explore/FavoriteTokensGrid.tsx +++ b/apps/mobile/src/components/explore/FavoriteTokensGrid.tsx @@ -3,9 +3,7 @@ import { useTranslation } from 'react-i18next' import { FadeIn, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' import { useAppSelector } from 'src/app/hooks' import { FavoriteHeaderRow } from 'src/components/explore/FavoriteHeaderRow' -import FavoriteTokenCard, { - FAVORITE_TOKEN_CARD_LOADER_HEIGHT, -} from 'src/components/explore/FavoriteTokenCard' +import FavoriteTokenCard, { FAVORITE_TOKEN_CARD_LOADER_HEIGHT } from 'src/components/explore/FavoriteTokenCard' import { Loader } from 'src/components/loading' import { AutoScrollProps, @@ -27,10 +25,7 @@ type FavoriteTokensGridProps = AutoScrollProps & { } /** Renders the favorite tokens section on the Explore tab */ -export function FavoriteTokensGrid({ - showLoading, - ...rest -}: FavoriteTokensGridProps): JSX.Element | null { +export function FavoriteTokensGrid({ showLoading, ...rest }: FavoriteTokensGridProps): JSX.Element | null { const { t } = useTranslation() const dispatch = useAppDispatch() @@ -49,7 +44,7 @@ export function FavoriteTokensGrid({ ({ data }: SortableGridChangeEvent) => { dispatch(setFavoriteTokens({ currencyIds: data })) }, - [dispatch] + [dispatch], ) const renderItem = useCallback>( @@ -63,7 +58,7 @@ export function FavoriteTokensGrid({ setIsEditing={setIsEditing} /> ), - [isEditing] + [isEditing], ) const animatedStyle = useAnimatedStyle(() => ({ diff --git a/apps/mobile/src/components/explore/FavoriteWalletCard.test.tsx b/apps/mobile/src/components/explore/FavoriteWalletCard.test.tsx index 916243e359d..4ad93744c9c 100644 --- a/apps/mobile/src/components/explore/FavoriteWalletCard.test.tsx +++ b/apps/mobile/src/components/explore/FavoriteWalletCard.test.tsx @@ -1,20 +1,14 @@ import { makeMutable } from 'react-native-reanimated' import configureMockStore from 'redux-mock-store' -import FavoriteWalletCard, { - FavoriteWalletCardProps, -} from 'src/components/explore/FavoriteWalletCard' +import FavoriteWalletCard, { FavoriteWalletCardProps } from 'src/components/explore/FavoriteWalletCard' import { preloadedMobileState } from 'src/test/fixtures' import { fireEvent, render, waitFor } from 'src/test/test-utils' import * as unitagHooks from 'uniswap/src/features/unitags/hooks' import { ON_PRESS_EVENT_PAYLOAD } from 'uniswap/src/test/fixtures' import { MobileScreens } from 'uniswap/src/types/screens/mobile' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import * as ensHooks from 'wallet/src/features/ens/api' -import { - SAMPLE_SEED_ADDRESS_1, - preloadedWalletState, - signerMnemonicAccount, -} from 'wallet/src/test/fixtures' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' +import { SAMPLE_SEED_ADDRESS_1, preloadedWalletState, signerMnemonicAccount } from 'wallet/src/test/fixtures' const mockedNavigation = { navigate: jest.fn(), diff --git a/apps/mobile/src/components/explore/FavoriteWalletCard.tsx b/apps/mobile/src/components/explore/FavoriteWalletCard.tsx index a69196f770e..e7299c5cf65 100644 --- a/apps/mobile/src/components/explore/FavoriteWalletCard.tsx +++ b/apps/mobile/src/components/explore/FavoriteWalletCard.tsx @@ -76,7 +76,8 @@ function FavoriteWalletCard({ setIsEditing(true) } }} - {...rest}> + {...rest} + > => { await preload(address) - }}> + }} + > diff --git a/apps/mobile/src/components/explore/FavoriteWalletsGrid.tsx b/apps/mobile/src/components/explore/FavoriteWalletsGrid.tsx index 7bf7cb38e8c..314cd6b0d87 100644 --- a/apps/mobile/src/components/explore/FavoriteWalletsGrid.tsx +++ b/apps/mobile/src/components/explore/FavoriteWalletsGrid.tsx @@ -25,10 +25,7 @@ type FavoriteWalletsGridProps = AutoScrollProps & { } /** Renders the favorite wallets section on the Explore tab */ -export function FavoriteWalletsGrid({ - showLoading, - ...rest -}: FavoriteWalletsGridProps): JSX.Element { +export function FavoriteWalletsGrid({ showLoading, ...rest }: FavoriteWalletsGridProps): JSX.Element { const { t } = useTranslation() const dispatch = useAppDispatch() @@ -48,7 +45,7 @@ export function FavoriteWalletsGrid({ ({ data }: SortableGridChangeEvent) => { dispatch(setFavoriteWallets({ addresses: data })) }, - [dispatch] + [dispatch], ) const renderItem = useCallback>( @@ -62,7 +59,7 @@ export function FavoriteWalletsGrid({ setIsEditing={setIsEditing} /> ), - [isEditing] + [isEditing], ) const animatedStyle = useAnimatedStyle(() => ({ diff --git a/apps/mobile/src/components/explore/RemoveButton.tsx b/apps/mobile/src/components/explore/RemoveButton.tsx index a42ada56f75..d03f9f543ac 100644 --- a/apps/mobile/src/components/explore/RemoveButton.tsx +++ b/apps/mobile/src/components/explore/RemoveButton.tsx @@ -23,7 +23,8 @@ export default function RemoveButton({ visible = true, ...rest }: RemoveButtonPr testID="explore/remove-button" width={imageSizes.image24} zIndex="$tooltip" - {...rest}> + {...rest} + > ) diff --git a/apps/mobile/src/components/explore/SortButton.test.tsx b/apps/mobile/src/components/explore/SortButton.test.tsx index f7a0902565c..ef305932469 100644 --- a/apps/mobile/src/components/explore/SortButton.test.tsx +++ b/apps/mobile/src/components/explore/SortButton.test.tsx @@ -65,18 +65,16 @@ describe('SortButton', () => { }, { title: 'Price increase (24H)', - systemIcon: - orderBy === ClientTokensOrderBy.PriceChangePercentage24hDesc ? 'checkmark' : '', + systemIcon: orderBy === ClientTokensOrderBy.PriceChangePercentage24hDesc ? 'checkmark' : '', orderBy: ClientTokensOrderBy.PriceChangePercentage24hDesc, }, { title: 'Price decrease (24H)', - systemIcon: - orderBy === ClientTokensOrderBy.PriceChangePercentage24hAsc ? 'checkmark' : '', + systemIcon: orderBy === ClientTokensOrderBy.PriceChangePercentage24hAsc ? 'checkmark' : '', orderBy: ClientTokensOrderBy.PriceChangePercentage24hAsc, }, ], - }) + }), ) }) }) diff --git a/apps/mobile/src/components/explore/SortButton.tsx b/apps/mobile/src/components/explore/SortButton.tsx index 9e4b47f7e1d..e0d00720fe6 100644 --- a/apps/mobile/src/components/explore/SortButton.tsx +++ b/apps/mobile/src/components/explore/SortButton.tsx @@ -2,10 +2,7 @@ import React, { memo, useMemo } from 'react' import { useTranslation } from 'react-i18next' import ContextMenu from 'react-native-context-menu-view' import { useAppDispatch } from 'src/app/hooks' -import { - getTokensOrderByMenuLabel, - getTokensOrderBySelectedLabel, -} from 'src/features/explore/utils' +import { getTokensOrderByMenuLabel, getTokensOrderBySelectedLabel } from 'src/features/explore/utils' import { disableOnPress } from 'src/utils/disableOnPress' import { Flex, Text, TouchableArea, useIsDarkMode } from 'ui/src' import { RotatableChevron } from 'ui/src/components/icons' @@ -73,7 +70,8 @@ function _SortButton({ orderBy }: FilterGroupProps): JSX.Element { sendAnalyticsEvent(MobileEventName.ExploreFilterSelected, { filter_type: selectedMenuAction.orderBy, }) - }}> + }} + > + onLongPress={disableOnPress} + > {orderBy === TokenSortableField.Volume || orderBy === TokenSortableField.TotalValueLocked} {getTokensOrderBySelectedLabel(orderBy, t)} - + diff --git a/apps/mobile/src/components/explore/TokenItem.test.tsx b/apps/mobile/src/components/explore/TokenItem.test.tsx index 7a73c486e8c..a0ef2fe0919 100644 --- a/apps/mobile/src/components/explore/TokenItem.test.tsx +++ b/apps/mobile/src/components/explore/TokenItem.test.tsx @@ -4,8 +4,8 @@ import * as exploreHooks from 'src/components/explore/hooks' import { TOKEN_ITEM_DATA, tokenItemData } from 'src/test/fixtures' import { fireEvent, render, within } from 'src/test/test-utils' import { ON_PRESS_EVENT_PAYLOAD } from 'uniswap/src/test/fixtures' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { TokenMetadataDisplayType } from 'wallet/src/features/wallet/types' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' describe('TokenItem', () => { const mockedTokenDetailsNavigation = { @@ -15,9 +15,7 @@ describe('TokenItem', () => { } beforeAll(() => { - jest - .spyOn(tokenDetailsHooks, 'useTokenDetailsNavigation') - .mockReturnValue(mockedTokenDetailsNavigation) + jest.spyOn(tokenDetailsHooks, 'useTokenDetailsNavigation').mockReturnValue(mockedTokenDetailsNavigation) jest.spyOn(exploreHooks, 'useExploreTokenContextMenu').mockReturnValue({ menuActions: [], onContextMenuPress: jest.fn(), @@ -50,9 +48,7 @@ describe('TokenItem', () => { fireEvent.press(getByTestId(`token-item-${data.name}`), ON_PRESS_EVENT_PAYLOAD) - expect(mockedTokenDetailsNavigation.navigate).toHaveBeenCalledWith( - buildCurrencyId(data.chainId, data.address) - ) + expect(mockedTokenDetailsNavigation.navigate).toHaveBeenCalledWith(buildCurrencyId(data.chainId, data.address)) }) describe('token price', () => { @@ -111,9 +107,7 @@ describe('TokenItem', () => { ] it.each(cases)('renders $test metadata subtitle', ({ type, expected }) => { - const { getByTestId } = render( - - ) + const { getByTestId } = render() const metadataSubtitle = getByTestId('token-item/metadata-subtitle') diff --git a/apps/mobile/src/components/explore/TokenItem.tsx b/apps/mobile/src/components/explore/TokenItem.tsx index 6668973eb10..1916a03d55b 100644 --- a/apps/mobile/src/components/explore/TokenItem.tsx +++ b/apps/mobile/src/components/explore/TokenItem.tsx @@ -11,16 +11,16 @@ import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' import { MobileEventName, SectionName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { WalletChainId } from 'uniswap/src/types/chains' -import { NumberType } from 'utilities/src/format/types' -import { RelativeChange } from 'wallet/src/components/text/RelativeChange' -import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' -import { TokenMetadataDisplayType } from 'wallet/src/features/wallet/types' import { buildCurrencyId, buildNativeCurrencyId, currencyIdToAddress, currencyIdToChain, -} from 'wallet/src/utils/currencyId' +} from 'uniswap/src/utils/currencyId' +import { NumberType } from 'utilities/src/format/types' +import { RelativeChange } from 'wallet/src/components/text/RelativeChange' +import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' +import { TokenMetadataDisplayType } from 'wallet/src/features/wallet/types' export type TokenItemData = { name: string @@ -41,11 +41,7 @@ interface TokenItemProps { metadataDisplayType?: TokenMetadataDisplayType } -export const TokenItem = memo(function _TokenItem({ - tokenItemData, - index, - metadataDisplayType, -}: TokenItemProps) { +export const TokenItem = memo(function _TokenItem({ tokenItemData, index, metadataDisplayType }: TokenItemProps) { const { t } = useTranslation() const tokenDetailsNavigation = useTokenDetailsNavigation() const { convertFiatAmountFormatted } = useLocalizationContext() @@ -65,10 +61,7 @@ export const TokenItem = memo(function _TokenItem({ const _currencyId = address ? buildCurrencyId(chainId, address) : buildNativeCurrencyId(chainId) const marketCapFormatted = convertFiatAmountFormatted(marketCap, NumberType.FiatTokenDetails) const volume24hFormatted = convertFiatAmountFormatted(volume24h, NumberType.FiatTokenDetails) - const totalValueLockedFormatted = convertFiatAmountFormatted( - totalValueLocked, - NumberType.FiatTokenDetails - ) + const totalValueLockedFormatted = convertFiatAmountFormatted(totalValueLocked, NumberType.FiatTokenDetails) const getMetadataSubtitle = (): string | undefined => { switch (metadataDisplayType) { @@ -109,7 +102,8 @@ export const TokenItem = memo(function _TokenItem({ hapticStyle={ImpactFeedbackStyle.Light} testID={`token-item-${name}`} onLongPress={disableOnPress} - onPress={onPress}> + onPress={onPress} + > {index !== undefined && ( @@ -125,11 +119,7 @@ export const TokenItem = memo(function _TokenItem({ {name} - + {getMetadataSubtitle()} diff --git a/apps/mobile/src/components/explore/hooks.ts b/apps/mobile/src/components/explore/hooks.ts index b1c2950d7ec..16986f14310 100644 --- a/apps/mobile/src/components/explore/hooks.ts +++ b/apps/mobile/src/components/explore/hooks.ts @@ -10,15 +10,12 @@ import { ElementName, ModalName, SectionNameType } from 'uniswap/src/features/te import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { WalletChainId } from 'uniswap/src/types/chains' import { CurrencyId } from 'uniswap/src/types/currency' +import { currencyIdToAddress } from 'uniswap/src/utils/currencyId' import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants' import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { AssetType } from 'wallet/src/entities/assets' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { useAppDispatch } from 'wallet/src/state' -import { currencyIdToAddress } from 'wallet/src/utils/currencyId' interface TokenMenuParams { currencyId: CurrencyId @@ -49,11 +46,8 @@ export function useExploreTokenContextMenu({ const currencyAddress = currencyIdToAddress(currencyId) const onPressReceive = useCallback( - () => - dispatch( - openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }) - ), - [dispatch] + () => dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })), + [dispatch], ) const onPressShare = useCallback(async () => { @@ -87,9 +81,7 @@ export function useExploreTokenContextMenu({ const menuActions = useMemo( () => [ { - title: isFavorited - ? t('explore.tokens.favorite.action.remove') - : t('explore.tokens.favorite.action.add'), + title: isFavorited ? t('explore.tokens.favorite.action.remove') : t('explore.tokens.favorite.action.add'), systemIcon: isFavorited ? 'heart.fill' : 'heart', onPress: onPressToggleFavorite, }, @@ -118,22 +110,14 @@ export function useExploreTokenContextMenu({ ] : []), ], - [ - isFavorited, - t, - onPressToggleFavorite, - onEditFavorites, - onPressSwap, - onPressReceive, - onPressShare, - ] + [isFavorited, t, onPressToggleFavorite, onEditFavorites, onPressSwap, onPressReceive, onPressShare], ) const onContextMenuPress = useCallback( async (e: NativeSyntheticEvent): Promise => { await menuActions[e.nativeEvent.index]?.onPress?.() }, - [menuActions] + [menuActions], ) return { menuActions, onContextMenuPress } @@ -141,7 +125,7 @@ export function useExploreTokenContextMenu({ export function useAnimatedCardDragStyle( pressProgress: SharedValue, - dragActivationProgress: SharedValue + dragActivationProgress: SharedValue, ): StyleProps { return useAnimatedStyle(() => ({ opacity: diff --git a/apps/mobile/src/components/explore/search/SearchEmptySection.tsx b/apps/mobile/src/components/explore/search/SearchEmptySection.tsx index c2784b58b42..4972dccc2fb 100644 --- a/apps/mobile/src/components/explore/search/SearchEmptySection.tsx +++ b/apps/mobile/src/components/explore/search/SearchEmptySection.tsx @@ -47,16 +47,8 @@ export function SearchEmptySection(): JSX.Element { - } - title={t('explore.search.section.recent')} - /> + + } title={t('explore.search.section.recent')} /> {t('explore.search.action.clear')} @@ -81,10 +73,7 @@ export function SearchEmptySection(): JSX.Element { + } data={SUGGESTED_WALLETS} keyExtractor={walletKey} @@ -100,7 +89,5 @@ const walletKey = (wallet: WalletSearchResult): string => { export const RecentIcon = (): JSX.Element => { const colors = useSporeColors() - return ( - - ) + return } diff --git a/apps/mobile/src/components/explore/search/SearchPopularNFTCollections.tsx b/apps/mobile/src/components/explore/search/SearchPopularNFTCollections.tsx index 2428f98703a..b7edf4c754e 100644 --- a/apps/mobile/src/components/explore/search/SearchPopularNFTCollections.tsx +++ b/apps/mobile/src/components/explore/search/SearchPopularNFTCollections.tsx @@ -1,20 +1,12 @@ import React, { useMemo } from 'react' import { FlatList, ListRenderItemInfo } from 'react-native' import { SearchNFTCollectionItem } from 'src/components/explore/search/items/SearchNFTCollectionItem' -import { - getSearchResultId, - gqlNFTToNFTCollectionSearchResult, -} from 'src/components/explore/search/utils' +import { getSearchResultId, gqlNFTToNFTCollectionSearchResult } from 'src/components/explore/search/utils' import { Inset, Loader } from 'ui/src' import { useSearchPopularNftCollectionsQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { - NFTCollectionSearchResult, - SearchResultType, -} from 'wallet/src/features/search/SearchResult' +import { NFTCollectionSearchResult, SearchResultType } from 'wallet/src/features/search/SearchResult' -function isNFTCollectionSearchResult( - result: NFTCollectionSearchResult | null -): result is NFTCollectionSearchResult { +function isNFTCollectionSearchResult(result: NFTCollectionSearchResult | null): result is NFTCollectionSearchResult { return (result as NFTCollectionSearchResult).type === SearchResultType.NFTCollection } @@ -27,9 +19,7 @@ export function SearchPopularNFTCollections(): JSX.Element { return } - const searchResults = data.topCollections.edges.map(({ node }) => - gqlNFTToNFTCollectionSearchResult(node) - ) + const searchResults = data.topCollections.edges.map(({ node }) => gqlNFTToNFTCollectionSearchResult(node)) return searchResults.filter(isNFTCollectionSearchResult) }, [data]) @@ -41,17 +31,9 @@ export function SearchPopularNFTCollections(): JSX.Element { ) } - return ( - - ) + return } -const renderNFTCollectionItem = ({ - item, -}: ListRenderItemInfo): JSX.Element => ( +const renderNFTCollectionItem = ({ item }: ListRenderItemInfo): JSX.Element => ( ) diff --git a/apps/mobile/src/components/explore/search/SearchPopularTokens.tsx b/apps/mobile/src/components/explore/search/SearchPopularTokens.tsx index 9701e286518..5b4b60f54af 100644 --- a/apps/mobile/src/components/explore/search/SearchPopularTokens.tsx +++ b/apps/mobile/src/components/explore/search/SearchPopularTokens.tsx @@ -3,7 +3,7 @@ import { FlatList, ListRenderItemInfo } from 'react-native' import { SearchTokenItem } from 'src/components/explore/search/items/SearchTokenItem' import { getSearchResultId } from 'src/components/explore/search/utils' import { Inset, Loader } from 'ui/src' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { SearchResultType, TokenSearchResult } from 'wallet/src/features/search/SearchResult' import { TopToken, usePopularTokens } from 'wallet/src/features/tokens/hooks' @@ -33,11 +33,8 @@ function gqlTokenToTokenSearchResult(token: Maybe): TokenSearchResult export function SearchPopularTokens(): JSX.Element { const { popularTokens, loading } = usePopularTokens() const tokens = useMemo( - () => - popularTokens - ?.map(gqlTokenToTokenSearchResult) - .filter((t): t is TokenSearchResult => Boolean(t)), - [popularTokens] + () => popularTokens?.map(gqlTokenToTokenSearchResult).filter((t): t is TokenSearchResult => Boolean(t)), + [popularTokens], ) if (loading) { diff --git a/apps/mobile/src/components/explore/search/SearchResultsSection.tsx b/apps/mobile/src/components/explore/search/SearchResultsSection.tsx index 1421b99fa41..dd8a3eb1aec 100644 --- a/apps/mobile/src/components/explore/search/SearchResultsSection.tsx +++ b/apps/mobile/src/components/explore/search/SearchResultsSection.tsx @@ -26,14 +26,10 @@ import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { useExploreSearchQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import i18n from 'uniswap/src/i18n/i18n' import { UniverseChainId } from 'uniswap/src/types/chains' +import { getValidAddress } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' import { SearchContext } from 'wallet/src/features/search/SearchContext' -import { - NFTCollectionSearchResult, - SearchResultType, - TokenSearchResult, -} from 'wallet/src/features/search/SearchResult' -import { getValidAddress } from 'wallet/src/utils/addresses' +import { NFTCollectionSearchResult, SearchResultType, TokenSearchResult } from 'wallet/src/features/search/SearchResult' const ICON_SIZE = '$icon.24' const ICON_COLOR = '$neutral2' @@ -108,7 +104,7 @@ export function SearchResultsSection({ searchQuery }: { searchQuery: string }): const validAddress: Address | undefined = useMemo( () => getValidAddress(searchQuery, true, false) ?? undefined, - [searchQuery] + [searchQuery], ) const countTokenResults = tokenResults?.length ?? 0 @@ -116,9 +112,7 @@ export function SearchResultsSection({ searchQuery }: { searchQuery: string }): const countWalletResults = walletSearchResults.length const countTotalResults = countTokenResults + countNftCollectionResults + countWalletResults - const prefixTokenMatch = tokenResults?.find((res: TokenSearchResult) => - isPrefixTokenMatch(res, searchQuery) - ) + const prefixTokenMatch = tokenResults?.find((res: TokenSearchResult) => isPrefixTokenMatch(res, searchQuery)) const hasVerifiedNFTResults = Boolean(nftCollectionResults?.some((res) => res.isVerified)) @@ -126,18 +120,14 @@ export function SearchResultsSection({ searchQuery }: { searchQuery: string }): return searchQuery.includes('.') }, [searchQuery]) - const showWalletSectionFirst = - isUsernameSearch && (exactUnitagMatch || (exactENSMatch && !prefixTokenMatch)) + const showWalletSectionFirst = isUsernameSearch && (exactUnitagMatch || (exactENSMatch && !prefixTokenMatch)) const showNftCollectionsBeforeTokens = hasVerifiedNFTResults && !tokenResults?.length const sortedSearchResults: SearchResultOrHeader[] = useMemo(() => { // Format results arrays with header, and handle empty results - const nftsWithHeader = nftCollectionResults?.length - ? [NFTHeaderItem, ...nftCollectionResults] - : [] + const nftsWithHeader = nftCollectionResults?.length ? [NFTHeaderItem, ...nftCollectionResults] : [] const tokensWithHeader = tokenResults?.length ? [TokenHeaderItem, ...tokenResults] : [] - const walletsWithHeader = - walletSearchResults.length > 0 ? [WalletHeaderItem, ...walletSearchResults] : [] + const walletsWithHeader = walletSearchResults.length > 0 ? [WalletHeaderItem, ...walletSearchResults] : [] let searchResultItems: SearchResultOrHeader[] = [] @@ -215,9 +205,8 @@ export function SearchResultsSection({ searchQuery }: { searchQuery: string }): ? undefined : props.index + 1 - - sortedSearchResults - .slice(0, props.index + 1) - .filter((item) => item.type === SEARCH_RESULT_HEADER_KEY).length + sortedSearchResults.slice(0, props.index + 1).filter((item) => item.type === SEARCH_RESULT_HEADER_KEY) + .length return renderSearchItem({ ...props, searchContext: { @@ -265,7 +254,7 @@ export const renderSearchItem = ({ logger.warn( 'SearchResultsSection', 'renderSearchItem', - `Found invalid list item in search results: ${JSON.stringify(searchResult)}` + `Found invalid list item in search results: ${JSON.stringify(searchResult)}`, ) return null } diff --git a/apps/mobile/src/components/explore/search/SearchSectionHeader.tsx b/apps/mobile/src/components/explore/search/SearchSectionHeader.tsx index b6f4fe2fbb5..1952d47cca1 100644 --- a/apps/mobile/src/components/explore/search/SearchSectionHeader.tsx +++ b/apps/mobile/src/components/explore/search/SearchSectionHeader.tsx @@ -6,11 +6,7 @@ interface SectionHeaderTextProps { icon?: JSX.Element } -export const SectionHeaderText = ({ - title, - icon, - ...rest -}: SectionHeaderTextProps & TextProps): JSX.Element => { +export const SectionHeaderText = ({ title, icon, ...rest }: SectionHeaderTextProps & TextProps): JSX.Element => { return ( {icon && icon} diff --git a/apps/mobile/src/components/explore/search/hooks.ts b/apps/mobile/src/components/explore/search/hooks.ts index 2706ee3e3be..e66cad8c955 100644 --- a/apps/mobile/src/components/explore/search/hooks.ts +++ b/apps/mobile/src/components/explore/search/hooks.ts @@ -1,10 +1,10 @@ import { useMemo } from 'react' import { useUnitagByAddress, useUnitagByName } from 'uniswap/src/features/unitags/hooks' import { UniverseChainId } from 'uniswap/src/types/chains' +import { getValidAddress } from 'uniswap/src/utils/addresses' import { useENS } from 'wallet/src/features/ens/useENS' import { SearchResultType, WalletSearchResult } from 'wallet/src/features/search/SearchResult' import { useIsSmartContractAddress } from 'wallet/src/features/transactions/transfer/hooks/useIsSmartContractAddress' -import { getValidAddress } from 'wallet/src/utils/addresses' // eslint-disable-next-line complexity export function useWalletSearchResults(query: string): { @@ -13,10 +13,7 @@ export function useWalletSearchResults(query: string): { exactENSMatch: boolean exactUnitagMatch: boolean } { - const validAddress: Address | undefined = useMemo( - () => getValidAddress(query, true, false) ?? undefined, - [query] - ) + const validAddress: Address | undefined = useMemo(() => getValidAddress(query, true, false) ?? undefined, [query]) const querySkippedIfValidAddress = validAddress ? null : query @@ -38,12 +35,13 @@ export function useWalletSearchResults(query: string): { const { unitag: unitagByName, loading: unitagLoading } = useUnitagByName(query) // Search for matching Unitag by address - const { unitag: unitagByAddress, loading: unitagByAddressLoading } = - useUnitagByAddress(validAddress) + const { unitag: unitagByAddress, loading: unitagByAddressLoading } = useUnitagByAddress(validAddress) // Search for matching EOA wallet address - const { isSmartContractAddress, loading: loadingIsSmartContractAddress } = - useIsSmartContractAddress(validAddress, UniverseChainId.Mainnet) + const { isSmartContractAddress, loading: loadingIsSmartContractAddress } = useIsSmartContractAddress( + validAddress, + UniverseChainId.Mainnet, + ) const hasENSResult = dotEthName && dotEthAddress const hasEOAResult = validAddress && !isSmartContractAddress @@ -108,11 +106,7 @@ export function useWalletSearchResults(query: string): { // Ensure loading is returned const walletsLoading = - dotEthLoading || - ensLoading || - loadingIsSmartContractAddress || - unitagLoading || - unitagByAddressLoading + dotEthLoading || ensLoading || loadingIsSmartContractAddress || unitagLoading || unitagByAddressLoading return { loading: walletsLoading, diff --git a/apps/mobile/src/components/explore/search/items/SearchENSAddressItem.tsx b/apps/mobile/src/components/explore/search/items/SearchENSAddressItem.tsx index 8348dd92172..4ebc857a132 100644 --- a/apps/mobile/src/components/explore/search/items/SearchENSAddressItem.tsx +++ b/apps/mobile/src/components/explore/search/items/SearchENSAddressItem.tsx @@ -3,22 +3,19 @@ import { useTranslation } from 'react-i18next' import { SearchWalletItemBase } from 'src/components/explore/search/items/SearchWalletItemBase' import { Flex, Text } from 'ui/src' import { imageSizes } from 'ui/src/theme' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' import { useENSAvatar, useENSName } from 'wallet/src/features/ens/api' import { getCompletedENSName } from 'wallet/src/features/ens/useENS' import { SearchContext } from 'wallet/src/features/search/SearchContext' import { ENSAddressSearchResult } from 'wallet/src/features/search/SearchResult' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' type SearchENSAddressItemProps = { searchResult: ENSAddressSearchResult searchContext?: SearchContext } -export function SearchENSAddressItem({ - searchResult, - searchContext, -}: SearchENSAddressItemProps): JSX.Element { +export function SearchENSAddressItem({ searchResult, searchContext }: SearchENSAddressItemProps): JSX.Element { const { t } = useTranslation() // Use `savedPrimaryEnsName` for WalletSearchResults that are stored in the search history @@ -36,7 +33,7 @@ export function SearchENSAddressItem({ * is `uniswap.eth`, then we should show "uni.eth | owned by uniswap.eth" */ const { data: fetchedPrimaryENSName, loading: isFetchingPrimaryENSName } = useENSName( - savedPrimaryENSName ? undefined : address + savedPrimaryENSName ? undefined : address, ) const primaryENSName = savedPrimaryENSName ?? fetchedPrimaryENSName @@ -53,11 +50,7 @@ export function SearchENSAddressItem({ - + {completedENSName || formattedAddress} {showSecondLine ? ( diff --git a/apps/mobile/src/components/explore/search/items/SearchEtherscanItem.tsx b/apps/mobile/src/components/explore/search/items/SearchEtherscanItem.tsx index ef3674db679..8b0f5519e35 100644 --- a/apps/mobile/src/components/explore/search/items/SearchEtherscanItem.tsx +++ b/apps/mobile/src/components/explore/search/items/SearchEtherscanItem.tsx @@ -5,10 +5,10 @@ import { Flex, ImpactFeedbackStyle, Text, TouchableArea, useSporeColors } from ' import { iconSizes } from 'ui/src/theme' import { ElementName } from 'uniswap/src/features/telemetry/constants' import { UniverseChainId } from 'uniswap/src/types/chains' +import { shortenAddress } from 'uniswap/src/utils/addresses' import { Arrow } from 'wallet/src/components/icons/Arrow' import { EtherscanSearchResult } from 'wallet/src/features/search/SearchResult' import { addToSearchHistory } from 'wallet/src/features/search/searchHistorySlice' -import { shortenAddress } from 'wallet/src/utils/addresses' import { ExplorerDataType, getExplorerLink, openUri } from 'wallet/src/utils/linking' type SearchEtherscanItemProps = { @@ -27,7 +27,7 @@ export function SearchEtherscanItem({ etherscanResult }: SearchEtherscanItemProp dispatch( addToSearchHistory({ searchResult: etherscanResult, - }) + }), ) } @@ -38,14 +38,9 @@ export function SearchEtherscanItem({ etherscanResult }: SearchEtherscanItemProp hapticFeedback hapticStyle={ImpactFeedbackStyle.Light} testID={ElementName.SearchEtherscanItem} - onPress={onPressViewEtherscan}> - + onPress={onPressViewEtherscan} + > + {shortenAddress(address)} diff --git a/apps/mobile/src/components/explore/search/items/SearchNFTCollectionItem.tsx b/apps/mobile/src/components/explore/search/items/SearchNFTCollectionItem.tsx index 1a177387b64..a784af746da 100644 --- a/apps/mobile/src/components/explore/search/items/SearchNFTCollectionItem.tsx +++ b/apps/mobile/src/components/explore/search/items/SearchNFTCollectionItem.tsx @@ -9,10 +9,7 @@ import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { NFTViewer } from 'wallet/src/features/images/NFTViewer' import { SearchContext } from 'wallet/src/features/search/SearchContext' -import { - NFTCollectionSearchResult, - SearchResultType, -} from 'wallet/src/features/search/SearchResult' +import { NFTCollectionSearchResult, SearchResultType } from 'wallet/src/features/search/SearchResult' import { addToSearchHistory } from 'wallet/src/features/search/searchHistorySlice' type NFTCollectionItemProps = { @@ -20,10 +17,7 @@ type NFTCollectionItemProps = { searchContext?: SearchContext } -export function SearchNFTCollectionItem({ - collection, - searchContext, -}: NFTCollectionItemProps): JSX.Element { +export function SearchNFTCollectionItem({ collection, searchContext }: NFTCollectionItemProps): JSX.Element { const { name, address, chainId, isVerified, imageUrl } = collection const dispatch = useAppDispatch() const navigation = useAppStackNavigation() @@ -56,7 +50,7 @@ export function SearchNFTCollectionItem({ imageUrl, isVerified, }, - }) + }), ) } @@ -65,21 +59,17 @@ export function SearchNFTCollectionItem({ hapticFeedback hapticStyle={ImpactFeedbackStyle.Light} testID={ElementName.SearchNFTCollectionItem} - onPress={onPress}> - + onPress={onPress} + > + + width={iconSizes.icon40} + > {imageUrl ? ( ) : ( diff --git a/apps/mobile/src/components/explore/search/items/SearchTokenItem.tsx b/apps/mobile/src/components/explore/search/items/SearchTokenItem.tsx index 5bd969e104f..5e5bd11054d 100644 --- a/apps/mobile/src/components/explore/search/items/SearchTokenItem.tsx +++ b/apps/mobile/src/components/explore/search/items/SearchTokenItem.tsx @@ -6,15 +6,15 @@ import { useExploreTokenContextMenu } from 'src/components/explore/hooks' import { disableOnPress } from 'src/utils/disableOnPress' import { Flex, ImpactFeedbackStyle, Text, TouchableArea, useIsDarkMode } from 'ui/src' import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' +import WarningIcon from 'uniswap/src/components/icons/WarningIcon' import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { ElementName, MobileEventName, SectionName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' -import WarningIcon from 'wallet/src/components/icons/WarningIcon' +import { shortenAddress } from 'uniswap/src/utils/addresses' +import { buildCurrencyId, buildNativeCurrencyId } from 'uniswap/src/utils/currencyId' import { SearchContext } from 'wallet/src/features/search/SearchContext' import { SearchResultType, TokenSearchResult } from 'wallet/src/features/search/SearchResult' import { addToSearchHistory } from 'wallet/src/features/search/searchHistorySlice' -import { shortenAddress } from 'wallet/src/utils/addresses' -import { buildCurrencyId, buildNativeCurrencyId } from 'wallet/src/utils/currencyId' type SearchTokenItemProps = { token: TokenSearchResult @@ -55,7 +55,7 @@ export function SearchTokenItem({ token, searchContext }: SearchTokenItemProps): logoUrl, safetyLevel, }, - }) + }), ) } @@ -72,7 +72,8 @@ export function SearchTokenItem({ token, searchContext }: SearchTokenItemProps): hapticStyle={ImpactFeedbackStyle.Light} testID={ElementName.SearchTokenItem} onLongPress={disableOnPress} - onPress={onPress}> + onPress={onPress} + > @@ -82,13 +83,8 @@ export function SearchTokenItem({ token, searchContext }: SearchTokenItemProps): {name} - {(safetyLevel === SafetyLevel.Blocked || - safetyLevel === SafetyLevel.StrongWarning) && ( - + {(safetyLevel === SafetyLevel.Blocked || safetyLevel === SafetyLevel.StrongWarning) && ( + )} @@ -97,10 +93,7 @@ export function SearchTokenItem({ token, searchContext }: SearchTokenItemProps): {address && ( - + {shortenAddress(address)} diff --git a/apps/mobile/src/components/explore/search/items/SearchUnitagItem.tsx b/apps/mobile/src/components/explore/search/items/SearchUnitagItem.tsx index 4549e7761e6..3ec3d1ed0ea 100644 --- a/apps/mobile/src/components/explore/search/items/SearchUnitagItem.tsx +++ b/apps/mobile/src/components/explore/search/items/SearchUnitagItem.tsx @@ -2,23 +2,20 @@ import React from 'react' import { SearchWalletItemBase } from 'src/components/explore/search/items/SearchWalletItemBase' import { Flex, Text } from 'ui/src' import { imageSizes } from 'ui/src/theme' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText' import { SearchContext } from 'wallet/src/features/search/SearchContext' import { UnitagSearchResult } from 'wallet/src/features/search/SearchResult' import { useAvatar } from 'wallet/src/features/wallet/hooks' import { DisplayNameType } from 'wallet/src/features/wallet/types' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' type SearchUnitagItemProps = { searchResult: UnitagSearchResult searchContext?: SearchContext } -export function SearchUnitagItem({ - searchResult, - searchContext, -}: SearchUnitagItemProps): JSX.Element { +export function SearchUnitagItem({ searchResult, searchContext }: SearchUnitagItemProps): JSX.Element { const { address, unitag } = searchResult const { avatar } = useAvatar(address) @@ -29,11 +26,7 @@ export function SearchUnitagItem({ - + {sanitizeAddressText(shortenAddress(address))} diff --git a/apps/mobile/src/components/explore/search/items/SearchWalletByAddressItem.tsx b/apps/mobile/src/components/explore/search/items/SearchWalletByAddressItem.tsx index 5659c4b3fdf..ae682cd32f1 100644 --- a/apps/mobile/src/components/explore/search/items/SearchWalletByAddressItem.tsx +++ b/apps/mobile/src/components/explore/search/items/SearchWalletByAddressItem.tsx @@ -2,11 +2,11 @@ import React from 'react' import { SearchWalletItemBase } from 'src/components/explore/search/items/SearchWalletItemBase' import { Flex, Text } from 'ui/src' import { imageSizes } from 'ui/src/theme' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' import { useENSAvatar, useENSName } from 'wallet/src/features/ens/api' import { SearchContext } from 'wallet/src/features/search/SearchContext' import { WalletByAddressSearchResult } from 'wallet/src/features/search/SearchResult' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' type SearchWalletByAddressItemProps = { searchResult: WalletByAddressSearchResult @@ -27,11 +27,7 @@ export function SearchWalletByAddressItem({ - + {ensName || formattedAddress} {ensName ? ( diff --git a/apps/mobile/src/components/explore/search/items/SearchWalletItemBase.tsx b/apps/mobile/src/components/explore/search/items/SearchWalletItemBase.tsx index c6a0a3465b1..88c599857c8 100644 --- a/apps/mobile/src/components/explore/search/items/SearchWalletItemBase.tsx +++ b/apps/mobile/src/components/explore/search/items/SearchWalletItemBase.tsx @@ -10,11 +10,7 @@ import { MobileEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { selectWatchedAddressSet } from 'wallet/src/features/favorites/selectors' import { SearchContext } from 'wallet/src/features/search/SearchContext' -import { - SearchResultType, - WalletSearchResult, - extractDomain, -} from 'wallet/src/features/search/SearchResult' +import { SearchResultType, WalletSearchResult, extractDomain } from 'wallet/src/features/search/SearchResult' import { addToSearchHistory } from 'wallet/src/features/search/searchHistorySlice' type SearchWalletItemBaseProps = { @@ -40,8 +36,8 @@ export function SearchWalletItemBase({ type === SearchResultType.Unitag ? searchResult.unitag : type === SearchResultType.ENSAddress - ? searchResult.ensName - : undefined + ? searchResult.ensName + : undefined sendAnalyticsEvent(MobileEventName.ExploreSearchResultClicked, { query: searchContext.query, name: walletName, @@ -61,13 +57,13 @@ export function SearchWalletItemBase({ ...searchResult, primaryENSName: searchResult.primaryENSName, }, - }) + }), ) } else { dispatch( addToSearchHistory({ searchResult, - }) + }), ) } } @@ -90,7 +86,8 @@ export function SearchWalletItemBase({ onPress={onPress} onPressIn={async (): Promise => { await preload(address) - }}> + }} + > {children} diff --git a/apps/mobile/src/components/explore/search/utils.test.ts b/apps/mobile/src/components/explore/search/utils.test.ts index 623b26be059..5d5dbaa4937 100644 --- a/apps/mobile/src/components/explore/search/utils.test.ts +++ b/apps/mobile/src/components/explore/search/utils.test.ts @@ -8,7 +8,7 @@ import { Chain, ExploreSearchQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { SearchResultType } from 'wallet/src/features/search/SearchResult' import { amount, diff --git a/apps/mobile/src/components/explore/search/utils.ts b/apps/mobile/src/components/explore/search/utils.ts index 1e54e0c221f..fe79efdfb4d 100644 --- a/apps/mobile/src/components/explore/search/utils.ts +++ b/apps/mobile/src/components/explore/search/utils.ts @@ -1,15 +1,8 @@ import { SEARCH_RESULT_HEADER_KEY } from 'src/components/explore/search/constants' import { SearchResultOrHeader } from 'src/components/explore/search/types' -import { - Chain, - ExploreSearchQuery, -} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' -import { - NFTCollectionSearchResult, - SearchResultType, - TokenSearchResult, -} from 'wallet/src/features/search/SearchResult' +import { Chain, ExploreSearchQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' +import { NFTCollectionSearchResult, SearchResultType, TokenSearchResult } from 'wallet/src/features/search/SearchResult' import { searchResultId } from 'wallet/src/features/search/searchHistorySlice' const MAX_TOKEN_RESULTS_COUNT = 4 @@ -19,7 +12,7 @@ type ExploreSearchResult = NonNullable // Formats the tokens portion of explore search results into sorted array of TokenSearchResult export function formatTokenSearchResults( data: ExploreSearchResult['searchTokens'], - searchQuery: string + searchQuery: string, ): TokenSearchResult[] | undefined { if (!data) { return @@ -28,41 +21,38 @@ export function formatTokenSearchResults( // Prevent showing "duplicate" token search results for tokens that are on multiple chains // and share the same TokenProject id. Only show the token that has the highest 1Y Uniswap trading volume // ex. UNI on Mainnet, Arbitrum, Optimism -> only show UNI on Mainnet b/c it has highest 1Y volume - const tokenResultsMap = data.reduce>( - (tokensMap, token) => { - if (!token) { - return tokensMap - } + const tokenResultsMap = data.reduce>((tokensMap, token) => { + if (!token) { + return tokensMap + } - const { chain, address, symbol, project, market } = token - const chainId = fromGraphQLChain(chain) + const { chain, address, symbol, project, market } = token + const chainId = fromGraphQLChain(chain) - if (!chainId || !project) { - return tokensMap - } + if (!chainId || !project) { + return tokensMap + } - const { name, safetyLevel, logoUrl } = project - - const tokenResult: TokenSearchResult & { volume1D: number } = { - type: SearchResultType.Token, - chainId, - address: address ?? null, - name: name ?? null, - symbol: symbol ?? '', - safetyLevel: safetyLevel ?? null, - logoUrl: logoUrl ?? null, - volume1D: market?.volume?.value ?? 0, - } + const { name, safetyLevel, logoUrl } = project - // For token results that share the same TokenProject id, use the token with highest volume - const currentTokenResult = tokensMap[project.id] - if (!currentTokenResult || tokenResult.volume1D > currentTokenResult.volume1D) { - tokensMap[project.id] = tokenResult - } - return tokensMap - }, - {} - ) + const tokenResult: TokenSearchResult & { volume1D: number } = { + type: SearchResultType.Token, + chainId, + address: address ?? null, + name: name ?? null, + symbol: symbol ?? '', + safetyLevel: safetyLevel ?? null, + logoUrl: logoUrl ?? null, + volume1D: market?.volume?.value ?? 0, + } + + // For token results that share the same TokenProject id, use the token with highest volume + const currentTokenResult = tokensMap[project.id] + if (!currentTokenResult || tokenResult.volume1D > currentTokenResult.volume1D) { + tokensMap[project.id] = tokenResult + } + return tokensMap + }, {}) return Object.values(tokenResultsMap) .slice(0, MAX_TOKEN_RESULTS_COUNT) @@ -88,7 +78,7 @@ function isExactTokenSearchResultMatch(searchResult: TokenSearchResult, query: s } export function formatNFTCollectionSearchResults( - data: ExploreSearchResult['nftCollections'] + data: ExploreSearchResult['nftCollections'], ): NFTCollectionSearchResult[] | undefined { if (!data) { return @@ -107,9 +97,7 @@ type NFTCollectionItemResult = NonNullable< NonNullable>['edges']>[0] >['node'] -export const gqlNFTToNFTCollectionSearchResult = ( - node: NFTCollectionItemResult -): NFTCollectionSearchResult | null => { +export const gqlNFTToNFTCollectionSearchResult = (node: NFTCollectionItemResult): NFTCollectionSearchResult | null => { const contract = node?.nftContracts?.[0] // Only show NFT results that have fully populated results const chainId = fromGraphQLChain(contract?.chain ?? Chain.Ethereum) diff --git a/apps/mobile/src/components/fiatOnRamp/CtaButton.tsx b/apps/mobile/src/components/fiatOnRamp/CtaButton.tsx index 63453f93bc1..af9601bc38b 100644 --- a/apps/mobile/src/components/fiatOnRamp/CtaButton.tsx +++ b/apps/mobile/src/components/fiatOnRamp/CtaButton.tsx @@ -34,7 +34,8 @@ export function FiatOnRampCtaButton({ } size="large" theme={buttonAvailable ? 'primary' : 'tertiary'} - onPress={onPress}> + onPress={onPress} + > {!isLoading && continueText} ) diff --git a/apps/mobile/src/components/forceUpgrade/ForceUpgradeModal.tsx b/apps/mobile/src/components/forceUpgrade/ForceUpgradeModal.tsx index 8a17dfbfb3f..8a25880442c 100644 --- a/apps/mobile/src/components/forceUpgrade/ForceUpgradeModal.tsx +++ b/apps/mobile/src/components/forceUpgrade/ForceUpgradeModal.tsx @@ -25,10 +25,7 @@ export function ForceUpgradeModal(): JSX.Element { // signerAccounts could be empty if no seed phrase imported or in onboarding const signerAccounts = useSignerAccounts() - const mnemonicId = - signerAccounts.length > 0 - ? (signerAccounts?.[0] as SignerMnemonicAccount)?.mnemonicId - : undefined + const mnemonicId = signerAccounts.length > 0 ? (signerAccounts?.[0] as SignerMnemonicAccount)?.mnemonicId : undefined const [showSeedPhrase, setShowSeedPhrase] = useState(false) @@ -72,7 +69,8 @@ export function ForceUpgradeModal(): JSX.Element { severity={WarningSeverity.High} title={t('forceUpgrade.title')} onClose={onClose} - onConfirm={onPressConfirm}> + onConfirm={onPressConfirm} + > {t('forceUpgrade.description')} @@ -88,7 +86,8 @@ export function ForceUpgradeModal(): JSX.Element { fullScreen backgroundColor={colors.surface1.get()} name={ModalName.ForceUpgradeModal} - onClose={onDismiss}> + onClose={onDismiss} + > diff --git a/apps/mobile/src/components/gradients/GradientBackground.tsx b/apps/mobile/src/components/gradients/GradientBackground.tsx index 3f348c9abec..fb91b7a5520 100644 --- a/apps/mobile/src/components/gradients/GradientBackground.tsx +++ b/apps/mobile/src/components/gradients/GradientBackground.tsx @@ -5,14 +5,7 @@ import { zIndices } from 'ui/src/theme' // Fills up entire parent by default export function GradientBackground({ children, ...rest }: FlexProps): JSX.Element { return ( - + {children} ) diff --git a/apps/mobile/src/components/home/ActivityTab.tsx b/apps/mobile/src/components/home/ActivityTab.tsx index 6e5f25c4fa6..f135a6a5e04 100644 --- a/apps/mobile/src/components/home/ActivityTab.tsx +++ b/apps/mobile/src/components/home/ActivityTab.tsx @@ -3,10 +3,7 @@ import { FlatList, RefreshControl } from 'react-native' import Animated from 'react-native-reanimated' import { useAppDispatch } from 'src/app/hooks' import { useAdaptiveFooter } from 'src/components/home/hooks' -import { - AnimatedBottomSheetFlatList, - AnimatedFlatList, -} from 'src/components/layout/AnimatedFlatList' +import { AnimatedBottomSheetFlatList, AnimatedFlatList } from 'src/components/layout/AnimatedFlatList' import { TAB_BAR_HEIGHT, TabProps } from 'src/components/layout/TabHelpers' import { useBiometricAppSettings, useBiometricPrompt } from 'src/features/biometrics/hooks' import { openModal } from 'src/features/modals/modalSlice' @@ -34,7 +31,7 @@ export const ActivityTab = memo( refreshing, onRefresh, }, - ref + ref, ) { const dispatch = useAppDispatch() const colors = useSporeColors() @@ -43,38 +40,27 @@ export const ActivityTab = memo( const { trigger: biometricsTrigger } = useBiometricPrompt() const { requiredForTransactions: requiresBiometrics } = useBiometricAppSettings() - const { onContentSizeChange, adaptiveFooter } = useAdaptiveFooter( - containerProps?.contentContainerStyle - ) + const { onContentSizeChange, adaptiveFooter } = useAdaptiveFooter(containerProps?.contentContainerStyle) const onPressReceive = (): void => { // in case we received a pending session from a previous scan after closing modal dispatch(removePendingSession()) - dispatch( - openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }) - ) + dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })) } - const { - maybeLoaderComponent, - maybeEmptyComponent, - renderActivityItem, - sectionData, - keyExtractor, - } = useActivityData({ - owner, - authTrigger: requiresBiometrics ? biometricsTrigger : undefined, - isExternalProfile, - emptyComponentStyle: containerProps?.emptyComponentStyle, - onPressEmptyState: onPressReceive, - }) + const { maybeLoaderComponent, maybeEmptyComponent, renderActivityItem, sectionData, keyExtractor } = + useActivityData({ + owner, + authTrigger: requiresBiometrics ? biometricsTrigger : undefined, + isExternalProfile, + emptyComponentStyle: containerProps?.emptyComponentStyle, + onPressEmptyState: onPressReceive, + }) const refreshControl = useMemo(() => { return ( ) - }) + }), ) diff --git a/apps/mobile/src/components/home/FeedTab.tsx b/apps/mobile/src/components/home/FeedTab.tsx index 560f8f43617..a4084f50641 100644 --- a/apps/mobile/src/components/home/FeedTab.tsx +++ b/apps/mobile/src/components/home/FeedTab.tsx @@ -37,7 +37,7 @@ const SectionTitle = ({ title }: { title: string }): JSX.Element => ( export const FeedTab = memo( forwardRef, TabProps>(function _FeedTab( { containerProps, scrollHandler, headerHeight, refreshing, onRefresh }, - ref + ref, ) { const { t } = useTranslation() const dispatch = useAppDispatch() @@ -58,19 +58,19 @@ export const FeedTab = memo( , SectionTitle, undefined, - undefined + undefined, ) }, []) - const { onRetry, hasData, isLoading, isError, sectionData, keyExtractor } = - useFormattedTransactionDataForFeed(watchedWalletsList, hideSpamTokens) + const { onRetry, hasData, isLoading, isError, sectionData, keyExtractor } = useFormattedTransactionDataForFeed( + watchedWalletsList, + hideSpamTokens, + ) const onPressReceive = (): void => { // in case we received a pending session from a previous scan after closing modal dispatch(removePendingSession()) - dispatch( - openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }) - ) + dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })) } const errorCard = ( @@ -104,9 +104,7 @@ export const FeedTab = memo( const refreshControl = useMemo(() => { return ( ) - }) + }), ) diff --git a/apps/mobile/src/components/home/NftsTab.tsx b/apps/mobile/src/components/home/NftsTab.tsx index fa8ee6f905a..f4a8e185a31 100644 --- a/apps/mobile/src/components/home/NftsTab.tsx +++ b/apps/mobile/src/components/home/NftsTab.tsx @@ -26,14 +26,14 @@ export const NftsTab = memo( headerHeight = 0, renderedInModal = false, }, - ref + ref, ) { const colors = useSporeColors() const insets = useDeviceInsets() const navigation = useAppStackNavigation() const { onContentSizeChange, footerHeight, adaptiveFooter } = useAdaptiveFooter( - containerProps?.contentContainerStyle + containerProps?.contentContainerStyle, ) const renderNFTItem = useCallback( @@ -50,15 +50,13 @@ export const NftsTab = memo( return }, - [owner, navigation] + [owner, navigation], ) const refreshControl = useMemo(() => { return ( ) - }) + }), ) diff --git a/apps/mobile/src/components/home/TokensTab.tsx b/apps/mobile/src/components/home/TokensTab.tsx index 06781de569d..df935ae53d1 100644 --- a/apps/mobile/src/components/home/TokensTab.tsx +++ b/apps/mobile/src/components/home/TokensTab.tsx @@ -26,105 +26,97 @@ export const TOKENS_TAB_DATA_DEPENDENCIES = [GQLQueries.PortfolioBalances] // ignore ref type export const TokensTab = memo( - forwardRef, TabProps & { isExternalProfile?: boolean }>( - function _TokensTab( - { - owner, - containerProps, - scrollHandler, - isExternalProfile = false, - renderedInModal = false, - onRefresh, - refreshing, - headerHeight, - }, - ref - ) { - const { t } = useTranslation() - const dispatch = useAppDispatch() - const tokenDetailsNavigation = useTokenDetailsNavigation() - const startProfilerTimer = useStartProfiler() - const forAggregatorEnabled = useFeatureFlag(FeatureFlags.ForAggregator) - const cexTransferEnabled = useFeatureFlag(FeatureFlags.CexTransfers) - const cexTransferProviders = useCexTransferProviders(cexTransferEnabled) - - const onPressToken = useCallback( - (currencyId: CurrencyId): void => { - startProfilerTimer({ source: MobileScreens.Home }) - tokenDetailsNavigation.navigate(currencyId) - }, - [startProfilerTimer, tokenDetailsNavigation] - ) + forwardRef, TabProps & { isExternalProfile?: boolean }>(function _TokensTab( + { + owner, + containerProps, + scrollHandler, + isExternalProfile = false, + renderedInModal = false, + onRefresh, + refreshing, + headerHeight, + }, + ref, + ) { + const { t } = useTranslation() + const dispatch = useAppDispatch() + const tokenDetailsNavigation = useTokenDetailsNavigation() + const startProfilerTimer = useStartProfiler() + const forAggregatorEnabled = useFeatureFlag(FeatureFlags.ForAggregator) + const cexTransferEnabled = useFeatureFlag(FeatureFlags.CexTransfers) + const cexTransferProviders = useCexTransferProviders(cexTransferEnabled) - const onPressAction = useCallback((): void => { - dispatch( - openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }) - ) - }, [dispatch]) + const onPressToken = useCallback( + (currencyId: CurrencyId): void => { + startProfilerTimer({ source: MobileScreens.Home }) + tokenDetailsNavigation.navigate(currencyId) + }, + [startProfilerTimer, tokenDetailsNavigation], + ) - const onPressBuy = useCallback(() => { - dispatch( - openModal({ - name: forAggregatorEnabled ? ModalName.FiatOnRampAggregator : ModalName.FiatOnRamp, - }) - ) - }, [dispatch, forAggregatorEnabled]) + const onPressAction = useCallback((): void => { + dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })) + }, [dispatch]) - const onPressReceive = useCallback(() => { - dispatch( - openModal( - cexTransferProviders.length > 0 - ? { - name: ModalName.ReceiveCryptoModal, - initialState: cexTransferProviders, - } - : { - name: ModalName.WalletConnectScan, - initialState: ScannerModalState.WalletQr, - } - ) - ) - }, [cexTransferProviders, dispatch]) + const onPressBuy = useCallback(() => { + dispatch( + openModal({ + name: forAggregatorEnabled ? ModalName.FiatOnRampAggregator : ModalName.FiatOnRamp, + }), + ) + }, [dispatch, forAggregatorEnabled]) - const onPressImport = useCallback(() => { - dispatch(openModal({ name: ModalName.AccountSwitcher })) - }, [dispatch]) + const onPressReceive = useCallback(() => { + dispatch( + openModal( + cexTransferProviders.length > 0 + ? { + name: ModalName.ReceiveCryptoModal, + initialState: cexTransferProviders, + } + : { + name: ModalName.WalletConnectScan, + initialState: ScannerModalState.WalletQr, + }, + ), + ) + }, [cexTransferProviders, dispatch]) - const renderEmpty = useMemo((): JSX.Element => { - // Show different empty state on external profile pages - return isExternalProfile ? ( - } - title={t('home.tokens.empty.title')} - onPress={onPressAction} - /> - ) : ( - - ) - }, [isExternalProfile, onPressAction, onPressBuy, onPressImport, onPressReceive, t]) + const onPressImport = useCallback(() => { + dispatch(openModal({ name: ModalName.AccountSwitcher })) + }, [dispatch]) - return ( - - - + const renderEmpty = useMemo((): JSX.Element => { + // Show different empty state on external profile pages + return isExternalProfile ? ( + } + title={t('home.tokens.empty.title')} + onPress={onPressAction} + /> + ) : ( + ) - } - ) + }, [isExternalProfile, onPressAction, onPressBuy, onPressImport, onPressReceive, t]) + + return ( + + + + ) + }), ) diff --git a/apps/mobile/src/components/home/hooks.tsx b/apps/mobile/src/components/home/hooks.tsx index dcbf7cacb2a..16764ff2b39 100644 --- a/apps/mobile/src/components/home/hooks.tsx +++ b/apps/mobile/src/components/home/hooks.tsx @@ -46,7 +46,7 @@ export function useAdaptiveFooter(contentContainerStyle?: StyleProp): footerHeight.value = Math.max(0, calculatedFooterHeight) }, - [footerHeight, contentContainerStyle, maxContentHeight] + [footerHeight, contentContainerStyle, maxContentHeight], ) useEffect(() => { diff --git a/apps/mobile/src/components/icons/BiometricsIcon.tsx b/apps/mobile/src/components/icons/BiometricsIcon.tsx index f6c52bb69c7..cf476b11daa 100644 --- a/apps/mobile/src/components/icons/BiometricsIcon.tsx +++ b/apps/mobile/src/components/icons/BiometricsIcon.tsx @@ -2,8 +2,7 @@ import { useDeviceSupportsBiometricAuth } from 'src/features/biometrics/hooks' import { Faceid, Fingerprint } from 'ui/src/components/icons' export function BiometricsIcon(): JSX.Element | null { - const { touchId: isTouchIdSupported, faceId: isFaceIdSupported } = - useDeviceSupportsBiometricAuth() + const { touchId: isTouchIdSupported, faceId: isFaceIdSupported } = useDeviceSupportsBiometricAuth() if (isTouchIdSupported) { return diff --git a/apps/mobile/src/components/icons/BlockExplorerIcon.tsx b/apps/mobile/src/components/icons/BlockExplorerIcon.tsx index f490c15901a..44651ad7cc7 100644 --- a/apps/mobile/src/components/icons/BlockExplorerIcon.tsx +++ b/apps/mobile/src/components/icons/BlockExplorerIcon.tsx @@ -16,11 +16,7 @@ function buildIconComponent(chainId: WalletChainId): React.FC { const isDarkMode = useIsDarkMode() - return isDarkMode ? ( - - ) : ( - - ) + return isDarkMode ? : } Component.displayName = `BlockExplorerIcon_${explorer.name}` iconsCache.set(chainId, Component) diff --git a/apps/mobile/src/components/icons/Favorite.tsx b/apps/mobile/src/components/icons/Favorite.tsx index f4bae471824..1dbf83c0b4d 100644 --- a/apps/mobile/src/components/icons/Favorite.tsx +++ b/apps/mobile/src/components/icons/Favorite.tsx @@ -1,10 +1,5 @@ import React, { useCallback, useEffect, useState } from 'react' -import { - useAnimatedStyle, - useDerivedValue, - withSequence, - withTiming, -} from 'react-native-reanimated' +import { useAnimatedStyle, useDerivedValue, withSequence, withTiming } from 'react-native-reanimated' import { useSporeColors } from 'ui/src' import HeartIcon from 'ui/src/assets/icons/heart.svg' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' @@ -23,7 +18,7 @@ export const Favorite = ({ isFavorited, size }: FavoriteButtonProps): JSX.Elemen const getColor = useCallback( () => (isFavorited ? colors.accent1.val : unfilledColor), - [isFavorited, colors.accent1, unfilledColor] + [isFavorited, colors.accent1, unfilledColor], ) const [color, setColor] = useState(getColor()) diff --git a/apps/mobile/src/components/input/PasswordInput.tsx b/apps/mobile/src/components/input/PasswordInput.tsx index f970e3eac34..14a73d962e4 100644 --- a/apps/mobile/src/components/input/PasswordInput.tsx +++ b/apps/mobile/src/components/input/PasswordInput.tsx @@ -7,10 +7,7 @@ import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { iconSizes } from 'ui/src/theme' import { TextInput, TextInputProps } from 'uniswap/src/components/input/TextInput' -export const PasswordInput = forwardRef(function _PasswordInput( - props, - ref -) { +export const PasswordInput = forwardRef(function _PasswordInput(props, ref) { const colors = useSporeColors() const [showPassword, setShowPassword] = useState(false) @@ -28,7 +25,8 @@ export const PasswordInput = forwardRef(functio borderColor="$surface3" borderRadius="$rounded16" borderWidth={1} - p="$spacing4"> + p="$spacing4" + > (functio {showPassword ? ( - + ) : ( - + )} diff --git a/apps/mobile/src/components/input/SelectionCircle.tsx b/apps/mobile/src/components/input/SelectionCircle.tsx index f4fefc40c53..65fd8fc5c27 100644 --- a/apps/mobile/src/components/input/SelectionCircle.tsx +++ b/apps/mobile/src/components/input/SelectionCircle.tsx @@ -22,7 +22,8 @@ export function SelectionCircle({ borderRadius="$roundedFull" borderWidth={1} height={iconSizes[size]} - width={iconSizes[size]}> + width={iconSizes[size]} + > void @@ -49,30 +47,26 @@ interface ReanimatedFlatlistProps extends FlatListProps { * TODO: [MOB-207] remove this and use Animated.FlatList directly when can use refs with it. Also type the generic T properly for FlatList and dont use `any` */ export const AnimatedFlatList = forwardRef, ReanimatedFlatlistProps>( - function _AnimatedFlatList( - { itemLayoutAnimation, FlatListComponent = ReanimatedFlatList, ...restProps }, - ref - ) { + function _AnimatedFlatList({ itemLayoutAnimation, FlatListComponent = ReanimatedFlatList, ...restProps }, ref) { // eslint-disable-next-line react-hooks/exhaustive-deps const cellRenderer = React.useMemo(() => createCellRenderer(itemLayoutAnimation), []) return - } + }, ) /** * In bottom sheet contexts, this will support pull to dismiss. * See AnimatedFlatList for other props. */ -export const AnimatedBottomSheetFlatList = forwardRef< - Animated.FlatList, - ReanimatedFlatlistProps ->(function _AnimatedBottomSheetFlatList(props, ref) { - return ( - - ) -}) +export const AnimatedBottomSheetFlatList = forwardRef, ReanimatedFlatlistProps>( + function _AnimatedBottomSheetFlatList(props, ref) { + return ( + + ) + }, +) diff --git a/apps/mobile/src/components/layout/BackHeader.tsx b/apps/mobile/src/components/layout/BackHeader.tsx index 1f76adf589d..0b43cc7b18b 100644 --- a/apps/mobile/src/components/layout/BackHeader.tsx +++ b/apps/mobile/src/components/layout/BackHeader.tsx @@ -23,7 +23,8 @@ export function BackHeader({ alignItems="center" justifyContent={alignment === 'left' ? 'flex-start' : 'space-between'} sentry-label="BackHeader" - {...spacingProps}> + {...spacingProps} + > {children} {endAdornment} diff --git a/apps/mobile/src/components/layout/Delayed.tsx b/apps/mobile/src/components/layout/Delayed.tsx index dc5446a070b..04c8b4460c5 100644 --- a/apps/mobile/src/components/layout/Delayed.tsx +++ b/apps/mobile/src/components/layout/Delayed.tsx @@ -13,10 +13,7 @@ type Props = { } /** HOC to delay rendering a component by some time in ms. */ -export const Delayed = ({ - children, - waitBeforeShow = Delay.Short, -}: PropsWithChildren): JSX.Element | null => { +export const Delayed = ({ children, waitBeforeShow = Delay.Short }: PropsWithChildren): JSX.Element | null => { const [isShown, setIsShown] = useReducer(() => true, false) useTimeout(setIsShown, waitBeforeShow) diff --git a/apps/mobile/src/components/layout/Screen.tsx b/apps/mobile/src/components/layout/Screen.tsx index e027d5cfd29..2e88c90432a 100644 --- a/apps/mobile/src/components/layout/Screen.tsx +++ b/apps/mobile/src/components/layout/Screen.tsx @@ -60,11 +60,7 @@ function SafeAreaWithInsets({ children, edges, noInsets, ...rest }: ScreenProps) ) } -export function Screen({ - backgroundColor = '$surface1', - children, - ...rest -}: ScreenProps): JSX.Element { +export function Screen({ backgroundColor = '$surface1', children, ...rest }: ScreenProps): JSX.Element { return ( {children} diff --git a/apps/mobile/src/components/layout/TabHelpers.tsx b/apps/mobile/src/components/layout/TabHelpers.tsx index 3a6a84da877..36a0f603484 100644 --- a/apps/mobile/src/components/layout/TabHelpers.tsx +++ b/apps/mobile/src/components/layout/TabHelpers.tsx @@ -112,9 +112,7 @@ export const TabLabel = ({ {/* Streamline UI by hiding the Activity tab spinner when focused and showing it only on the specific pending transactions. */} - {route.title === 'Activity' && !isExternalProfile && !focused ? ( - - ) : null} + {route.title === 'Activity' && !isExternalProfile && !focused ? : null} ) } @@ -125,35 +123,34 @@ export const TabLabel = ({ export const useScrollSync = ( currentTabIndex: SharedValue, scrollPairs: ScrollPair[], - headerConfig: HeaderConfig + headerConfig: HeaderConfig, ): { sync: (event: NativeSyntheticEvent) => void } => { - const sync: - | FlatListProps['onMomentumScrollEnd'] - | FlashListProps['onMomentumScrollEnd'] = useCallback( - (event: { nativeEvent: NativeScrollEvent }) => { - const { y } = event.nativeEvent.contentOffset + const sync: FlatListProps['onMomentumScrollEnd'] | FlashListProps['onMomentumScrollEnd'] = + useCallback( + (event: { nativeEvent: NativeScrollEvent }) => { + const { y } = event.nativeEvent.contentOffset - const { heightCollapsed, heightExpanded } = headerConfig + const { heightCollapsed, heightExpanded } = headerConfig - const headerDiff = heightExpanded - heightCollapsed + const headerDiff = heightExpanded - heightCollapsed - for (const { list, position, index } of scrollPairs) { - const scrollPosition = position.value + for (const { list, position, index } of scrollPairs) { + const scrollPosition = position.value - if (scrollPosition > headerDiff && y > headerDiff) { - continue - } + if (scrollPosition > headerDiff && y > headerDiff) { + continue + } - if (index !== currentTabIndex.value) { - list.current?.scrollToOffset({ - offset: Math.min(y, headerDiff), - animated: false, - }) + if (index !== currentTabIndex.value) { + list.current?.scrollToOffset({ + offset: Math.min(y, headerDiff), + animated: false, + }) + } } - } - }, - [currentTabIndex, scrollPairs, headerConfig] - ) + }, + [currentTabIndex, scrollPairs, headerConfig], + ) return useMemo(() => ({ sync }), [sync]) } diff --git a/apps/mobile/src/components/layout/VirtualizedList.tsx b/apps/mobile/src/components/layout/VirtualizedList.tsx index 326fb485ac2..f5a009dc9ee 100644 --- a/apps/mobile/src/components/layout/VirtualizedList.tsx +++ b/apps/mobile/src/components/layout/VirtualizedList.tsx @@ -1,8 +1,5 @@ import React, { ComponentProps, PropsWithChildren } from 'react' -import { - AnimatedBottomSheetFlatList, - AnimatedFlatList, -} from 'src/components/layout/AnimatedFlatList' +import { AnimatedBottomSheetFlatList, AnimatedFlatList } from 'src/components/layout/AnimatedFlatList' type VirtualizedListProps = PropsWithChildren>> & { renderedInModal?: boolean @@ -10,23 +7,24 @@ type VirtualizedListProps = PropsWithChildren( - function _VirtualizedList({ children, renderedInModal, ...props }: VirtualizedListProps, ref) { - const List = renderedInModal ? AnimatedBottomSheetFlatList : AnimatedFlatList - return ( - {children}} - data={[]} - keyExtractor={(): string => 'key'} - keyboardShouldPersistTaps="always" - renderItem={null} - scrollEventThrottle={16} - showsHorizontalScrollIndicator={false} - showsVerticalScrollIndicator={false} - /> - ) - } -) +export const VirtualizedList = React.forwardRef(function _VirtualizedList( + { children, renderedInModal, ...props }: VirtualizedListProps, + ref, +) { + const List = renderedInModal ? AnimatedBottomSheetFlatList : AnimatedFlatList + return ( + {children}} + data={[]} + keyExtractor={(): string => 'key'} + keyboardShouldPersistTaps="always" + renderItem={null} + scrollEventThrottle={16} + showsHorizontalScrollIndicator={false} + showsVerticalScrollIndicator={false} + /> + ) +}) diff --git a/apps/mobile/src/components/layout/screens/HeaderScrollScreen.tsx b/apps/mobile/src/components/layout/screens/HeaderScrollScreen.tsx index 8ef7d767bc6..7cbafe79b3a 100644 --- a/apps/mobile/src/components/layout/screens/HeaderScrollScreen.tsx +++ b/apps/mobile/src/components/layout/screens/HeaderScrollScreen.tsx @@ -57,10 +57,7 @@ export function HeaderScrollScreen({ }) return ( - + {showHandleBar ? : null} + onScroll={scrollHandler} + > {children} diff --git a/apps/mobile/src/components/layout/screens/ScrollHeader.tsx b/apps/mobile/src/components/layout/screens/ScrollHeader.tsx index 3efccd01322..f8fb8275d45 100644 --- a/apps/mobile/src/components/layout/screens/ScrollHeader.tsx +++ b/apps/mobile/src/components/layout/screens/ScrollHeader.tsx @@ -1,12 +1,7 @@ import { useScrollToTop } from '@react-navigation/native' import React, { ReactElement, useMemo } from 'react' import { StyleProp, ViewStyle } from 'react-native' -import Animated, { - Extrapolate, - SharedValue, - interpolate, - useAnimatedStyle, -} from 'react-native-reanimated' +import Animated, { Extrapolate, SharedValue, interpolate, useAnimatedStyle } from 'react-native-reanimated' import { BackButton } from 'src/components/buttons/BackButton' import { WithScrollToTop } from 'src/components/layout/screens/WithScrollToTop' import { ColorTokens, Flex, useDeviceInsets } from 'ui/src' @@ -50,12 +45,7 @@ export function ScrollHeader({ const visibleOnScrollStyle = useAnimatedStyle(() => { return { - opacity: interpolate( - scrollY.value, - [0, showHeaderScrollYDistance], - [0, 1], - Extrapolate.CLAMP - ), + opacity: interpolate(scrollY.value, [0, showHeaderScrollYDistance], [0, 1], Extrapolate.CLAMP), } }) @@ -71,10 +61,7 @@ export function ScrollHeader({ const headerWrapperStyles = fullScreen ? [visibleOnScrollStyle, { zIndex: zIndices.popover }] : [] return ( - + + style={headerRowStyles} + > {alwaysShowCenterElement ? ( @@ -128,7 +116,8 @@ function HeaderWrapper({ position="absolute" right={0} style={style} - top={0}> + top={0} + > {children} ) diff --git a/apps/mobile/src/components/layout/screens/WithScrollToTop.tsx b/apps/mobile/src/components/layout/screens/WithScrollToTop.tsx index 7b1982bebd8..8b8e038cb56 100644 --- a/apps/mobile/src/components/layout/screens/WithScrollToTop.tsx +++ b/apps/mobile/src/components/layout/screens/WithScrollToTop.tsx @@ -3,15 +3,16 @@ import { Pressable } from 'react-native' // accept any ref // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const WithScrollToTop = forwardRef>( - function _WithScrollToTop({ children }: PropsWithChildren, ref) { - const onPress = (): void => { - if (!ref || typeof ref === 'function') { - return - } - ref.current.scrollToOffset({ animated: true, offset: 0 }) +export const WithScrollToTop = forwardRef>(function _WithScrollToTop( + { children }: PropsWithChildren, + ref, +) { + const onPress = (): void => { + if (!ref || typeof ref === 'function') { + return } - - return {children} + ref.current.scrollToOffset({ animated: true, offset: 0 }) } -) + + return {children} +}) diff --git a/apps/mobile/src/components/loading/TransactionLoader.tsx b/apps/mobile/src/components/loading/TransactionLoader.tsx index 8faac9787e5..1fc37674a20 100644 --- a/apps/mobile/src/components/loading/TransactionLoader.tsx +++ b/apps/mobile/src/components/loading/TransactionLoader.tsx @@ -9,20 +9,8 @@ interface TransactionLoaderProps { export function TransactionLoader({ opacity }: TransactionLoaderProps): JSX.Element { return ( - - + + - + diff --git a/apps/mobile/src/components/loading/WaveLoader.tsx b/apps/mobile/src/components/loading/WaveLoader.tsx index 8da33555d80..93ebddc6de8 100644 --- a/apps/mobile/src/components/loading/WaveLoader.tsx +++ b/apps/mobile/src/components/loading/WaveLoader.tsx @@ -35,13 +35,7 @@ export function WaveLoader(): JSX.Element { })) return ( - + diff --git a/apps/mobile/src/components/loading/index.tsx b/apps/mobile/src/components/loading/index.tsx index 07071ea921f..88f585805a7 100644 --- a/apps/mobile/src/components/loading/index.tsx +++ b/apps/mobile/src/components/loading/index.tsx @@ -11,11 +11,7 @@ function Graph(): JSX.Element { ) } -export const Transaction = memo(function _Transaction({ - repeat = 1, -}: { - repeat?: number -}): JSX.Element { +export const Transaction = memo(function _Transaction({ repeat = 1 }: { repeat?: number }): JSX.Element { return ( diff --git a/apps/mobile/src/components/mnemonic/HiddenMnemonicWordView.tsx b/apps/mobile/src/components/mnemonic/HiddenMnemonicWordView.tsx index d45b83bef5b..2f25f72e983 100644 --- a/apps/mobile/src/components/mnemonic/HiddenMnemonicWordView.tsx +++ b/apps/mobile/src/components/mnemonic/HiddenMnemonicWordView.tsx @@ -14,7 +14,8 @@ export function HiddenMnemonicWordView(): JSX.Element { height="40%" mt="$spacing16" px="$spacing32" - py="$spacing24"> + py="$spacing24" + > diff --git a/apps/mobile/src/components/mnemonic/MnemonicConfirmation.tsx b/apps/mobile/src/components/mnemonic/MnemonicConfirmation.tsx index c84fd27653e..3699d56824c 100644 --- a/apps/mobile/src/components/mnemonic/MnemonicConfirmation.tsx +++ b/apps/mobile/src/components/mnemonic/MnemonicConfirmation.tsx @@ -12,8 +12,7 @@ interface NativeMnemonicConfirmationProps { onConfirmComplete: () => void } -const NativeMnemonicConfirmation = - requireNativeComponent('MnemonicConfirmation') +const NativeMnemonicConfirmation = requireNativeComponent('MnemonicConfirmation') type MnemonicConfirmationProps = ViewProps & { mnemonicId: Address diff --git a/apps/mobile/src/components/mnemonic/MnemonicDisplay.tsx b/apps/mobile/src/components/mnemonic/MnemonicDisplay.tsx index 275fca82caa..e59084e0871 100644 --- a/apps/mobile/src/components/mnemonic/MnemonicDisplay.tsx +++ b/apps/mobile/src/components/mnemonic/MnemonicDisplay.tsx @@ -53,7 +53,8 @@ export function MnemonicDisplay(props: MnemonicDisplayProps): JSX.Element { // until the height is measured display={height ? 'flex' : 'none'} gap="$spacing8" - p="$spacing16"> + p="$spacing16" + > diff --git a/apps/mobile/src/components/mnemonic/SeedPhraseDisplay.tsx b/apps/mobile/src/components/mnemonic/SeedPhraseDisplay.tsx index 5c028645e3e..f3263166f28 100644 --- a/apps/mobile/src/components/mnemonic/SeedPhraseDisplay.tsx +++ b/apps/mobile/src/components/mnemonic/SeedPhraseDisplay.tsx @@ -17,18 +17,12 @@ type Props = { walletNeedsRestore?: boolean } -export function SeedPhraseDisplay({ - mnemonicId, - onDismiss, - walletNeedsRestore, -}: Props): JSX.Element { +export function SeedPhraseDisplay({ mnemonicId, onDismiss, walletNeedsRestore }: Props): JSX.Element { const { t } = useTranslation() const { isModalOpen: isWalletRestoreModalOpen } = useWalletRestore({ openModalImmediately: true }) const [showScreenShotWarningModal, setShowScreenShotWarningModal] = useState(false) const [showSeedPhrase, setShowSeedPhrase] = useState(false) - const [showSeedPhraseViewWarningModal, setShowSeedPhraseViewWarningModal] = useState( - !walletNeedsRestore - ) + const [showSeedPhraseViewWarningModal, setShowSeedPhraseViewWarningModal] = useState(!walletNeedsRestore) const prevIsWalletRestoreModalOpen = usePrevious(isWalletRestoreModalOpen) @@ -74,13 +68,8 @@ export function SeedPhraseDisplay({ )} - diff --git a/apps/mobile/src/components/modals/Modal.tsx b/apps/mobile/src/components/modals/Modal.tsx index 613cdae00b6..3b9e3c4f365 100644 --- a/apps/mobile/src/components/modals/Modal.tsx +++ b/apps/mobile/src/components/modals/Modal.tsx @@ -43,17 +43,20 @@ Props): JSX.Element { animationType={animationType} presentationStyle={presentationStyle} transparent={transparent} /* {...rest} */ - visible={visible}> + visible={visible} + > + onPress={dismissable ? hide : undefined} + > + width={width} + > {title && ( {title} diff --git a/apps/mobile/src/components/sortableGrid/SortableGirdProvider.tsx b/apps/mobile/src/components/sortableGrid/SortableGirdProvider.tsx index f6a9b5afa19..d0a8bd174a8 100644 --- a/apps/mobile/src/components/sortableGrid/SortableGirdProvider.tsx +++ b/apps/mobile/src/components/sortableGrid/SortableGirdProvider.tsx @@ -4,17 +4,10 @@ import type { DragContextProviderProps, LayoutContextProviderProps, } from 'src/components/sortableGrid/contexts' -import { - AutoScrollProvider, - DragContextProvider, - LayoutContextProvider, -} from 'src/components/sortableGrid/contexts' +import { AutoScrollProvider, DragContextProvider, LayoutContextProvider } from 'src/components/sortableGrid/contexts' type SortableGridProviderProps = PropsWithChildren< - Omit< - LayoutContextProviderProps & DragContextProviderProps & AutoScrollProviderProps, - 'itemKeys' - > + Omit & AutoScrollProviderProps, 'itemKeys'> > export function SortableGridProvider({ @@ -55,11 +48,9 @@ export function SortableGridProvider({ keyExtractor={keyExtractor} onChange={onChange} onDragStart={onDragStart} - onDrop={onDrop}> - + onDrop={onDrop} + > + {children} diff --git a/apps/mobile/src/components/sortableGrid/SortableGrid.tsx b/apps/mobile/src/components/sortableGrid/SortableGrid.tsx index b1ae5f74b38..96be8f5e1be 100644 --- a/apps/mobile/src/components/sortableGrid/SortableGrid.tsx +++ b/apps/mobile/src/components/sortableGrid/SortableGrid.tsx @@ -123,17 +123,12 @@ function SortableGridInner({ + onLayout={handleGridMeasurement} + > {data.map((item, index) => { const key = keyExtractor(item, index) return ( - + ) })} @@ -142,10 +137,7 @@ function SortableGridInner({ from the animated style was applied. We can't use onLayout on the grid items wrapper component because it already has the same height as containerHeight value, thus the onLayout callback won't be called again, because the size of the component doesn't change. */} - + ) } diff --git a/apps/mobile/src/components/sortableGrid/SortableGridItem.tsx b/apps/mobile/src/components/sortableGrid/SortableGridItem.tsx index ec669206c62..69b656f96cb 100644 --- a/apps/mobile/src/components/sortableGrid/SortableGridItem.tsx +++ b/apps/mobile/src/components/sortableGrid/SortableGridItem.tsx @@ -17,11 +17,7 @@ import { OFFSET_EPS, TIME_TO_ACTIVATE_PAN, } from 'src/components/sortableGrid/constants' -import { - useAutoScrollContext, - useDragContext, - useLayoutContext, -} from 'src/components/sortableGrid/contexts' +import { useAutoScrollContext, useDragContext, useLayoutContext } from 'src/components/sortableGrid/contexts' import { useItemPosition } from 'src/components/sortableGrid/hooks' import { GridItemExiting } from 'src/components/sortableGrid/layoutAnimations' import { SortableGridRenderItem } from 'src/components/sortableGrid/types' @@ -34,12 +30,7 @@ type SortableGridItemProps = { numColumns: number } -function SortableGridItem({ - item, - itemKey, - renderItem, - numColumns, -}: SortableGridItemProps): JSX.Element { +function SortableGridItem({ item, itemKey, renderItem, numColumns }: SortableGridItemProps): JSX.Element { const { measuredItemsCount, targetContainerHeight, @@ -98,7 +89,7 @@ function SortableGridItem({ itemDimensions.value[key] = { width, height } })(itemKey) }, - [itemKey, itemDimensions, measuredItemsCount] + [itemKey, itemDimensions, measuredItemsCount], ) const handleDragEnd = useCallback(() => { @@ -119,7 +110,7 @@ function SortableGridItem({ isTouched.value = true const progress = withDelay( ACTIVATE_PAN_ANIMATION_DELAY, - withTiming(1, { duration: TIME_TO_ACTIVATE_PAN - ACTIVATE_PAN_ANIMATION_DELAY }) + withTiming(1, { duration: TIME_TO_ACTIVATE_PAN - ACTIVATE_PAN_ANIMATION_DELAY }), ) pressProgress.value = progress activationProgress.value = progress @@ -162,7 +153,7 @@ function SortableGridItem({ pressProgress, scrollY, startScrollOffset, - ] + ], ) // ITEM POSITIONING AND ANIMATION @@ -170,11 +161,7 @@ function SortableGridItem({ // INITIAL RENDER // (relative placements - no absolute positioning yet) // This ensures there is no blank space when grid items are being measured - if ( - !initialRenderCompleted.value || - appliedContainerHeight.value === -1 || - columnWidth.value === -1 - ) { + if (!initialRenderCompleted.value || appliedContainerHeight.value === -1 || columnWidth.value === -1) { return { width: `${100 / numColumns}%`, } @@ -217,12 +204,7 @@ function SortableGridItem({ top: y, left: x, width: columnWidth.value, - zIndex: getItemZIndex( - isActive.value, - pressProgress.value, - { x, y }, - targetItemPosition.value - ), + zIndex: getItemZIndex(isActive.value, pressProgress.value, { x, y }, targetItemPosition.value), } }) @@ -234,7 +216,7 @@ function SortableGridItem({ shadowColor: interpolateColor( pressProgress.value, [0, 1], - ['transparent', `rgba(0, 0, 0, ${activeItemShadowOpacity.value})`] + ['transparent', `rgba(0, 0, 0, ${activeItemShadowOpacity.value})`], ), })) @@ -245,15 +227,11 @@ function SortableGridItem({ pressProgress, dragActivationProgress: activationProgress, }), - [item, renderItem, activationProgress, pressProgress] + [item, renderItem, activationProgress, pressProgress], ) return ( - + {content} diff --git a/apps/mobile/src/components/sortableGrid/contexts/AutoScrollContextProvider.tsx b/apps/mobile/src/components/sortableGrid/contexts/AutoScrollContextProvider.tsx index d1a79b3ec8a..a04293a8def 100644 --- a/apps/mobile/src/components/sortableGrid/contexts/AutoScrollContextProvider.tsx +++ b/apps/mobile/src/components/sortableGrid/contexts/AutoScrollContextProvider.tsx @@ -1,12 +1,6 @@ import { PropsWithChildren, createContext, useContext, useMemo, useRef } from 'react' import { View } from 'react-native' -import { - SharedValue, - runOnJS, - useAnimatedReaction, - useDerivedValue, - useSharedValue, -} from 'react-native-reanimated' +import { SharedValue, runOnJS, useAnimatedReaction, useDerivedValue, useSharedValue } from 'react-native-reanimated' import { AUTO_SCROLL_THRESHOLD } from 'src/components/sortableGrid/constants' import { useDragContext } from 'src/components/sortableGrid/contexts/DragContextProvider' import { useLayoutContext } from 'src/components/sortableGrid/contexts/LayoutContextProvider' @@ -54,7 +48,7 @@ export function AutoScrollProvider({ const scrollTarget = useSharedValue(0) const scrollDirection = useSharedValue(0) // 1 = down, -1 = up const activeItemHeight = useDerivedValue( - () => (activeItemKey.value ? itemDimensions.value[activeItemKey.value]?.height : -1) ?? -1 + () => (activeItemKey.value ? itemDimensions.value[activeItemKey.value]?.height : -1) ?? -1, ) // REFS @@ -64,13 +58,11 @@ export function AutoScrollProvider({ // Values used to scroll the container to the proper offset // (updated from the SortableGridInner component) const containerStartOffset = useSharedValue(0) - const containerEndOffset = useDerivedValue( - () => containerStartOffset.value + targetContainerHeight.value - ) + const containerEndOffset = useDerivedValue(() => containerStartOffset.value + targetContainerHeight.value) const startScrollOffset = useSharedValue(0) const scrollOffsetDiff = useDerivedValue(() => - activeItemKey.value === null ? 0 : scrollYValue.value - startScrollOffset.value + activeItemKey.value === null ? 0 : scrollYValue.value - startScrollOffset.value, ) /** @@ -98,7 +90,7 @@ export function AutoScrollProvider({ () => { // Reset when the active index changes scrollDirection.value = 0 - } + }, ) // AUTO SCROLL HANDLER @@ -110,8 +102,7 @@ export function AutoScrollProvider({ } return { - itemAbsoluteY: - activeItemPosition.value.y + containerStartOffset.value + scrollOffsetDiff.value, + itemAbsoluteY: activeItemPosition.value.y + containerStartOffset.value + scrollOffsetDiff.value, activeHeight: activeItemHeight.value, minOffset: containerStartOffset.value, maxOffset: containerEndOffset.value - visibleHeightValue.value, @@ -146,10 +137,7 @@ export function AutoScrollProvider({ // If the active item is above the current scroll position (with small threshold // to start scrolling earlier) and the scroll position is not at the top of the // grid, scroll up - if ( - itemAbsoluteY < scrollY + AUTO_SCROLL_THRESHOLD && - scrollY > minOffset - AUTO_SCROLL_THRESHOLD - ) { + if (itemAbsoluteY < scrollY + AUTO_SCROLL_THRESHOLD && scrollY > minOffset - AUTO_SCROLL_THRESHOLD) { currentScrollTarget = Math.max(minOffset - AUTO_SCROLL_THRESHOLD, scrollY - activeHeight) currentScrollDirection = -1 } @@ -184,7 +172,7 @@ export function AutoScrollProvider({ scrollDirection.value = currentScrollDirection scrollTarget.value = currentScrollTarget runOnJS(scrollToOffset)(currentScrollTarget) - } + }, ) /** @@ -199,14 +187,7 @@ export function AutoScrollProvider({ startScrollOffset, scrollY: scrollYValue, }), - [ - gridContainerRef, - containerStartOffset, - containerEndOffset, - scrollOffsetDiff, - startScrollOffset, - scrollYValue, - ] + [gridContainerRef, containerStartOffset, containerEndOffset, scrollOffsetDiff, startScrollOffset, scrollYValue], ) return {children} diff --git a/apps/mobile/src/components/sortableGrid/contexts/DragContextProvider.tsx b/apps/mobile/src/components/sortableGrid/contexts/DragContextProvider.tsx index 4b1e7650448..b079e6a0be3 100644 --- a/apps/mobile/src/components/sortableGrid/contexts/DragContextProvider.tsx +++ b/apps/mobile/src/components/sortableGrid/contexts/DragContextProvider.tsx @@ -1,11 +1,5 @@ import { PropsWithChildren, createContext, useContext, useMemo } from 'react' -import { - SharedValue, - runOnJS, - useAnimatedReaction, - useDerivedValue, - useSharedValue, -} from 'react-native-reanimated' +import { SharedValue, runOnJS, useAnimatedReaction, useDerivedValue, useSharedValue } from 'react-native-reanimated' import { useLayoutContext } from 'src/components/sortableGrid/contexts/LayoutContextProvider' import { useStableCallback } from 'src/components/sortableGrid/hooks' import { @@ -92,22 +86,20 @@ export function DragContextProvider({ /** * HANDLERS */ - const handleDragStart = useStableCallback( - async (key: string, keyToIdx: Record) => { - const index = keyToIdx[key] - if (index === undefined) { - return - } - const item = data[index] - if (hapticFeedback) { - await HapticFeedback.impact(ImpactFeedbackStyle.Heavy) - } - if (!onDragStart || !item) { - return - } - onDragStart({ index, item }) + const handleDragStart = useStableCallback(async (key: string, keyToIdx: Record) => { + const index = keyToIdx[key] + if (index === undefined) { + return } - ) + const item = data[index] + if (hapticFeedback) { + await HapticFeedback.impact(ImpactFeedbackStyle.Heavy) + } + if (!onDragStart || !item) { + return + } + onDragStart({ index, item }) + }) const handleDrop = useStableCallback((key: string, keyToIdx: Record) => { const index = keyToIdx[key] @@ -121,39 +113,37 @@ export function DragContextProvider({ onDrop({ index, item }) }) - const handleChange = useStableCallback( - async (swappedKey: string, keyToIdx: Record) => { - if (!onChange) { - return - } - const toIndex = keyToIdx[swappedKey] - if (toIndex === undefined) { - return - } - const fromIndex = itemKeys.indexOf(swappedKey) + const handleChange = useStableCallback(async (swappedKey: string, keyToIdx: Record) => { + if (!onChange) { + return + } + const toIndex = keyToIdx[swappedKey] + if (toIndex === undefined) { + return + } + const fromIndex = itemKeys.indexOf(swappedKey) - const reorderedData: I[] = [] + const reorderedData: I[] = [] - if (hapticFeedback) { - await HapticFeedback.impact(ImpactFeedbackStyle.Medium) - } + if (hapticFeedback) { + await HapticFeedback.impact(ImpactFeedbackStyle.Medium) + } - for (let i = 0; i < data.length; i++) { - const item = data[i] - if (!item) { - return - } - const itemKey = keyExtractor(item, i) - const index = keyToIdx[itemKey] - if (index === undefined) { - return - } - reorderedData[index] = item + for (let i = 0; i < data.length; i++) { + const item = data[i] + if (!item) { + return } - - onChange({ data: reorderedData, fromIndex, toIndex }) + const itemKey = keyExtractor(item, i) + const index = keyToIdx[itemKey] + if (index === undefined) { + return + } + reorderedData[index] = item } - ) + + onChange({ data: reorderedData, fromIndex, toIndex }) + }) /** * REACTIONS @@ -172,7 +162,7 @@ export function DragContextProvider({ prevActiveItemKey.value = key } }, - [handleDragStart, handleChange] + [handleDragStart, handleChange], ) // Handle drop (after animation of the active item is finished @@ -184,7 +174,7 @@ export function DragContextProvider({ runOnJS(handleDrop)(prevActiveItemKey.value, keyToIndex.value) } }, - [handleDrop] + [handleDrop], ) /** @@ -210,7 +200,7 @@ export function DragContextProvider({ activeItemScale, activeItemOpacity, activeItemShadowOpacity, - ] + ], ) return {children} diff --git a/apps/mobile/src/components/sortableGrid/contexts/LayoutContextProvider.tsx b/apps/mobile/src/components/sortableGrid/contexts/LayoutContextProvider.tsx index 849bd2dc689..c60f573d536 100644 --- a/apps/mobile/src/components/sortableGrid/contexts/LayoutContextProvider.tsx +++ b/apps/mobile/src/components/sortableGrid/contexts/LayoutContextProvider.tsx @@ -73,15 +73,11 @@ export function LayoutContextProvider({ const containerWidth = useSharedValue(-1) const containerHeight = useSharedValue(-1) const targetContainerHeight = useSharedValue(-1) - const columnWidth = useDerivedValue(() => - containerWidth.value === -1 ? -1 : containerWidth.value / numColumns - ) + const columnWidth = useDerivedValue(() => (containerWidth.value === -1 ? -1 : containerWidth.value / numColumns)) // KEY-INDEX MAPPINGS const indexToKey = useSharedValue([]) - const keyToIndex = useDerivedValue(() => - Object.fromEntries(indexToKey.value.map((key, index) => [key, index])) - ) + const keyToIndex = useDerivedValue(() => Object.fromEntries(indexToKey.value.map((key, index) => [key, index]))) // POSITIONING const itemPositions = useDerivedValue>(() => { @@ -98,7 +94,7 @@ export function LayoutContextProvider({ x: columnWidth.value * getColumnIndex(parseInt(index, 10), numColumns), y: rowOffsets.value[getRowIndex(parseInt(index, 10), numColumns)] ?? 0, }, - ]) + ]), ) }, [rowsCount, columnWidth, rowOffsets, indexToKey, numColumns]) @@ -125,7 +121,7 @@ export function LayoutContextProvider({ itemDimensions.value = { ...itemDimensions.value } } }, - [itemKeys] + [itemKeys], ) // ROW OFFSETS UPDATER @@ -144,7 +140,7 @@ export function LayoutContextProvider({ const rowIndex = getRowIndex(parseInt(itemIndex, 10), numColumns) offsets[rowIndex + 1] = Math.max( offsets[rowIndex + 1] ?? 0, - (offsets[rowIndex] ?? 0) + (dimensions[key]?.height ?? 0) + (offsets[rowIndex] ?? 0) + (dimensions[key]?.height ?? 0), ) } // Update row offsets only if they have changed @@ -155,7 +151,7 @@ export function LayoutContextProvider({ rowOffsets.value = offsets } }, - [numColumns] + [numColumns], ) useAnimatedReaction( @@ -183,7 +179,7 @@ export function LayoutContextProvider({ containerHeight.value = newHeight } }, - [animateContainerHeight] + [animateContainerHeight], ) /** @@ -217,7 +213,7 @@ export function LayoutContextProvider({ keyToIndex, indexToKey, itemPositions, - ] + ], ) return {children} diff --git a/apps/mobile/src/components/sortableGrid/hooks.ts b/apps/mobile/src/components/sortableGrid/hooks.ts index 879e679246f..d9ad7a31c6e 100644 --- a/apps/mobile/src/components/sortableGrid/hooks.ts +++ b/apps/mobile/src/components/sortableGrid/hooks.ts @@ -1,22 +1,12 @@ import { useCallback, useRef } from 'react' -import { - SharedValue, - runOnJS, - useAnimatedReaction, - useSharedValue, - withTiming, -} from 'react-native-reanimated' -import { - useAutoScrollContext, - useDragContext, - useLayoutContext, -} from 'src/components/sortableGrid/contexts' +import { SharedValue, runOnJS, useAnimatedReaction, useSharedValue, withTiming } from 'react-native-reanimated' +import { useAutoScrollContext, useDragContext, useLayoutContext } from 'src/components/sortableGrid/contexts' import { getColumnIndex, getRowIndex } from 'src/components/sortableGrid/utils' import { HapticFeedback, ImpactFeedbackStyle } from 'ui/src' export function useStableCallback< // eslint-disable-next-line @typescript-eslint/no-explicit-any - C extends (...args: Array) => any + C extends (...args: Array) => any, >(callback?: C): C { const callbackRef = useRef(callback) callbackRef.current = callback @@ -24,7 +14,7 @@ export function useStableCallback< return useCallback( // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return (...args: Array) => callbackRef.current?.(...args), - [] + [], ) as C } @@ -51,7 +41,7 @@ export function useItemPosition(key: string): { x.value = x.value === null ? position.x : withTiming(position.x) y.value = y.value === null ? position.y : withTiming(position.y) }, - [key] + [key], ) useAnimatedReaction( @@ -64,15 +54,14 @@ export function useItemPosition(key: string): { x.value = position.x y.value = position.y + offsetDiff } - } + }, ) return { x, y } } export function useItemOrderUpdater(numColumns: number, hapticFeedback: boolean): void { - const { keyToIndex, indexToKey, rowOffsets, targetContainerHeight, itemDimensions } = - useLayoutContext() + const { keyToIndex, indexToKey, rowOffsets, targetContainerHeight, itemDimensions } = useLayoutContext() const { activeItemKey, activeItemPosition } = useDragContext() const { scrollOffsetDiff } = useAutoScrollContext() @@ -120,11 +109,7 @@ export function useItemOrderUpdater(numColumns: number, hapticFeedback: boolean) let dy = 0 if (yOffsetAbove > 0 && centerY < yOffsetAbove) { dy = -1 - } else if ( - yOffsetBelow !== undefined && - yOffsetBelow < targetContainerHeight.value && - centerY > yOffsetBelow - ) { + } else if (yOffsetBelow !== undefined && yOffsetBelow < targetContainerHeight.value && centerY > yOffsetBelow) { dy = 1 } @@ -132,11 +117,7 @@ export function useItemOrderUpdater(numColumns: number, hapticFeedback: boolean) let dx = 0 if (xOffsetLeft > 0 && centerX < xOffsetLeft) { dx = -1 - } else if ( - columnIndex < numColumns - 1 && - activeIndex < itemsCount && - centerX > xOffsetRight - ) { + } else if (columnIndex < numColumns - 1 && activeIndex < itemsCount && centerX > xOffsetRight) { dx = 1 } @@ -168,6 +149,6 @@ export function useItemOrderUpdater(numColumns: number, hapticFeedback: boolean) runOnJS(vibrate)() } }, - [hapticFeedback] + [hapticFeedback], ) } diff --git a/apps/mobile/src/components/sortableGrid/utils.ts b/apps/mobile/src/components/sortableGrid/utils.ts index e12e24aaa35..e336f1039c1 100644 --- a/apps/mobile/src/components/sortableGrid/utils.ts +++ b/apps/mobile/src/components/sortableGrid/utils.ts @@ -1,20 +1,11 @@ import { Vector } from 'src/components/sortableGrid/types' -export const areArraysDifferent = ( - arr1: T[], - arr2: T[], - areEqual = (a: T, b: T): boolean => a === b -): boolean => { +export const areArraysDifferent = (arr1: T[], arr2: T[], areEqual = (a: T, b: T): boolean => a === b): boolean => { 'worklet' - return ( - arr1.length !== arr2.length || arr1.some((item, index) => !areEqual(item, arr2[index] as T)) - ) + return arr1.length !== arr2.length || arr1.some((item, index) => !areEqual(item, arr2[index] as T)) } -const hasProp = ( - object: O, - prop: P -): object is O & Record => { +const hasProp = (object: O, prop: P): object is O & Record => { return prop in object } @@ -45,11 +36,7 @@ export const getColumnIndex = (index: number, numColumns: number): number => { return index % numColumns } -export const getItemsInColumnCount = ( - index: number, - numColumns: number, - itemsCount: number -): number => { +export const getItemsInColumnCount = (index: number, numColumns: number, itemsCount: number): number => { 'worklet' const columnIndex = getColumnIndex(index, numColumns) return Math.floor(itemsCount / numColumns) + (columnIndex < itemsCount % numColumns ? 1 : 0) @@ -59,7 +46,7 @@ export const getItemZIndex = ( isActive: boolean, pressProgress: number, position: Vector, - targetPosition?: Vector + targetPosition?: Vector, ): number => { 'worklet' if (isActive) { diff --git a/apps/mobile/src/components/text/AnimatedText.tsx b/apps/mobile/src/components/text/AnimatedText.tsx index 4cb62cad7dd..312cf355797 100644 --- a/apps/mobile/src/components/text/AnimatedText.tsx +++ b/apps/mobile/src/components/text/AnimatedText.tsx @@ -1,11 +1,5 @@ import React from 'react' -import { - TextProps as RNTextProps, - StyleSheet, - TextInput, - TextInputProps, - useWindowDimensions, -} from 'react-native' +import { TextProps as RNTextProps, StyleSheet, TextInput, TextInputProps, useWindowDimensions } from 'react-native' import Animated, { useAnimatedProps } from 'react-native-reanimated' import { Flex, TextProps as TamaTextProps, TextFrame, usePropsAndStyle } from 'ui/src' import { TextLoaderWrapper } from 'ui/src/components/text/Text' @@ -58,9 +52,7 @@ export const BaseAnimatedText = ({ {/* Use the text component to properly calculate the width of the loading shimmer. An input component with a width dependent on the length of the content was sometimes rendered with a very small width regardless of the text passed as a value */} - - {loadingPlaceholderText} - + {loadingPlaceholderText} ) @@ -96,7 +88,7 @@ export const AnimatedText = ({ style, ...propsIn }: TextProps): JSX.Element => { }, { forComponent: TextFrame, - } + }, ) const { fontScale } = useWindowDimensions() diff --git a/apps/mobile/src/components/text/DecimalNumber.test.tsx b/apps/mobile/src/components/text/DecimalNumber.test.tsx index 626917cff85..85c77a6fa9e 100644 --- a/apps/mobile/src/components/text/DecimalNumber.test.tsx +++ b/apps/mobile/src/components/text/DecimalNumber.test.tsx @@ -3,17 +3,13 @@ import { DecimalNumber } from 'src/components/text/DecimalNumber' import { render } from 'src/test/test-utils' it('renders a DecimalNumber', () => { - const tree = render( - - ) + const tree = render() expect(tree).toMatchSnapshot() }) it('renders a DecimalNumber without a comma separator', () => { - const tree = render( - - ) + const tree = render() expect(tree).toMatchSnapshot() }) diff --git a/apps/mobile/src/components/text/DecimalNumber.tsx b/apps/mobile/src/components/text/DecimalNumber.tsx index c8d669f63b2..8fc2e69a730 100644 --- a/apps/mobile/src/components/text/DecimalNumber.tsx +++ b/apps/mobile/src/components/text/DecimalNumber.tsx @@ -24,18 +24,13 @@ export function DecimalNumber({ }: DecimalNumberProps): JSX.Element { const [pre, post] = formattedNumber.split(separator) - const decimalPartColor = - number === undefined || number >= decimalThreshold ? '$neutral3' : '$neutral1' + const decimalPartColor = number === undefined || number >= decimalThreshold ? '$neutral3' : '$neutral1' return ( {pre} {post && ( - + {separator} {post} diff --git a/apps/mobile/src/components/text/LongMarkdownText.test.tsx b/apps/mobile/src/components/text/LongMarkdownText.test.tsx index c6adee187cd..91b68e5d156 100644 --- a/apps/mobile/src/components/text/LongMarkdownText.test.tsx +++ b/apps/mobile/src/components/text/LongMarkdownText.test.tsx @@ -67,7 +67,7 @@ describe(LongMarkdownText, () => { // props are at index 0, ref is at index 1 expect(MockedMarkdown.mock.lastCall[0]).toEqual( - getMarkdownPropsWithHeight('auto') // height auto means the text doesn't exceed the limit + getMarkdownPropsWithHeight('auto'), // height auto means the text doesn't exceed the limit ) }) @@ -89,7 +89,7 @@ describe(LongMarkdownText, () => { measureMarkdown(tree, 5) // Assume Some very long text is five lines expect(MockedMarkdown.mock.lastCall[0]).toEqual( - getMarkdownPropsWithHeight(LINE_HEIGHT * 3) // Height is limited to 3 lines + getMarkdownPropsWithHeight(LINE_HEIGHT * 3), // Height is limited to 3 lines ) }) @@ -115,7 +115,7 @@ describe(LongMarkdownText, () => { fireEvent.press(readMoreButton) expect(MockedMarkdown.mock.lastCall[0]).toEqual( - getMarkdownPropsWithHeight('auto') // height auto means the text doesn't exceed the limit + getMarkdownPropsWithHeight('auto'), // height auto means the text doesn't exceed the limit ) }) @@ -142,13 +142,13 @@ describe(LongMarkdownText, () => { fireEvent.press(readMoreButton) // expand expect(MockedMarkdown.mock.lastCall[0]).toEqual( - getMarkdownPropsWithHeight('auto') // height auto means the text doesn't exceed the limit + getMarkdownPropsWithHeight('auto'), // height auto means the text doesn't exceed the limit ) fireEvent.press(readMoreButton) // collapse expect(MockedMarkdown.mock.lastCall[0]).toEqual( - getMarkdownPropsWithHeight(LINE_HEIGHT * 3) // Height is limited to 3 lines + getMarkdownPropsWithHeight(LINE_HEIGHT * 3), // Height is limited to 3 lines ) }) }) diff --git a/apps/mobile/src/components/text/LongMarkdownText.tsx b/apps/mobile/src/components/text/LongMarkdownText.tsx index 491fda5507c..e33d6a80755 100644 --- a/apps/mobile/src/components/text/LongMarkdownText.tsx +++ b/apps/mobile/src/components/text/LongMarkdownText.tsx @@ -48,7 +48,7 @@ export function LongMarkdownText(props: LongMarkdownTextProps): JSX.Element { toggleExpanded() initialContentHeightRef.current = textContentHeight }, - [initialDisplayedLines, textLineHeight] + [initialDisplayedLines, textLineHeight], ) const codeStyle = { backgroundColor: codeBackgroundColor, borderColor: 'transparent' } @@ -86,7 +86,8 @@ export function LongMarkdownText(props: LongMarkdownTextProps): JSX.Element { }, }): void => { setTextLineHeight(height) - }}> + }} + > + onPress={toggleExpanded} + > {expanded ? t('common.longText.button.less') : t('common.longText.button.more')} ) : null} diff --git a/apps/mobile/src/components/text/LongText.tsx b/apps/mobile/src/components/text/LongText.tsx index b9f727d15fc..77917601f2f 100644 --- a/apps/mobile/src/components/text/LongText.tsx +++ b/apps/mobile/src/components/text/LongText.tsx @@ -13,10 +13,7 @@ type LongTextProps = { codeBackgroundColor?: string readMoreOrLessColor?: string variant?: keyof typeof fonts -} & Omit< - ComponentProps, - 'children' | 'numberOfLines' | 'onTextLayout' | 'color' | 'variant' -> +} & Omit, 'children' | 'numberOfLines' | 'onTextLayout' | 'color' | 'variant'> export function LongText(props: LongTextProps): JSX.Element { const colors = useSporeColors() @@ -45,7 +42,7 @@ export function LongText(props: LongTextProps): JSX.Element { setExpanded(false) isInitializedRef.current = true }, - [initialDisplayedLines] + [initialDisplayedLines], ) return ( @@ -55,7 +52,8 @@ export function LongText(props: LongTextProps): JSX.Element { style={{ color }} variant={variant} onTextLayout={onTextLayout} - {...rest}> + {...rest} + > {text} @@ -68,7 +66,8 @@ export function LongText(props: LongTextProps): JSX.Element { style={{ color: readMoreOrLessColor }} testID="read-more-button" variant="buttonLabel3" - onPress={(): void => setExpanded(!expanded)}> + onPress={(): void => setExpanded(!expanded)} + > {expanded ? t('common.longText.button.less') : t('common.longText.button.more')} ) : null} diff --git a/apps/mobile/src/components/text/Pill.stories.mdx b/apps/mobile/src/components/text/Pill.stories.mdx index 0ebac543b18..fedd2e25f5c 100644 --- a/apps/mobile/src/components/text/Pill.stories.mdx +++ b/apps/mobile/src/components/text/Pill.stories.mdx @@ -14,7 +14,8 @@ export const Template = (args) => void 0} /> args={{ label: 'Your tokens', borderColor: 'sporeBlack', - }}> + }} + > {Template.bind({})} diff --git a/apps/mobile/src/components/text/TextWithFuseMatches.test.tsx b/apps/mobile/src/components/text/TextWithFuseMatches.test.tsx index d7c5a8a4e67..11a4d6252c6 100644 --- a/apps/mobile/src/components/text/TextWithFuseMatches.test.tsx +++ b/apps/mobile/src/components/text/TextWithFuseMatches.test.tsx @@ -15,7 +15,7 @@ it('renders text with few matches', () => { { value: 'xt wit', indices: [[4, 8]] }, ]} text="A text without matches" - /> + />, ) expect(tree).toMatchSnapshot() }) diff --git a/apps/mobile/src/components/tokens/TokenMetadata.tsx b/apps/mobile/src/components/tokens/TokenMetadata.tsx index 9a65ae260d3..ec5fcaed49d 100644 --- a/apps/mobile/src/components/tokens/TokenMetadata.tsx +++ b/apps/mobile/src/components/tokens/TokenMetadata.tsx @@ -7,10 +7,7 @@ type TokenMetadataProps = PropsWithChildren<{ }> /** Helper component to format rhs metadata for a given token. */ -export const TokenMetadata = ({ - children, - align = 'flex-end', -}: TokenMetadataProps): JSX.Element => { +export const TokenMetadata = ({ children, align = 'flex-end' }: TokenMetadataProps): JSX.Element => { return ( diff --git a/apps/mobile/src/components/tooltip/TooltipButton.tsx b/apps/mobile/src/components/tooltip/TooltipButton.tsx index a73bdaefd47..e566ddb49fb 100644 --- a/apps/mobile/src/components/tooltip/TooltipButton.tsx +++ b/apps/mobile/src/components/tooltip/TooltipButton.tsx @@ -38,7 +38,8 @@ export function TooltipInfoButton({ Keyboard.dismiss() setShowModal(true) }} - {...rest}> + {...rest} + > setShowModal(false)}> + onClose={(): void => setShowModal(false)} + > {modalContent ?? null} )} diff --git a/apps/mobile/src/components/unitags/ChangeUnitagModal.tsx b/apps/mobile/src/components/unitags/ChangeUnitagModal.tsx index 185489867b4..50020f7837f 100644 --- a/apps/mobile/src/components/unitags/ChangeUnitagModal.tsx +++ b/apps/mobile/src/components/unitags/ChangeUnitagModal.tsx @@ -49,16 +49,16 @@ export function ChangeUnitagModal({ const [isChangeResponseLoading, setIsChangeResponseLoading] = useState(false) const [unitagToCheck, setUnitagToCheck] = useState(unitag) - const { error: canClaimUnitagNameError, loading: loadingUnitagErrorCheck } = - useCanClaimUnitagName(address, unitagToCheck) + const { error: canClaimUnitagNameError, loading: loadingUnitagErrorCheck } = useCanClaimUnitagName( + address, + unitagToCheck, + ) const { errorCode } = useCanAddressClaimUnitag(address, true) const { triggerRefetchUnitags } = useUnitagUpdater() const isUnitagEdited = unitag !== newUnitag - const isUnitagInvalid = - newUnitag === unitagToCheck && !!canClaimUnitagNameError && !loadingUnitagErrorCheck - const isUnitagValid = - isUnitagEdited && !canClaimUnitagNameError && !loadingUnitagErrorCheck && !!newUnitag + const isUnitagInvalid = newUnitag === unitagToCheck && !!canClaimUnitagNameError && !loadingUnitagErrorCheck + const isUnitagValid = isUnitagEdited && !canClaimUnitagNameError && !loadingUnitagErrorCheck && !!newUnitag const hasReachedAddressLimit = errorCode === UnitagErrorCodes.AddressLimitReached const isSubmitButtonDisabled = isCheckingUnitag || @@ -116,7 +116,7 @@ export function ChangeUnitagModal({ pushNotification({ type: AppNotificationType.Error, errorMessage: parseUnitagErrorCode(t, unitagToCheck, changeResponse.errorCode), - }) + }), ) return } @@ -129,7 +129,7 @@ export function ChangeUnitagModal({ pushNotification({ type: AppNotificationType.Success, title: t('unitags.notification.username.title'), - }) + }), ) navigation.goBack() onClose() @@ -143,7 +143,7 @@ export function ChangeUnitagModal({ pushNotification({ type: AppNotificationType.Error, errorMessage: t('unitags.notification.username.error'), - }) + }), ) onClose() setIsChangeResponseLoading(false) @@ -194,9 +194,7 @@ export function ChangeUnitagModal({ return ( <> - {showConfirmModal && ( - - )} + {showConfirmModal && } 0 ? keyboardHeight - spacing.spacing20 : '$spacing12'} pt="$spacing12" - px="$spacing24"> + px="$spacing24" + > {t('unitags.editUsername.title')} @@ -214,7 +213,8 @@ export function ChangeUnitagModal({ borderColor="$surface3" borderRadius="$rounded16" borderWidth="$spacing1" - px="$spacing24"> + px="$spacing24" + > + width="100%" + > {t('unitags.editUsername.warning.max')} ) : ( - + {t('unitags.editUsername.warning.default')} @@ -274,7 +270,8 @@ export function ChangeUnitagModal({ disabled={isSubmitButtonDisabled} testID={ElementName.Confirm} theme="primary" - onPress={onPressSaveChanges}> + onPress={onPressSaveChanges} + > {isCheckingUnitag || isChangeResponseLoading ? ( @@ -307,7 +304,8 @@ function ChangeUnitagConfirmModal({ borderRadius="$rounded12" height="$spacing48" mb="$spacing8" - minWidth="$spacing48"> + minWidth="$spacing48" + > diff --git a/apps/mobile/src/components/unitags/ChooseNftModal.tsx b/apps/mobile/src/components/unitags/ChooseNftModal.tsx index ddd6f8cb3db..14928d47672 100644 --- a/apps/mobile/src/components/unitags/ChooseNftModal.tsx +++ b/apps/mobile/src/components/unitags/ChooseNftModal.tsx @@ -31,7 +31,8 @@ export const ChooseNftModal = ({ address, setPhotoUri, onClose }: ChooseNftProps hideHandlebar={false} isDismissible={true} name={ModalName.NftCollection} - onClose={onClose}> + onClose={onClose} + > + onClose={onClose} + > {options.map((option) => ( @@ -114,19 +115,17 @@ const ChoosePhotoOption = ({ type }: { type: PhotoAction }): JSX.Element => { borderRadius="$rounded20" gap="$spacing16" justifyContent="flex-start" - p="$spacing24"> - {type === PhotoAction.BrowseCameraRoll && ( - - )} + p="$spacing24" + > + {type === PhotoAction.BrowseCameraRoll && } {type === PhotoAction.BrowseNftsList && } - {type === PhotoAction.RemovePhoto && ( - - )} + {type === PhotoAction.RemovePhoto && } + variant="buttonLabel2" + > {type === PhotoAction.BrowseCameraRoll && t('unitags.choosePhoto.option.cameraRoll')} {type === PhotoAction.BrowseNftsList && t('unitags.choosePhoto.option.nft')} {type === PhotoAction.RemovePhoto && t('unitags.choosePhoto.option.remove')} diff --git a/apps/mobile/src/components/unitags/DeleteUnitagModal.tsx b/apps/mobile/src/components/unitags/DeleteUnitagModal.tsx index bb04b14ae66..7aec7369cd8 100644 --- a/apps/mobile/src/components/unitags/DeleteUnitagModal.tsx +++ b/apps/mobile/src/components/unitags/DeleteUnitagModal.tsx @@ -41,7 +41,7 @@ export function DeleteUnitagModal({ pushNotification({ type: AppNotificationType.Error, errorMessage: t('unitags.notification.delete.error'), - }) + }), ) onClose() } @@ -68,7 +68,7 @@ export function DeleteUnitagModal({ pushNotification({ type: AppNotificationType.Success, title: t('unitags.notification.delete.title'), - }) + }), ) navigation.goBack() onClose() @@ -90,7 +90,8 @@ export function DeleteUnitagModal({ borderRadius="$rounded12" height="$spacing48" mb="$spacing8" - minWidth="$spacing48"> + minWidth="$spacing48" + > @@ -100,12 +101,7 @@ export function DeleteUnitagModal({ {t('unitags.delete.confirm.subtitle')} - - - - - ) -} diff --git a/apps/mobile/src/features/scantastic/ExtensionWaitlistModalState.ts b/apps/mobile/src/features/scantastic/ExtensionWaitlistModalState.ts deleted file mode 100644 index 0cdb634c5a1..00000000000 --- a/apps/mobile/src/features/scantastic/ExtensionWaitlistModalState.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface ExtensionWaitlistModalState { - isUserOnWaitlist: boolean -} diff --git a/apps/mobile/src/features/scantastic/ScantasticModal.tsx b/apps/mobile/src/features/scantastic/ScantasticModal.tsx index dd1d30404ed..7d802444423 100644 --- a/apps/mobile/src/features/scantastic/ScantasticModal.tsx +++ b/apps/mobile/src/features/scantastic/ScantasticModal.tsx @@ -14,10 +14,7 @@ import { ModalName } from 'uniswap/src/features/telemetry/constants' import { logger } from 'utilities/src/logger/logger' import { ONE_MINUTE_MS, ONE_SECOND_MS } from 'utilities/src/time/time' import { useInterval } from 'utilities/src/time/timing' -import { - ExtensionOnboardingState, - setExtensionOnboardingState, -} from 'wallet/src/features/behaviorHistory/slice' +import { ExtensionOnboardingState, setExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/slice' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' import { useSignerAccounts } from 'wallet/src/features/wallet/hooks' @@ -42,7 +39,7 @@ export function ScantasticModal(): JSX.Element | null { // Use the first mnemonic account because zero-balance mnemonic accounts will fail to retrieve the mnemonic from rnEthers const account = useSignerAccounts().sort( - (account1, account2) => account1.derivationIndex - account2.derivationIndex + (account1, account2) => account1.derivationIndex - account2.derivationIndex, )[0] if (!account) { @@ -54,9 +51,7 @@ export function ScantasticModal(): JSX.Element | null { const [OTP, setOTP] = useState('') // Once a user has scanned a QR they have 6 minutes to correctly input the OTP - const [expirationTimestamp, setExpirationTimestamp] = useState( - Date.now() + 6 * ONE_MINUTE_MS - ) + const [expirationTimestamp, setExpirationTimestamp] = useState(Date.now() + 6 * ONE_MINUTE_MS) const pubKey = params?.publicKey const uuid = params?.uuid const device = `${params?.vendor || ''} ${params?.model || ''}`.trim() @@ -81,7 +76,7 @@ export function ScantasticModal(): JSX.Element | null { pushNotification({ type: AppNotificationType.ScantasticComplete, hideDelay: 6 * ONE_SECOND_MS, - }) + }), ) dispatch(setExtensionOnboardingState(ExtensionOnboardingState.Completed)) dispatch(closeAllModals()) @@ -172,8 +167,7 @@ export function ScantasticModal(): JSX.Element | null { requiredForAppAccess: biometricAuthRequiredForAppAccess, requiredForTransactions: biometricAuthRequiredForTransactions, } = useBiometricAppSettings() - const requiresBiometricAuth = - biometricAuthRequiredForAppAccess || biometricAuthRequiredForTransactions + const requiresBiometricAuth = biometricAuthRequiredForAppAccess || biometricAuthRequiredForTransactions const onConfirmSync = async (): Promise => { if (requiresBiometricAuth) { @@ -227,10 +221,7 @@ export function ScantasticModal(): JSX.Element | null { if (showIPWarning) { return ( - + @@ -251,10 +242,7 @@ export function ScantasticModal(): JSX.Element | null { if (expired) { return ( - + @@ -273,10 +261,7 @@ export function ScantasticModal(): JSX.Element | null { if (OTP) { return ( - + @@ -299,10 +284,7 @@ export function ScantasticModal(): JSX.Element | null { if (error) { return ( - + @@ -324,10 +306,7 @@ export function ScantasticModal(): JSX.Element | null { const renderDeviceDetails = Boolean(device || browser) return ( - + @@ -343,7 +322,8 @@ export function ScantasticModal(): JSX.Element | null { borderWidth={1} gap="$spacing12" p="$spacing16" - width="100%"> + width="100%" + > {device && ( @@ -369,7 +349,8 @@ export function ScantasticModal(): JSX.Element | null { borderRadius="$rounded16" gap="$spacing8" p="$spacing16" - width="100%"> + width="100%" + > {t('scantastic.confirmation.warning')} @@ -380,7 +361,8 @@ export function ScantasticModal(): JSX.Element | null { icon={requiresBiometricAuth ? : undefined} mb="$spacing4" theme="primary" - onPress={onConfirmSync}> + onPress={onConfirmSync} + > {t('scantastic.confirmation.button.continue')} diff --git a/apps/mobile/src/features/telemetry/directLogScreens.ts b/apps/mobile/src/features/telemetry/directLogScreens.ts index 0437ea27bb5..292ee4e7782 100644 --- a/apps/mobile/src/features/telemetry/directLogScreens.ts +++ b/apps/mobile/src/features/telemetry/directLogScreens.ts @@ -7,9 +7,6 @@ export const DIRECT_LOG_ONLY_SCREENS: string[] = [ MobileScreens.NFTCollection, ] -export function shouldLogScreen( - directFromPage: boolean | undefined, - screen: string | undefined -): boolean { +export function shouldLogScreen(directFromPage: boolean | undefined, screen: string | undefined): boolean { return directFromPage || screen === undefined || !DIRECT_LOG_ONLY_SCREENS.includes(screen) } diff --git a/apps/mobile/src/features/telemetry/hooks.ts b/apps/mobile/src/features/telemetry/hooks.ts index 243faf1481f..b8b29446762 100644 --- a/apps/mobile/src/features/telemetry/hooks.ts +++ b/apps/mobile/src/features/telemetry/hooks.ts @@ -1,23 +1,23 @@ import { useEffect, useMemo } from 'react' import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { MobileAppsFlyerEvents, MobileEventName } from 'uniswap/src/features/telemetry/constants' +import { sendAnalyticsEvent, sendAppsFlyerEvent } from 'uniswap/src/features/telemetry/send' +import { logger } from 'utilities/src/logger/logger' +import { areSameDays } from 'utilities/src/time/date' +import { useAccountList } from 'wallet/src/features/accounts/hooks' import { selectAllowAnalytics, selectLastBalancesReport, selectLastBalancesReportValue, selectLastHeartbeat, selectWalletIsFunded, -} from 'src/features/telemetry/selectors' +} from 'wallet/src/features/telemetry/selectors' import { recordBalancesReport, recordHeartbeat, recordWalletFunded, shouldReportBalances, -} from 'src/features/telemetry/slice' -import { MobileAppsFlyerEvents, MobileEventName } from 'uniswap/src/features/telemetry/constants' -import { sendAnalyticsEvent, sendAppsFlyerEvent } from 'uniswap/src/features/telemetry/send' -import { logger } from 'utilities/src/logger/logger' -import { areSameDays } from 'utilities/src/time/date' -import { useAccountList } from 'wallet/src/features/accounts/hooks' +} from 'wallet/src/features/telemetry/slice' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { useAccounts } from 'wallet/src/features/wallet/hooks' @@ -58,19 +58,14 @@ export function useLastBalancesReporter(): () => void { // Only trigger the first time a funded wallet is detected dispatch(recordWalletFunded()) sendAppsFlyerEvent(MobileAppsFlyerEvents.WalletFunded, { sumOfFunds }).catch((error) => - logger.debug('hooks', 'useLastBalancesReporter', error) + logger.debug('hooks', 'useLastBalancesReporter', error), ) } }, [dispatch, signerAccountValues, walletIsFunded]) const reporter = (): void => { if ( - shouldReportBalances( - lastBalancesReport, - lastBalancesReportValue, - signerAccountAddresses, - signerAccountValues - ) + shouldReportBalances(lastBalancesReport, lastBalancesReportValue, signerAccountAddresses, signerAccountValues) ) { const totalBalance = signerAccountValues.reduce((a, b) => a + b, 0) diff --git a/apps/mobile/src/features/telemetry/saga.ts b/apps/mobile/src/features/telemetry/saga.ts index 4e2aa8719f3..6ec6b773933 100644 --- a/apps/mobile/src/features/telemetry/saga.ts +++ b/apps/mobile/src/features/telemetry/saga.ts @@ -1,10 +1,10 @@ // eslint-disable-next-line no-restricted-imports import { OriginApplication } from '@uniswap/analytics' import DeviceInfo, { getUniqueId } from 'react-native-device-info' -import { selectAllowAnalytics } from 'src/features/telemetry/selectors' import { call, delay, fork, select, takeEvery } from 'typed-redux-saga' import { uniswapUrls } from 'uniswap/src/constants/urls' import { ApplicationTransport } from 'utilities/src/telemetry/analytics/ApplicationTransport' +import { selectAllowAnalytics } from 'wallet/src/features/telemetry/selectors' // eslint-disable-next-line no-restricted-imports import { analytics } from 'utilities/src/telemetry/analytics/analytics' import { transactionActions } from 'wallet/src/features/transactions/slice' @@ -23,7 +23,7 @@ export function* telemetrySaga() { }), allowAnalytics, undefined, - async () => getUniqueId() + async () => getUniqueId(), ) yield* fork(watchTransactionEvents) } diff --git a/apps/mobile/src/features/telemetry/selectors.ts b/apps/mobile/src/features/telemetry/selectors.ts deleted file mode 100644 index d23c5176656..00000000000 --- a/apps/mobile/src/features/telemetry/selectors.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { MobileState } from 'src/app/reducer' - -export const selectLastBalancesReport = (state: MobileState): number => - state.telemetry.lastBalancesReport - -export const selectLastBalancesReportValue = (state: MobileState): number | undefined => - state.telemetry.lastBalancesReportValue - -export const selectLastHeartbeat = (state: MobileState): number => state.telemetry.lastHeartbeat - -export const selectWalletIsFunded = (state: MobileState): boolean => state.telemetry.walletIsFunded - -export const selectAllowAnalytics = (state: MobileState): boolean => state.telemetry.allowAnalytics diff --git a/apps/mobile/src/features/telemetry/utils.ts b/apps/mobile/src/features/telemetry/utils.ts index 940194dd1ba..5df615db78a 100644 --- a/apps/mobile/src/features/telemetry/utils.ts +++ b/apps/mobile/src/features/telemetry/utils.ts @@ -11,7 +11,7 @@ export enum AuthMethod { export function getAuthMethod( isSettingEnabled: boolean, isTouchIdSupported: boolean, - isFaceIdSupported: boolean + isFaceIdSupported: boolean, ): AuthMethod { if (isSettingEnabled) { // both cannot be true since no iOS device supports both @@ -28,7 +28,7 @@ export function getAuthMethod( export function getEventParams( screen: MobileAppScreen, - params: RootParamList[MobileAppScreen] + params: RootParamList[MobileAppScreen], ): Record | undefined { switch (screen) { case MobileScreens.SettingsWallet: diff --git a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem.stories.tsx b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem.stories.tsx index 4a947d1be3f..ae95efe8ba8 100644 --- a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem.stories.tsx +++ b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem.stories.tsx @@ -1,11 +1,8 @@ import type { Meta, StoryObj } from '@storybook/react' import React from 'react' -import { - Chain, - TokenDocument, -} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { getNativeAddress } from 'uniswap/src/constants/addresses' +import { Chain, TokenDocument } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' import { FiatPurchaseSummaryItem } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem' import TransactionSummaryLayout from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout' diff --git a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTApproveSummaryItem.stories.tsx b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTApproveSummaryItem.stories.tsx index 903e7de0161..1139a81d9e0 100644 --- a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTApproveSummaryItem.stories.tsx +++ b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTApproveSummaryItem.stories.tsx @@ -36,6 +36,7 @@ const baseApproveTx: Omit & { 'https://lh3.googleusercontent.com/9LokgAuB0Xqkio273GE0pY0WSJwOExFtFI1SkJT2jK-USvqFc-5if7ZP5PQ1h8s5YPimyJG5cSOdGGR2UaD3gTYMKAhj6yikYaw=s250', name: 'Froggy Friend #1777', tokenId: '1777', + address: '0x7ad05c1b87e93be306a9eadf80ea60d7648f1b6f', }, }, } diff --git a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTMintSummaryItem.stories.tsx b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTMintSummaryItem.stories.tsx index 74dc72a45ca..7fa84aa8b97 100644 --- a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTMintSummaryItem.stories.tsx +++ b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTMintSummaryItem.stories.tsx @@ -34,6 +34,7 @@ const baseNFTMintTx: Omit & { 'https://lh3.googleusercontent.com/9LokgAuB0Xqkio273GE0pY0WSJwOExFtFI1SkJT2jK-USvqFc-5if7ZP5PQ1h8s5YPimyJG5cSOdGGR2UaD3gTYMKAhj6yikYaw=s250', name: 'Froggy Friend #1777', tokenId: '1777', + address: '0x7ad05c1b87e93be306a9eadf80ea60d7648f1b6f', }, }, } diff --git a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTTradeSummaryItem.stories.tsx b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTTradeSummaryItem.stories.tsx index 83c7fbeb53a..2f1a0509038 100644 --- a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTTradeSummaryItem.stories.tsx +++ b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/NFTTradeSummaryItem.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react' import React from 'react' import { UniverseChainId } from 'uniswap/src/types/chains' +import { buildNativeCurrencyId } from 'uniswap/src/utils/currencyId' import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' import { NFTTradeSummaryItem } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/NFTTradeSummaryItem' import TransactionSummaryLayout from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout' @@ -11,7 +12,6 @@ import { TransactionStatus, TransactionType, } from 'wallet/src/features/transactions/types' -import { buildNativeCurrencyId } from 'wallet/src/utils/currencyId' const meta: Meta = { title: 'WIP/Activity Items', @@ -38,6 +38,7 @@ const baseNFTBuyTx: Omit & { 'https://lh3.googleusercontent.com/9LokgAuB0Xqkio273GE0pY0WSJwOExFtFI1SkJT2jK-USvqFc-5if7ZP5PQ1h8s5YPimyJG5cSOdGGR2UaD3gTYMKAhj6yikYaw=s250', name: 'Froggy Friend #1777', tokenId: '1777', + address: '0x7ad05c1b87e93be306a9eadf80ea60d7648f1b6f', }, purchaseCurrencyId: buildNativeCurrencyId(UniverseChainId.Mainnet), purchaseCurrencyAmountRaw: '1000000000000000000', diff --git a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/ReceiveSummaryItem.stories.tsx b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/ReceiveSummaryItem.stories.tsx index 59c5c99dcd3..b452b27097f 100644 --- a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/ReceiveSummaryItem.stories.tsx +++ b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/ReceiveSummaryItem.stories.tsx @@ -118,6 +118,7 @@ const baseNFTReceiveTx: Omit & { 'https://lh3.googleusercontent.com/9LokgAuB0Xqkio273GE0pY0WSJwOExFtFI1SkJT2jK-USvqFc-5if7ZP5PQ1h8s5YPimyJG5cSOdGGR2UaD3gTYMKAhj6yikYaw=s250', name: 'Froggy Friend #1777', tokenId: '1777', + address: '0x7ad05c1b87e93be306a9eadf80ea60d7648f1b6f', }, }, } diff --git a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/SendSummaryItem.stories.tsx b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/SendSummaryItem.stories.tsx index 7d54d8e886b..c7986bc1bbd 100644 --- a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/SendSummaryItem.stories.tsx +++ b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/SendSummaryItem.stories.tsx @@ -119,6 +119,7 @@ const baseNFTSendTx: Omit & { 'https://lh3.googleusercontent.com/9LokgAuB0Xqkio273GE0pY0WSJwOExFtFI1SkJT2jK-USvqFc-5if7ZP5PQ1h8s5YPimyJG5cSOdGGR2UaD3gTYMKAhj6yikYaw=s250', name: 'Froggy Friend #1777', tokenId: '1777', + address: '0x7ad05c1b87e93be306a9eadf80ea60d7648f1b6f', }, }, } diff --git a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem.stories.tsx b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem.stories.tsx index 6ecc64c8f8c..20a439ee89d 100644 --- a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem.stories.tsx +++ b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem.stories.tsx @@ -3,6 +3,7 @@ import { TradeType } from '@uniswap/sdk-core' import React from 'react' import { TokenDocument } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { UniverseChainId } from 'uniswap/src/types/chains' +import { buildCurrencyId, buildNativeCurrencyId } from 'uniswap/src/utils/currencyId' import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' import { SwapSummaryItem } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem' import TransactionSummaryLayout from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout' @@ -13,7 +14,6 @@ import { TransactionStatus, TransactionType, } from 'wallet/src/features/transactions/types' -import { buildCurrencyId, buildNativeCurrencyId } from 'wallet/src/utils/currencyId' const meta: Meta = { title: 'WIP/Activity Items', @@ -103,10 +103,7 @@ const baseSwapTx: Omit & { expectedInputCurrencyAmountRaw: '50000000000000000', maximumInputCurrencyAmountRaw: '50000000000000000', inputCurrencyId: buildNativeCurrencyId(UniverseChainId.Mainnet), - outputCurrencyId: buildCurrencyId( - UniverseChainId.Mainnet, - '0x6b175474e89094c44da98b954eedeac495271d0f' - ), + outputCurrencyId: buildCurrencyId(UniverseChainId.Mainnet, '0x6b175474e89094c44da98b954eedeac495271d0f'), transactedUSDValue: 105.21800000000002, }, } diff --git a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/WrapSummaryItem.stories.tsx b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/WrapSummaryItem.stories.tsx index be2d6ef490f..4747ec5db63 100644 --- a/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/WrapSummaryItem.stories.tsx +++ b/apps/mobile/src/features/transactions/SummaryCards/SummaryItems/WrapSummaryItem.stories.tsx @@ -97,14 +97,13 @@ const baseWrapTx: Omit & { typeInfo: WrapTr }, } -const baseUnwrapTx: Omit & { typeInfo: WrapTransactionInfo } = - { - ...baseWrapTx, - typeInfo: { - ...baseWrapTx.typeInfo, - unwrapped: true, - }, - } +const baseUnwrapTx: Omit & { typeInfo: WrapTransactionInfo } = { + ...baseWrapTx, + typeInfo: { + ...baseWrapTx.typeInfo, + unwrapped: true, + }, +} export const Wrap: StoryObj = { render: () => ( diff --git a/apps/mobile/src/features/transactions/TransactionPending/TransactionPending.tsx b/apps/mobile/src/features/transactions/TransactionPending/TransactionPending.tsx index c7899be822d..9560e60a3e7 100644 --- a/apps/mobile/src/features/transactions/TransactionPending/TransactionPending.tsx +++ b/apps/mobile/src/features/transactions/TransactionPending/TransactionPending.tsx @@ -5,11 +5,7 @@ import { Button, Flex, Text, TouchableArea } from 'ui/src' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { ElementName } from 'uniswap/src/features/telemetry/constants' import { WalletChainId } from 'uniswap/src/types/chains' -import { - TransactionDetails, - TransactionStatus, - isFinalizedTx, -} from 'wallet/src/features/transactions/types' +import { TransactionDetails, TransactionStatus, isFinalizedTx } from 'wallet/src/features/transactions/types' import { openTransactionLink } from 'wallet/src/utils/linking' type TransactionStatusProps = { @@ -60,10 +56,7 @@ export function TransactionPending({ {transaction && isFinalizedTx(transaction) ? ( - ) : null} diff --git a/apps/mobile/src/features/transactions/transfer/TransferFlow.tsx b/apps/mobile/src/features/transactions/transfer/TransferFlow.tsx index 1d2fd8bda5b..63dadc8c139 100644 --- a/apps/mobile/src/features/transactions/transfer/TransferFlow.tsx +++ b/apps/mobile/src/features/transactions/transfer/TransferFlow.tsx @@ -18,10 +18,8 @@ import { useBottomSheetContext } from 'uniswap/src/components/modals/BottomSheet import { HandleBar } from 'uniswap/src/components/modals/HandleBar' import Trace from 'uniswap/src/features/telemetry/Trace' import { ModalName, SectionName } from 'uniswap/src/features/telemetry/constants' -import { - TokenSelectorModal, - TokenSelectorVariation, -} from 'wallet/src/components/TokenSelector/TokenSelector' +import { currencyAddress } from 'uniswap/src/utils/currencyId' +import { TokenSelectorModal, TokenSelectorVariation } from 'wallet/src/components/TokenSelector/TokenSelector' import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal' import { useTransactionGasFee } from 'wallet/src/features/gas/hooks' import { GasFeeResult, GasSpeed } from 'wallet/src/features/gas/types' @@ -33,10 +31,7 @@ import { INITIAL_TRANSACTION_STATE, transactionStateReducer, } from 'wallet/src/features/transactions/transactionState/transactionState' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { TransferReview } from 'wallet/src/features/transactions/transfer/TransferReview' import { TransferTokenForm } from 'wallet/src/features/transactions/transfer/TransferTokenForm' import { useDerivedTransferInfo } from 'wallet/src/features/transactions/transfer/hooks/useDerivedTransferInfo' @@ -48,12 +43,8 @@ import { } from 'wallet/src/features/transactions/transfer/hooks/useTransferCallback' import { useTransferTransactionRequest } from 'wallet/src/features/transactions/transfer/hooks/useTransferTransactionRequest' import { useTransferWarnings } from 'wallet/src/features/transactions/transfer/hooks/useTransferWarnings' -import { - DerivedTransferInfo, - TokenSelectorFlow, -} from 'wallet/src/features/transactions/transfer/types' +import { DerivedTransferInfo, TokenSelectorFlow } from 'wallet/src/features/transactions/transfer/types' import { TransactionStep, TransferFlowProps } from 'wallet/src/features/transactions/types' -import { currencyAddress } from 'wallet/src/utils/currencyId' interface TransferFormProps { prefilledState?: TransactionState @@ -67,10 +58,7 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS const { fullWidth } = useDeviceDimensions() const { isSheetReady } = useBottomSheetContext() - const [state, dispatch] = useReducer( - transactionStateReducer, - prefilledState || INITIAL_TRANSACTION_STATE - ) + const [state, dispatch] = useReducer(transactionStateReducer, prefilledState || INITIAL_TRANSACTION_STATE) const derivedTransferInfo = useDerivedTransferInfo(state) const [showViewOnlyModal, setShowViewOnlyModal] = useState(false) const [step, setStep] = useState(TransactionStep.FORM) @@ -87,13 +75,12 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS txRequest, GasSpeed.Urgent, // stop polling for gas once transaction is submitted - step === TransactionStep.SUBMITTED || - warnings.some((warning) => warning.action === WarningAction.DisableReview) + step === TransactionStep.SUBMITTED || warnings.some((warning) => warning.action === WarningAction.DisableReview), ) const transferTxWithGasSettings = useMemo( (): providers.TransactionRequest => ({ ...txRequest, ...gasFee.params }), - [gasFee.params, txRequest] + [gasFee.params, txRequest], ) const gasWarning = useTransactionGasWarning({ @@ -107,10 +94,7 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS const parsedSendWarnings = useParsedSendWarnings(allWarnings) - const { onSelectCurrency, onHideTokenSelector } = useTokenSelectorActionHandlers( - dispatch, - TokenSelectorFlow.Transfer - ) + const { onSelectCurrency, onHideTokenSelector } = useTokenSelectorActionHandlers(dispatch, TokenSelectorFlow.Transfer) // optimization for not rendering InnerContent initially, // when modal is opened with recipient or token selector presented @@ -180,10 +164,7 @@ export function TransferFlow({ prefilledState, onClose }: TransferFormProps): JS height as 100% height doesn't include the handlebar height */} {step !== TransactionStep.SUBMITTED && ( - + )} {renderInnerContentRouter && isSheetReady && ( - } + icon={} modalName={ModalName.SwapWarning} severity={WarningSeverity.Low} title={t('send.warning.viewOnly.title')} @@ -253,10 +228,7 @@ type TransferInnerContentProps = { onReviewPrev: () => void onRetrySubmit: () => void setShowViewOnlyModal: (show: boolean) => void -} & Pick< - TransferFlowProps, - 'derivedInfo' | 'onClose' | 'dispatch' | 'gasFee' | 'txRequest' | 'warnings' | 'exactValue' -> +} & Pick function TransferInnerContent({ showingSelectorScreen, @@ -275,8 +247,7 @@ function TransferInnerContent({ }: TransferInnerContentProps): JSX.Element | null { // TODO: move this up in the tree to mobile specific flow const { walletNeedsRestore, openWalletRestoreModal } = useWalletRestore() - const { showNativeKeyboard, onDecimalPadLayout, isLayoutPending, onInputPanelLayout } = - useShouldShowNativeKeyboard() + const { showNativeKeyboard, onDecimalPadLayout, isLayoutPending, onInputPanelLayout } = useShouldShowNativeKeyboard() const { currencyAmounts, recipient, currencyInInfo, nftIn, chainId, txId } = derivedTransferInfo const transferERC20Callback = useTransferERC20Callback( @@ -286,7 +257,7 @@ function TransferInnerContent({ currencyInInfo ? currencyAddress(currencyInInfo.currency) : undefined, currencyAmounts[CurrencyField.INPUT]?.quotient.toString(), txRequest, - onReviewNext + onReviewNext, ) const transferNFTCallback = useTransferNFTCallback( txId, @@ -295,7 +266,7 @@ function TransferInnerContent({ nftIn?.nftContract?.address, nftIn?.tokenId, txRequest, - onReviewNext + onReviewNext, ) const onTransfer = (): void => { @@ -318,11 +289,7 @@ function TransferInnerContent({ case TransactionStep.SUBMITTED: return ( - + ) case TransactionStep.FORM: diff --git a/apps/mobile/src/features/transactions/transfer/TransferHeader.tsx b/apps/mobile/src/features/transactions/transfer/TransferHeader.tsx index 935efb69208..c833eec1dec 100644 --- a/apps/mobile/src/features/transactions/transfer/TransferHeader.tsx +++ b/apps/mobile/src/features/transactions/transfer/TransferHeader.tsx @@ -11,10 +11,7 @@ type HeaderContentProps = Pick & { setShowViewOnlyModal: Dispatch> } -export function TransferHeader({ - flowName, - setShowViewOnlyModal, -}: HeaderContentProps): JSX.Element { +export function TransferHeader({ flowName, setShowViewOnlyModal }: HeaderContentProps): JSX.Element { const colors = useSporeColors() const account = useActiveAccountWithThrow() const { t } = useTranslation() @@ -29,7 +26,8 @@ export function TransferHeader({ mt="$spacing8" pb="$spacing8" pl="$spacing12" - pr="$spacing16"> + pr="$spacing16" + > {flowName} @@ -41,13 +39,10 @@ export function TransferHeader({ justifyContent="center" px="$spacing8" py="$spacing4" - onPress={(): void => setShowViewOnlyModal(true)}> + onPress={(): void => setShowViewOnlyModal(true)} + > - + {t('swap.header.viewOnly')} diff --git a/apps/mobile/src/features/transactions/transfer/TransferStatus.tsx b/apps/mobile/src/features/transactions/transfer/TransferStatus.tsx index 1459053a3ca..04d07fd7f7e 100644 --- a/apps/mobile/src/features/transactions/transfer/TransferStatus.tsx +++ b/apps/mobile/src/features/transactions/transfer/TransferStatus.tsx @@ -6,18 +6,11 @@ import { AppTFunction } from 'ui/src/i18n/types' import { FiatCurrencyInfo } from 'uniswap/src/features/fiatOnRamp/types' import { NumberType } from 'utilities/src/format/types' import { useAppFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' -import { - LocalizationContextState, - useLocalizationContext, -} from 'wallet/src/features/language/LocalizationContext' +import { LocalizationContextState, useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useSelectTransaction } from 'wallet/src/features/transactions/hooks' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { DerivedTransferInfo } from 'wallet/src/features/transactions/transfer/types' -import { - TransactionDetails, - TransactionStatus, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { TransactionDetails, TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' import { useActiveAccountAddressWithThrow, useDisplayName } from 'wallet/src/features/wallet/hooks' type TransferStatusProps = { @@ -32,13 +25,12 @@ const getTextFromTransferStatus = ( fiatCurrencyInfo: FiatCurrencyInfo, derivedTransferInfo: DerivedTransferInfo, recipient: string | undefined, - transactionDetails?: TransactionDetails + transactionDetails?: TransactionDetails, ): { title: string description: string } => { - const { currencyInInfo, nftIn, currencyAmounts, isFiatInput, exactAmountFiat } = - derivedTransferInfo + const { currencyInInfo, nftIn, currencyAmounts, isFiatInput, exactAmountFiat } = derivedTransferInfo if ( !transactionDetails || transactionDetails.typeInfo.type !== TransactionType.Send || @@ -89,11 +81,7 @@ const getTextFromTransferStatus = ( } } -export function TransferStatus({ - derivedTransferInfo, - onNext, - onTryAgain, -}: TransferStatusProps): JSX.Element | null { +export function TransferStatus({ derivedTransferInfo, onNext, onTryAgain }: TransferStatusProps): JSX.Element | null { const { t } = useTranslation() const formatter = useLocalizationContext() const appFiatCurrencyInfo = useAppFiatCurrencyInfo() @@ -106,14 +94,7 @@ export function TransferStatus({ const displayName = useDisplayName(recipient, { includeUnitagSuffix: true }) const recipientName = displayName?.name ?? recipient const { title, description } = useMemo(() => { - return getTextFromTransferStatus( - t, - formatter, - appFiatCurrencyInfo, - derivedTransferInfo, - recipientName, - transaction - ) + return getTextFromTransferStatus(t, formatter, appFiatCurrencyInfo, derivedTransferInfo, recipientName, transaction) }, [t, formatter, appFiatCurrencyInfo, derivedTransferInfo, recipientName, transaction]) const onClose = useCallback(() => { diff --git a/apps/mobile/src/features/transactions/transfer/transferRewrite/TransferFlow.tsx b/apps/mobile/src/features/transactions/transfer/transferRewrite/TransferFlow.tsx index b55d7a4ae84..9610643d296 100644 --- a/apps/mobile/src/features/transactions/transfer/transferRewrite/TransferFlow.tsx +++ b/apps/mobile/src/features/transactions/transfer/transferRewrite/TransferFlow.tsx @@ -11,10 +11,7 @@ import { import { useWalletRestore } from 'src/features/wallet/hooks' import Trace from 'uniswap/src/features/telemetry/Trace' import { ModalName, SectionName } from 'uniswap/src/features/telemetry/constants' -import { - SwapFormContextProvider, - SwapFormState, -} from 'wallet/src/features/transactions/contexts/SwapFormContext' +import { SwapFormContextProvider, SwapFormState } from 'wallet/src/features/transactions/contexts/SwapFormContext' import { TransactionModal } from 'wallet/src/features/transactions/swap/TransactionModal' import { getFocusOnCurrencyFieldFromInitialState } from 'wallet/src/features/transactions/swap/hooks/useSwapPrefilledState' import { TradeProtocolPreference } from 'wallet/src/features/transactions/transactionState/types' @@ -37,7 +34,8 @@ export function TransferFlow(): JSX.Element { modalName={ModalName.Send} openWalletRestoreModal={openWalletRestoreModal} walletNeedsRestore={walletNeedsRestore} - onClose={onClose}> + onClose={onClose} + > @@ -99,7 +97,7 @@ function TransferContextsContainer({ children }: { children?: ReactNode }): JSX. tradeProtocolPreference: TradeProtocolPreference.Default, } : undefined, - [initialState] + [initialState], ) return ( diff --git a/apps/mobile/src/features/transactions/transfer/transferRewrite/TransferScreenContext.tsx b/apps/mobile/src/features/transactions/transfer/transferRewrite/TransferScreenContext.tsx index 354be19430f..fbe41b0778f 100644 --- a/apps/mobile/src/features/transactions/transfer/transferRewrite/TransferScreenContext.tsx +++ b/apps/mobile/src/features/transactions/transfer/transferRewrite/TransferScreenContext.tsx @@ -11,9 +11,7 @@ type TransferScreenContextState = { setScreen: (screen: TransferScreen) => void } -export const TransferScreenContext = createContext( - undefined -) +export const TransferScreenContext = createContext(undefined) // TODO: re-use the same context built in extension, and move that to shared folder. export function TransferScreenContextProvider({ children }: { children: ReactNode }): JSX.Element { @@ -31,7 +29,7 @@ export function TransferScreenContextProvider({ children }: { children: ReactNod screenRef, setScreen: wrappedSetScreen, }), - [screen, wrappedSetScreen] + [screen, wrappedSetScreen], ) return {children} @@ -41,9 +39,7 @@ export const useTransferScreenContext = (): TransferScreenContextState => { const transferContext = useContext(TransferScreenContext) if (transferContext === undefined) { - throw new Error( - '`useTransferScreenContext ` must be used inside of `TransferScreenContextProvider`' - ) + throw new Error('`useTransferScreenContext ` must be used inside of `TransferScreenContextProvider`') } return transferContext diff --git a/apps/mobile/src/features/tweaks/slice.ts b/apps/mobile/src/features/tweaks/slice.ts index 0a6bdd47f90..1e6a0c63ff9 100644 --- a/apps/mobile/src/features/tweaks/slice.ts +++ b/apps/mobile/src/features/tweaks/slice.ts @@ -11,10 +11,7 @@ export const slice = createSlice({ name: 'tweaks', initialState: initialTweaksState, reducers: { - setCustomEndpoint: ( - state, - { payload: { customEndpoint } }: PayloadAction<{ customEndpoint?: CustomEndpoint }> - ) => { + setCustomEndpoint: (state, { payload: { customEndpoint } }: PayloadAction<{ customEndpoint?: CustomEndpoint }>) => { state.customEndpoint = customEndpoint }, }, diff --git a/apps/mobile/src/features/unitags/ChooseProfilePictureScreen.tsx b/apps/mobile/src/features/unitags/ChooseProfilePictureScreen.tsx index f7fc81d3b3b..e5763d5cb66 100644 --- a/apps/mobile/src/features/unitags/ChooseProfilePictureScreen.tsx +++ b/apps/mobile/src/features/unitags/ChooseProfilePictureScreen.tsx @@ -94,7 +94,7 @@ export function ChooseProfilePictureScreen({ { source, hasENSAddress: !!ensName, - } + }, ) setIsClaiming(false) setClaimError(attemptClaimError) @@ -115,7 +115,8 @@ export function ChooseProfilePictureScreen({ return ( + title={t('unitags.onboarding.profile.title')} + > @@ -128,11 +129,9 @@ export function ChooseProfilePictureScreen({ p="$spacing4" position="absolute" right={-spacing.spacing2} - testID={ElementName.Edit}> - + testID={ElementName.Edit} + > + @@ -151,7 +150,8 @@ export function ChooseProfilePictureScreen({ size="medium" testID={ElementName.Continue} theme="primary" - onPress={onPressContinue}> + onPress={onPressContinue} + > {isClaiming ? ( @@ -173,28 +173,9 @@ export function ChooseProfilePictureScreen({ ) } -function ProfilePicture({ - address, - imageUri, -}: { - address: Maybe
- imageUri?: string -}): JSX.Element { +function ProfilePicture({ address, imageUri }: { address: Maybe
; imageUri?: string }): JSX.Element { if (address) { - return ( - - ) + return } - return ( - - ) + return } diff --git a/apps/mobile/src/features/unitags/ClaimUnitagScreen.tsx b/apps/mobile/src/features/unitags/ClaimUnitagScreen.tsx index c41d92c1de2..3ad4ffe64ff 100644 --- a/apps/mobile/src/features/unitags/ClaimUnitagScreen.tsx +++ b/apps/mobile/src/features/unitags/ClaimUnitagScreen.tsx @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { NativeStackScreenProps } from '@react-navigation/native-stack' import { ADDRESS_ZERO } from '@uniswap/v3-sdk' import { default as React, useCallback, useEffect, useState } from 'react' @@ -23,6 +22,7 @@ import { ElementName, ModalName, UnitagEventName } from 'uniswap/src/features/te import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding' import { MobileScreens, OnboardingScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile' +import { shortenAddress } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' import { ONE_SECOND_MS } from 'utilities/src/time/time' import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal' @@ -31,13 +31,8 @@ import { useCreateOnboardingAccountIfNone, useOnboardingContext, } from 'wallet/src/features/onboarding/OnboardingContext' -import { - UNITAG_SUFFIX, - UNITAG_SUFFIX_NO_LEADING_DOT, - UNITAG_VALID_REGEX, -} from 'wallet/src/features/unitags/constants' +import { UNITAG_SUFFIX, UNITAG_SUFFIX_NO_LEADING_DOT, UNITAG_VALID_REGEX } from 'wallet/src/features/unitags/constants' import { useCanClaimUnitagName } from 'wallet/src/features/unitags/hooks' -import { shortenAddress } from 'wallet/src/utils/addresses' import { useDynamicFontSizing } from 'wallet/src/utils/useDynamicFontSizing' const MAX_UNITAG_CHAR_LENGTH = 20 @@ -96,7 +91,7 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { const { onLayout, fontSize, onSetFontSize } = useDynamicFontSizing( MAX_CHAR_PIXEL_WIDTH, MAX_INPUT_FONT_SIZE, - MIN_INPUT_FONT_SIZE + MIN_INPUT_FONT_SIZE, ) useEffect(() => { @@ -113,7 +108,7 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { unitagInputContainerTranslateY.value - UNITAG_NAME_ANIMATE_DISTANCE_Y, { duration: ONE_SECOND_MS / 2, - } + }, ) setTimeout(() => { setShowTextInputView(true) @@ -122,13 +117,7 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { }) return unsubscribe - }, [ - navigation, - showTextInputView, - setShowTextInputView, - addressViewOpacity, - unitagInputContainerTranslateY, - ]) + }, [navigation, showTextInputView, setShowTextInputView, addressViewOpacity, unitagInputContainerTranslateY]) const onChangeTextInput = useCallback( (text: string): void => { @@ -146,7 +135,7 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { setUnitagInputValue(text?.trim()) }, - [inputPlaceholder, onSetFontSize] + [inputPlaceholder, onSetFontSize], ) const onPressAddressTooltip = (): void => { @@ -194,22 +183,17 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { initialDelay, withTiming(unitagInputContainerTranslateY.value + UNITAG_NAME_ANIMATE_DISTANCE_Y, { duration: translateYDuration, - }) + }), ) // Navigate to ChooseProfilePicture screen after initial delay + translation to allow animations to finish setTimeout(() => { - navigate( - entryPoint === OnboardingScreens.Landing - ? MobileScreens.OnboardingStack - : MobileScreens.UnitagStack, - { - screen: UnitagScreens.ChooseProfilePicture, - params: { unitag, entryPoint, address: unitagAddress, unitagFontSize: fontSize }, - } - ) + navigate(entryPoint === OnboardingScreens.Landing ? MobileScreens.OnboardingStack : MobileScreens.UnitagStack, { + screen: UnitagScreens.ChooseProfilePicture, + params: { unitag, entryPoint, address: unitagAddress, unitagFontSize: fontSize }, + }) }, initialDelay + translateYDuration) }, - [addressViewOpacity, entryPoint, unitagAddress, unitagInputContainerTranslateY, fontSize] + [addressViewOpacity, entryPoint, unitagAddress, unitagInputContainerTranslateY, fontSize], ) // Handle when useUnitagError completes loading and returns a result after onPressContinue is called @@ -261,20 +245,17 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { onLayout={(event): void => { onLayout(event) onSetFontSize(inputPlaceholder + UNITAG_SUFFIX_CHARS_ONLY) - }}> + }} + > {/* Fixed text that animates in when TextInput is animated out */} + style={{ transform: [{ translateY: unitagInputContainerTranslateY }] }} + > {!showTextInputView && ( - + )} @@ -285,7 +266,8 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { animation="quick" enterStyle={{ opacity: 0, x: 40 }} exitStyle={{ opacity: 0, x: 40 }} - gap="$none"> + gap="$none" + > + lineHeight={fonts.heading2.lineHeight} + > {UNITAG_SUFFIX} @@ -326,7 +309,8 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { alignItems="center" gap="$spacing8" style={addressViewAnimatedStyle} - onPress={onPressAddressTooltip}> + onPress={onPressAddressTooltip} + > {shortenAddress(unitagAddress ?? ADDRESS_ZERO)} @@ -334,27 +318,19 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { onPress={(): void => { Keyboard.dismiss() setShowInfoModal(true) - }}> + }} + > {canClaimUnitagNameError && unitagToCheck === unitagInputValue && ( - + {canClaimUnitagNameError}{' '} {requiresENSMatch && ( - ), + highlight: , }} i18nKey="unitags.onboarding.claimPeriod.link" /> @@ -383,7 +359,8 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { size="medium" testID={ElementName.Continue} theme="primary" - onPress={onPressContinue}> + onPress={onPressContinue} + > {isCheckingUnitag ? ( @@ -393,14 +370,9 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element { )} - {showInfoModal && ( - setShowInfoModal(false)} /> - )} + {showInfoModal && setShowInfoModal(false)} />} {showClaimPeriodInfoModal && ( - setShowClaimPeriodInfoModal(false)} - /> + setShowClaimPeriodInfoModal(false)} /> )} ) @@ -446,7 +418,8 @@ const InfoModal = ({ px="$spacing12" shadowColor="$neutral3" shadowOpacity={0.4} - shadowRadius="$spacing4"> + shadowRadius="$spacing4" + > {usernamePlaceholder} @@ -463,13 +436,7 @@ const InfoModal = ({ ) } -const ClaimPeriodInfoModal = ({ - onClose, - username, -}: { - onClose: () => void - username: string -}): JSX.Element => { +const ClaimPeriodInfoModal = ({ onClose, username }: { onClose: () => void; username: string }): JSX.Element => { const colors = useSporeColors() const { t } = useTranslation() @@ -478,17 +445,11 @@ const ClaimPeriodInfoModal = ({ backgroundIconColor={colors.surface1.get()} caption={t('unitags.onboarding.claimPeriod.description', { username })} closeText={t('common.button.close')} - icon={ - - } + icon={} modalName={ModalName.ENSClaimPeriod} title={t('unitags.onboarding.claimPeriod.title')} - onClose={onClose}> + onClose={onClose} + > ) diff --git a/apps/mobile/src/features/unitags/ConfirmationElements.tsx b/apps/mobile/src/features/unitags/ConfirmationElements.tsx deleted file mode 100644 index 3bd8fb70ec0..00000000000 --- a/apps/mobile/src/features/unitags/ConfirmationElements.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import { Flex, Image, Text, useSporeColors } from 'ui/src' -import { DAI_LOGO, ENS_LOGO, ETH_LOGO, FROGGY, OPENSEA_LOGO, USDC_LOGO } from 'ui/src/assets' -import HeartIcon from 'ui/src/assets/icons/heart.svg' -import SendIcon from 'ui/src/assets/icons/send-action.svg' -import { colors, iconSizes, imageSizes, opacify } from 'ui/src/theme' -import { Arrow } from 'wallet/src/components/icons/Arrow' - -export const FroggyElement = (): JSX.Element => { - return ( - - - - ) -} - -export const OpenseaElement = (): JSX.Element => { - return ( - - - - ) -} - -export const SwapElement = (): JSX.Element => { - const sporeColors = useSporeColors() - return ( - - - - - ETH - - - - - - - DAI - - - - ) -} - -export const ENSElement = (): JSX.Element => { - return ( - - - - ) -} - -export const ReceiveUSDCElement = (): JSX.Element => { - return ( - - - +100 - - - - ) -} - -export const SendElement = (): JSX.Element => { - return ( - - - - ) -} - -export const HeartElement = (): JSX.Element => { - return ( - - - - ) -} - -export const TextElement = ({ text }: { text: string }): JSX.Element => { - return ( - - - {text} - - - ) -} - -export const EmojiElement = ({ emoji }: { emoji: string }): JSX.Element => { - return ( - - - {emoji} - - - ) -} diff --git a/apps/mobile/src/features/unitags/EditUnitagProfileScreen.tsx b/apps/mobile/src/features/unitags/EditUnitagProfileScreen.tsx index fa479875d5e..a8454abf5e7 100644 --- a/apps/mobile/src/features/unitags/EditUnitagProfileScreen.tsx +++ b/apps/mobile/src/features/unitags/EditUnitagProfileScreen.tsx @@ -12,16 +12,7 @@ import { ChoosePhotoOptionsModal } from 'src/components/unitags/ChoosePhotoOptio import { DeleteUnitagModal } from 'src/components/unitags/DeleteUnitagModal' import { UnitagProfilePicture } from 'src/components/unitags/UnitagProfilePicture' import { HeaderRadial, solidHeaderProps } from 'src/features/externalProfile/ProfileHeader' -import { - Button, - Flex, - LinearGradient, - ScrollView, - Text, - getUniconColors, - useIsDarkMode, - useSporeColors, -} from 'ui/src' +import { Button, Flex, LinearGradient, ScrollView, Text, getUniconColors, useIsDarkMode, useSporeColors } from 'ui/src' import { Pen, TripleDots } from 'ui/src/components/icons' import { borderRadii, fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme' import { useExtractedColors } from 'ui/src/utils/colors' @@ -33,6 +24,7 @@ import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks' import { ProfileMetadata } from 'uniswap/src/features/unitags/types' import { UniverseChainId } from 'uniswap/src/types/chains' import { MobileScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile' +import { shortenAddress } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' import { isIOS } from 'utilities/src/platform' import { normalizeTwitterUsername } from 'utilities/src/primitives/string' @@ -47,14 +39,13 @@ import { useWalletSigners } from 'wallet/src/features/wallet/context' import { useAccount } from 'wallet/src/features/wallet/hooks' import { DisplayNameType } from 'wallet/src/features/wallet/types' import { useAppDispatch } from 'wallet/src/state' -import { shortenAddress } from 'wallet/src/utils/addresses' const BIO_TEXT_INPUT_LINES = 6 const isProfileMetadataEdited = ( loading: boolean, updatedMetadata: ProfileMetadata, - initialMetadata?: ProfileMetadata + initialMetadata?: ProfileMetadata, ): boolean => { return ( !loading && @@ -75,9 +66,7 @@ function isFieldEdited(a: string | undefined, b: string | undefined): boolean { } } -export function EditUnitagProfileScreen({ - route, -}: UnitagStackScreenProp): JSX.Element { +export function EditUnitagProfileScreen({ route }: UnitagStackScreenProp): JSX.Element { const { address, unitag, entryPoint } = route.params const { t } = useTranslation() const colors = useSporeColors() @@ -115,11 +104,7 @@ export function EditUnitagProfileScreen({ twitter: twitterInput, } - const profileMetadataEdited = isProfileMetadataEdited( - updateResponseLoading, - updatedMetadata, - unitagMetadata - ) + const profileMetadataEdited = isProfileMetadataEdited(updateResponseLoading, updatedMetadata, unitagMetadata) useEffect(() => { // Only want to set values on first time unitag loads, when we have not yet made the PUT request @@ -195,9 +180,7 @@ export function EditUnitagProfileScreen({ ? { ...updatedMetadata, // Add Date.now() to the end to ensure the resulting URL is not cached by devices - avatar: avatarUploadUrlResponse?.avatarUrl - ? avatarUploadUrlResponse.avatarUrl + `?${Date.now()}` - : undefined, + avatar: avatarUploadUrlResponse?.avatarUrl ? avatarUploadUrlResponse.avatarUrl + `?${Date.now()}` : undefined, } : updatedMetadata @@ -226,7 +209,7 @@ export function EditUnitagProfileScreen({ pushNotification({ type: AppNotificationType.Success, title: t('unitags.notification.profile.title'), - }) + }), ) triggerRefetchUnitags() if (uploadedNewAvatar) { @@ -245,7 +228,7 @@ export function EditUnitagProfileScreen({ pushNotification({ type: AppNotificationType.Error, errorMessage: t('unitags.notification.profile.error'), - }) + }), ) } @@ -263,7 +246,8 @@ export function EditUnitagProfileScreen({ contentContainerStyle={styles.expand} // Disable the keyboard avoiding view when the modals are open, otherwise background elements will shift up when the user is editing their username enabled={!showDeleteUnitagModal && !showChangeUnitagModal} - style={styles.base}> + style={styles.base} + > + }} + > @@ -293,17 +278,17 @@ export function EditUnitagProfileScreen({ p="$spacing16" onPressBack={ // If entering from confirmation screen, back btn navigates to home - entryPoint === UnitagScreens.UnitagConfirmation - ? (): void => navigate(MobileScreens.Home) - : undefined - }> + entryPoint === UnitagScreens.UnitagConfirmation ? (): void => navigate(MobileScreens.Home) : undefined + } + > {t('settings.setting.wallet.action.editProfile')} + showsVerticalScrollIndicator={false} + > @@ -324,24 +309,12 @@ export function EditUnitagProfileScreen({ style={styles.headerGradient} /> {avatarImageUri && avatarColors?.primary ? ( - + ) : null} - + - + - + right={-spacing.spacing2} + > + @@ -440,7 +411,8 @@ export function EditUnitagProfileScreen({ mx="$spacing24" size="medium" theme="primary" - onPress={onPressSaveChanges}> + onPress={onPressSaveChanges} + > {t('common.button.save')} {showAvatarModal && ( @@ -454,18 +426,10 @@ export function EditUnitagProfileScreen({ )} {showDeleteUnitagModal && ( - setShowDeleteUnitagModal(false)} - /> + setShowDeleteUnitagModal(false)} /> )} {showChangeUnitagModal && ( - setShowChangeUnitagModal(false)} - /> + setShowChangeUnitagModal(false)} /> )} ) diff --git a/apps/mobile/src/features/unitags/UnitagConfirmationScreen.tsx b/apps/mobile/src/features/unitags/UnitagConfirmationScreen.tsx index ac52fa574e9..c93ebfd0722 100644 --- a/apps/mobile/src/features/unitags/UnitagConfirmationScreen.tsx +++ b/apps/mobile/src/features/unitags/UnitagConfirmationScreen.tsx @@ -4,6 +4,11 @@ import { navigate } from 'src/app/navigation/rootNavigation' import { UnitagStackScreenProp } from 'src/app/navigation/types' import { Screen } from 'src/components/layout/Screen' import { UnitagWithProfilePicture } from 'src/components/unitags/UnitagWithProfilePicture' +import { AnimatePresence, Button, Flex, Text, useDeviceInsets } from 'ui/src' +import { AnimateInOrder } from 'ui/src/animations' +import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' +import { spacing } from 'ui/src/theme' +import { MobileScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile' import { ENSElement, EmojiElement, @@ -14,12 +19,7 @@ import { SendElement, SwapElement, TextElement, -} from 'src/screens/Onboarding/OnboardingElements' -import { AnimatePresence, Button, Flex, Text, useDeviceInsets } from 'ui/src' -import { AnimateInOrder } from 'ui/src/animations' -import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' -import { spacing } from 'ui/src/theme' -import { MobileScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile' +} from 'wallet/src/components/landing/elements' import { UNITAG_SUFFIX } from 'wallet/src/features/unitags/constants' export function UnitagConfirmationScreen({ @@ -62,7 +62,7 @@ export function UnitagConfirmationScreen({ { element: , coordinates: { x: 1, y: 2 } }, { element: , coordinates: { x: 3.5, y: 2.5 } }, ], - [t] + [t], ) return ( @@ -75,7 +75,8 @@ export function UnitagConfirmationScreen({ enterStyle={{ opacity: 0, scale: 0.5 }} exitStyle={{ opacity: 0, scale: 0.5 }} index={1} - position="absolute"> + position="absolute" + > + position="absolute" + > + {...getInsetPropsForCoordinates(boxWidth, coordinates.x, coordinates.y)} + > {element} ))} - + @@ -145,7 +144,7 @@ export function UnitagConfirmationScreen({ const getInsetPropsForCoordinates = ( boxWidth: number, x: number, - y: number + y: number, ): { top?: number; right?: number; bottom?: number; left?: number } => { const unitSize = 10 const unit = boxWidth / unitSize diff --git a/apps/mobile/src/features/unitags/UnitagName.tsx b/apps/mobile/src/features/unitags/UnitagName.tsx index 414101aa1e5..d316fe1e86a 100644 --- a/apps/mobile/src/features/unitags/UnitagName.tsx +++ b/apps/mobile/src/features/unitags/UnitagName.tsx @@ -21,13 +21,15 @@ export function UnitagName({ enterStyle={{ opacity: 0 }} exitStyle={{ opacity: 0 }} gap="$spacing20" - opacity={opacity}> + opacity={opacity} + > + lineHeight={fonts.heading2.lineHeight} + > {name} + top={-spacing.spacing4} + > diff --git a/apps/mobile/src/features/wallet/hooks.ts b/apps/mobile/src/features/wallet/hooks.ts index 1eff07fc5c5..173b2c716ac 100644 --- a/apps/mobile/src/features/wallet/hooks.ts +++ b/apps/mobile/src/features/wallet/hooks.ts @@ -36,7 +36,7 @@ export function useWalletRestore(params?: { openModalImmediately?: boolean }): { setWalletNeedsRestore(hasImportedSeedPhrase && !addresses.length) } openRestoreWalletModalIfNeeded().catch((error) => - logger.error(error, { tags: { file: 'wallet/hooks', function: 'useWalletRestore' } }) + logger.error(error, { tags: { file: 'wallet/hooks', function: 'useWalletRestore' } }), ) }, [dispatch, hasImportedSeedPhrase, isRestoreWalletEnabled]) diff --git a/apps/mobile/src/features/wallet/saga.ts b/apps/mobile/src/features/wallet/saga.ts index 7ada1246464..e070dda55c6 100644 --- a/apps/mobile/src/features/wallet/saga.ts +++ b/apps/mobile/src/features/wallet/saga.ts @@ -19,7 +19,7 @@ function* onRestoreMnemonicComplete() { pushNotification({ type: AppNotificationType.Success, title: i18n.t('notification.restore.success'), - }) + }), ) yield* call(dispatchNavigationAction, StackActions.replace(MobileScreens.Home)) } diff --git a/apps/mobile/src/features/walletConnect/api.ts b/apps/mobile/src/features/walletConnect/api.ts index fcd22b0d068..9a2d43c429f 100644 --- a/apps/mobile/src/features/walletConnect/api.ts +++ b/apps/mobile/src/features/walletConnect/api.ts @@ -1,6 +1,6 @@ import { getOneSignalPushToken } from 'src/features/notifications/Onesignal' import { config } from 'uniswap/src/config' -import { isJestRun } from 'utilities/src/environment' +import { isTestEnv } from 'utilities/src/environment' import { logger } from 'utilities/src/logger/logger' import { isAndroid } from 'utilities/src/platform' @@ -36,7 +36,7 @@ export async function registerWCClientForPushNotifications(clientId: string): Pr await fetch(`${WC_HOSTED_PUSH_SERVER_URL}/clients`, request) } catch (error) { // Shouldn't log if this is a jest run to avoid logging after a test completes - if (!isJestRun) { + if (!isTestEnv()) { logger.error(error, { tags: { file: 'walletConnectApi', function: 'registerWCv2ClientForPushNotifications' }, }) diff --git a/apps/mobile/src/features/walletConnect/saga.ts b/apps/mobile/src/features/walletConnect/saga.ts index b5c3fff7bad..ad2bf357931 100644 --- a/apps/mobile/src/features/walletConnect/saga.ts +++ b/apps/mobile/src/features/walletConnect/saga.ts @@ -74,15 +74,13 @@ function createWalletConnectChannel(): EventChannel { * and the proposal namespaces (chains, methods, events) */ const sessionProposalHandler = async ( - proposalEvent: Omit, 'topic'> + proposalEvent: Omit, 'topic'>, ): Promise => { const { params: proposal } = proposalEvent emit({ type: 'session_proposal', proposal }) } - const sessionRequestHandler = async ( - request: Web3WalletTypes.SessionRequest - ): Promise => { + const sessionRequestHandler = async (request: Web3WalletTypes.SessionRequest): Promise => { emit({ type: 'session_request', request }) } @@ -188,7 +186,7 @@ function* handleSessionProposal(proposal: ProposalTypes.Struct) { source: 'walletconnect', }, }, - }) + }), ) } catch (e) { // Reject pending session if required namespaces includes non-EVM chains or unsupported EVM chains @@ -197,9 +195,7 @@ function* handleSessionProposal(proposal: ProposalTypes.Struct) { reason: getSdkError('UNSUPPORTED_CHAINS'), }) - const chainLabels = WALLET_SUPPORTED_CHAIN_IDS.map( - (chainId) => UNIVERSE_CHAIN_INFO[chainId].label - ).join(', ') + const chainLabels = WALLET_SUPPORTED_CHAIN_IDS.map((chainId) => UNIVERSE_CHAIN_INFO[chainId].label).join(', ') const confirmed = yield* call( showAlert, @@ -207,7 +203,7 @@ function* handleSessionProposal(proposal: ProposalTypes.Struct) { i18n.t('walletConnect.error.connection.message', { chainNames: chainLabels, dappName: dapp.name, - }) + }), ) if (confirmed) { yield* put(setHasPendingSessionError(false)) @@ -220,7 +216,7 @@ function* handleSessionProposal(proposal: ProposalTypes.Struct) { 'WalletConnectSaga', 'sessionProposalHandler', 'Rejected session proposal due to invalid proposal namespaces: ', - e + e, ) } } @@ -248,36 +244,25 @@ function* handleSessionRequest(sessionRequest: PendingRequestTypes.Struct) { addRequest({ account, request, - }) + }), ) break } case EthMethod.EthSendTransaction: { - const { account, request } = parseTransactionRequest( - method, - topic, - id, - chainId, - dapp, - requestParams - ) + const { account, request } = parseTransactionRequest(method, topic, id, chainId, dapp, requestParams) yield* put( addRequest({ account, request, - }) + }), ) break } default: // Reject request for an invalid method - logger.warn( - 'WalletConnectSaga', - 'sessionRequestHandler', - `Session request method is unsupported: ${method}` - ) + logger.warn('WalletConnectSaga', 'sessionRequestHandler', `Session request method is unsupported: ${method}`) yield* call([wcWeb3Wallet, wcWeb3Wallet.respondSessionRequest], { topic, response: { @@ -317,7 +302,7 @@ function* populateActiveSessions() { // Verify account address for session exists in wallet's accounts const matchingAccount = Object.values(accounts).find( - (account) => account.address.toLowerCase() === accountAddress.toLowerCase() + (account) => account.address.toLowerCase() === accountAddress.toLowerCase(), ) if (!matchingAccount) { continue @@ -344,7 +329,7 @@ function* populateActiveSessions() { namespaces: session.namespaces, }, account: accountAddress, - }) + }), ) } } diff --git a/apps/mobile/src/features/walletConnect/selectors.ts b/apps/mobile/src/features/walletConnect/selectors.ts index e06d6763169..79c527d34cd 100644 --- a/apps/mobile/src/features/walletConnect/selectors.ts +++ b/apps/mobile/src/features/walletConnect/selectors.ts @@ -21,11 +21,7 @@ export const selectSessions = return Object.values(wcAccount.sessions) } -export const makeSelectSessions = (): Selector< - MobileState, - WalletConnectSession[] | undefined, - [Maybe
] -> => +export const makeSelectSessions = (): Selector]> => createSelector( (state: MobileState) => state.walletConnect.byAccount, (_: MobileState, address: Maybe
) => address, @@ -40,7 +36,7 @@ export const makeSelectSessions = (): Selector< } return Object.values(wcAccount.sessions) - } + }, ) export const selectPendingRequests = (state: MobileState): WalletConnectRequest[] => { diff --git a/apps/mobile/src/features/walletConnect/signWcRequestSaga.ts b/apps/mobile/src/features/walletConnect/signWcRequestSaga.ts index ffeef9c2330..89a0fecc631 100644 --- a/apps/mobile/src/features/walletConnect/signWcRequestSaga.ts +++ b/apps/mobile/src/features/walletConnect/signWcRequestSaga.ts @@ -1,26 +1,14 @@ import { providers } from 'ethers' import { wcWeb3Wallet } from 'src/features/walletConnect/saga' -import { - TransactionRequest, - UwuLinkErc20Request, -} from 'src/features/walletConnect/walletConnectSlice' +import { TransactionRequest, UwuLinkErc20Request } from 'src/features/walletConnect/walletConnectSlice' import { call, put } from 'typed-redux-saga' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' -import { - DappInfo, - EthMethod, - EthSignMethod, - UwULinkMethod, - WalletConnectEvent, -} from 'uniswap/src/types/walletConnect' +import { DappInfo, EthMethod, EthSignMethod, UwULinkMethod, WalletConnectEvent } from 'uniswap/src/types/walletConnect' import { logger } from 'utilities/src/logger/logger' import { AssetType } from 'wallet/src/entities/assets' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' -import { - SendTransactionParams, - sendTransaction, -} from 'wallet/src/features/transactions/sendTransactionSaga' +import { SendTransactionParams, sendTransaction } from 'wallet/src/features/transactions/sendTransactionSaga' import { TransactionType } from 'wallet/src/features/transactions/types' import { Account } from 'wallet/src/features/wallet/accounts/types' import { getSignerManager } from 'wallet/src/features/wallet/context' @@ -62,15 +50,12 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams pushNotification({ type: AppNotificationType.Success, title: 'Checked in', - }) + }), ) } } else if (method === EthMethod.SignTypedData || method === EthMethod.SignTypedDataV4) { signature = yield* call(signTypedDataMessage, params.message, account, signerManager) - } else if ( - method === EthMethod.EthSendTransaction && - params.request.type === UwULinkMethod.Erc20Send - ) { + } else if (method === EthMethod.EthSendTransaction && params.request.type === UwULinkMethod.Erc20Send) { const txParams: SendTransactionParams = { chainId: params.transaction.chainId || UniverseChainId.Mainnet, account, @@ -107,7 +92,7 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams pushNotification({ type: AppNotificationType.TransactionPending, chainId: txParams.chainId, - }) + }), ) } @@ -132,7 +117,7 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams }).catch((error) => logger.error(error, { tags: { file: 'walletConnect/saga', function: 'signWcRequest/uwulink' }, - }) + }), ) } } catch (error) { @@ -155,7 +140,7 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams imageUrl: params.dapp.icon ?? null, chainId, address: account.address, - }) + }), ) logger.error(error, { tags: { file: 'walletConnect/saga', function: 'signWcRequest' } }) } @@ -163,5 +148,5 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams export const { wrappedSaga: signWcRequestSaga, actions: signWcRequestActions } = createSaga( signWcRequest, - 'signWalletConnect' + 'signWalletConnect', ) diff --git a/apps/mobile/src/features/walletConnect/utils.ts b/apps/mobile/src/features/walletConnect/utils.ts index 23d0f1da835..ceee1373455 100644 --- a/apps/mobile/src/features/walletConnect/utils.ts +++ b/apps/mobile/src/features/walletConnect/utils.ts @@ -3,10 +3,10 @@ import { Web3WalletTypes } from '@walletconnect/web3wallet' import { utils } from 'ethers' import { wcWeb3Wallet } from 'src/features/walletConnect/saga' import { SignRequest, TransactionRequest } from 'src/features/walletConnect/walletConnectSlice' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { WalletChainId } from 'uniswap/src/types/chains' import { EthMethod, EthSignMethod } from 'uniswap/src/types/walletConnect' import { logger } from 'utilities/src/logger/logger' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' /** * Construct WalletConnect 2.0 session namespaces to complete a new pairing. Used when approving a new pairing request. @@ -18,7 +18,7 @@ import { toSupportedChainId } from 'wallet/src/features/chains/utils' */ export const getSessionNamespaces = ( account: Address, - proposalNamespaces: ProposalTypes.RequiredNamespaces + proposalNamespaces: ProposalTypes.RequiredNamespaces, ): SessionTypes.Namespaces => { // Below inspired from https://github.com/WalletConnect/web-examples/blob/main/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx#L63 const namespaces: SessionTypes.Namespaces = {} @@ -46,9 +46,7 @@ export const getSupportedWalletConnectChains = (chains?: string[]): WalletChainI return } - return chains - .map((chain) => getChainIdFromEIP155String(chain)) - .filter((c): c is WalletChainId => Boolean(c)) + return chains.map((chain) => getChainIdFromEIP155String(chain)).filter((c): c is WalletChainId => Boolean(c)) } /** @@ -89,7 +87,7 @@ export const parseSignRequest = ( internalId: number, chainId: WalletChainId, dapp: SignClientTypes.Metadata, - requestParams: Web3WalletTypes.SessionRequest['params']['request']['params'] + requestParams: Web3WalletTypes.SessionRequest['params']['request']['params'], ): { account: Address; request: SignRequest } => { const { address, rawMessage, message } = getAddressAndMessageToSign(method, requestParams) return { @@ -131,7 +129,7 @@ export const parseTransactionRequest = ( internalId: number, chainId: WalletChainId, dapp: SignClientTypes.Metadata, - requestParams: Web3WalletTypes.SessionRequest['params']['request']['params'] + requestParams: Web3WalletTypes.SessionRequest['params']['request']['params'], ): { account: Address; request: TransactionRequest } => { // Omit gasPrice and nonce in tx sent from dapp since it is calculated later const { from, to, data, gasLimit, value } = requestParams[0] @@ -170,7 +168,7 @@ export const parseTransactionRequest = ( */ export function getAddressAndMessageToSign( ethMethod: EthSignMethod, - params: Web3WalletTypes.SessionRequest['params']['request']['params'] + params: Web3WalletTypes.SessionRequest['params']['request']['params'], ): { address: string; rawMessage: string; message: string | null } { switch (ethMethod) { case EthMethod.PersonalSign: diff --git a/apps/mobile/src/features/walletConnect/walletConnectSlice.ts b/apps/mobile/src/features/walletConnect/walletConnectSlice.ts index 87c80c91f41..3c32ad23661 100644 --- a/apps/mobile/src/features/walletConnect/walletConnectSlice.ts +++ b/apps/mobile/src/features/walletConnect/walletConnectSlice.ts @@ -1,13 +1,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { ProposalTypes, SessionTypes } from '@walletconnect/types' import { WalletChainId } from 'uniswap/src/types/chains' -import { - DappInfo, - EthMethod, - EthSignMethod, - EthTransaction, - UwULinkMethod, -} from 'uniswap/src/types/walletConnect' +import { DappInfo, EthMethod, EthSignMethod, EthTransaction, UwULinkMethod } from 'uniswap/src/types/walletConnect' export type WalletConnectPendingSession = { id: string @@ -64,9 +58,7 @@ export interface UwuLinkErc20Request extends BaseRequest { export type WalletConnectRequest = SignRequest | TransactionRequest | UwuLinkErc20Request -export const isTransactionRequest = ( - request: WalletConnectRequest -): request is TransactionRequest => +export const isTransactionRequest = (request: WalletConnectRequest): request is TransactionRequest => request.type === EthMethod.EthSendTransaction || request.type === UwULinkMethod.Erc20Send export interface WalletConnectState { @@ -91,10 +83,7 @@ const slice = createSlice({ name: 'walletConnect', initialState: initialWalletConnectState, reducers: { - addSession: ( - state, - action: PayloadAction<{ account: string; wcSession: WalletConnectSession }> - ) => { + addSession: (state, action: PayloadAction<{ account: string; wcSession: WalletConnectSession }>) => { const { wcSession, account } = action.payload state.byAccount[account] ??= { sessions: {} } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion @@ -102,10 +91,7 @@ const slice = createSlice({ state.pendingSession = null }, - updateSession: ( - state, - action: PayloadAction<{ account: string; wcSession: WalletConnectSession }> - ) => { + updateSession: (state, action: PayloadAction<{ account: string; wcSession: WalletConnectSession }>) => { const { wcSession, account } = action.payload const wcAccount = state.byAccount[account] if (wcAccount) { @@ -135,10 +121,7 @@ const slice = createSlice({ }) }, - addPendingSession: ( - state, - action: PayloadAction<{ wcSession: WalletConnectPendingSession }> - ) => { + addPendingSession: (state, action: PayloadAction<{ wcSession: WalletConnectPendingSession }>) => { const { wcSession } = action.payload state.pendingSession = wcSession }, @@ -147,22 +130,14 @@ const slice = createSlice({ state.pendingSession = null }, - addRequest: ( - state, - action: PayloadAction<{ request: WalletConnectRequest; account: string }> - ) => { + addRequest: (state, action: PayloadAction<{ request: WalletConnectRequest; account: string }>) => { const { request } = action.payload state.pendingRequests.push(request) }, - removeRequest: ( - state, - action: PayloadAction<{ requestInternalId: string; account: string }> - ) => { + removeRequest: (state, action: PayloadAction<{ requestInternalId: string; account: string }>) => { const { requestInternalId } = action.payload - state.pendingRequests = state.pendingRequests.filter( - (req) => req.internalId !== requestInternalId - ) + state.pendingRequests = state.pendingRequests.filter((req) => req.internalId !== requestInternalId) }, setDidOpenFromDeepLink: (state, action: PayloadAction) => { diff --git a/apps/mobile/src/features/widgets/widgets.ts b/apps/mobile/src/features/widgets/widgets.ts index f7425053ce7..367ee40607e 100644 --- a/apps/mobile/src/features/widgets/widgets.ts +++ b/apps/mobile/src/features/widgets/widgets.ts @@ -57,14 +57,15 @@ export const setFavoritesUserDefaults = (currencyIds: CurrencyId[]): void => { } export const setAccountAddressesUserDefaults = (accounts: Account[]): void => { - const userDefaultAccounts: Array<{ address: string; name: Maybe; isSigner: boolean }> = - accounts.map((account: Account) => { + const userDefaultAccounts: Array<{ address: string; name: Maybe; isSigner: boolean }> = accounts.map( + (account: Account) => { return { address: account.address, name: account.name, isSigner: account.type === AccountType.SignerMnemonic, } - }) + }, + ) const data = { accounts: userDefaultAccounts, } diff --git a/apps/mobile/src/index.ts b/apps/mobile/src/index.ts index 14285a59908..e58b46e762f 100644 --- a/apps/mobile/src/index.ts +++ b/apps/mobile/src/index.ts @@ -12,7 +12,7 @@ declare global { style: Record[] | Record, config?: { shouldMatchAllProps?: boolean - } + }, ): R } } diff --git a/apps/mobile/src/lib/RNEthersRs.ts b/apps/mobile/src/lib/RNEthersRs.ts index 9d9dc5ef31c..cb2e34a848d 100644 --- a/apps/mobile/src/lib/RNEthersRs.ts +++ b/apps/mobile/src/lib/RNEthersRs.ts @@ -32,18 +32,12 @@ export function getAddressesForStoredPrivateKeys(): Promise { } // returns the address for the mnemonic -export function generateAddressForMnemonic( - mnemonic: string, - derivationIndex: number -): Promise { +export function generateAddressForMnemonic(mnemonic: string, derivationIndex: number): Promise { return RNEthersRS.generateAddressForMnemonic(mnemonic, derivationIndex) } // returns the address of the generated key -export function generateAndStorePrivateKey( - mnemonicId: string, - derivationIndex: number -): Promise { +export function generateAndStorePrivateKey(mnemonicId: string, derivationIndex: number): Promise { return RNEthersRS.generateAndStorePrivateKey(mnemonicId, derivationIndex) } @@ -51,11 +45,7 @@ export function removePrivateKey(address: string): Promise { return RNEthersRS.removePrivateKey(address) } -export function signTransactionHashForAddress( - address: string, - hash: string, - chainId: number -): Promise { +export function signTransactionHashForAddress(address: string, hash: string, chainId: number): Promise { return RNEthersRS.signTransactionHashForAddress(address, hash, chainId) } @@ -63,10 +53,6 @@ export function signMessageForAddress(address: string, message: string): Promise return RNEthersRS.signMessageForAddress(address, message) } -export function signHashForAddress( - address: string, - hash: string, - chainId: number -): Promise { +export function signHashForAddress(address: string, hash: string, chainId: number): Promise { return RNEthersRS.signHashForAddress(address, hash, chainId) } diff --git a/apps/mobile/src/screens/AppLoadingScreen.tsx b/apps/mobile/src/screens/AppLoadingScreen.tsx index 2054bfd92b0..e1b0bb3e2f8 100644 --- a/apps/mobile/src/screens/AppLoadingScreen.tsx +++ b/apps/mobile/src/screens/AppLoadingScreen.tsx @@ -1,31 +1,147 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' import dayjs from 'dayjs' +import { isEnrolledAsync } from 'expo-local-authentication' import { t } from 'i18next' -import { useEffect, useState } from 'react' +import { isNumber } from 'lodash' +import { useCallback, useEffect, useState } from 'react' import { OnboardingStackParamList } from 'src/app/navigation/types' import { SplashScreen } from 'src/features/appLoading/SplashScreen' -import { useOnDeviceRecoveryData } from 'src/screens/Import/useOnDeviceRecoveryData' +import { useBiometricContext } from 'src/features/biometrics/context' +import { useBiometricAppSettings } from 'src/features/biometrics/hooks' +import { + NotificationPermission, + useNotificationOSPermissionsEnabled, +} from 'src/features/notifications/hooks/useNotificationOSPermissionsEnabled' +import { RecoveryWalletInfo, useOnDeviceRecoveryData } from 'src/screens/Import/useOnDeviceRecoveryData' +import { hideSplashScreen } from 'src/utils/splashScreen' +import { DynamicConfigs } from 'uniswap/src/features/gating/configs' +import { useDynamicConfig } from 'uniswap/src/features/gating/hooks' +import { MobileEventName } from 'uniswap/src/features/telemetry/constants' +import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding' import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' import { logger } from 'utilities/src/logger/logger' import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { AccountType } from 'wallet/src/features/wallet/accounts/types' +import { AccountType, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' +import { selectAnyAddressHasNotificationsEnabled } from 'wallet/src/features/wallet/selectors' import { setFinishedOnboarding } from 'wallet/src/features/wallet/slice' -import { useAppDispatch } from 'wallet/src/state' +import { useAppDispatch, useAppSelector } from 'wallet/src/state' export const SPLASH_SCREEN = { uri: 'SplashScreen' } type Props = NativeStackScreenProps +function useFinishAutomatedRecovery(navigation: Props['navigation']): { + finishRecovery: (mnemonicId: string, recoveryWalletInfos: RecoveryWalletInfo[]) => void +} { + const dispatch = useAppDispatch() + const { setRecoveredImportedAccounts, finishOnboarding } = useOnboardingContext() + + const notificationOSPermission = useNotificationOSPermissionsEnabled() + const hasAnyNotificationsEnabled = useAppSelector(selectAnyAddressHasNotificationsEnabled) + const { deviceSupportsBiometrics } = useBiometricContext() + const { requiredForTransactions: isBiometricAuthEnabled } = useBiometricAppSettings() + + const importAccounts = useCallback( + async (mnemonicId: string, recoveryWalletInfos: RecoveryWalletInfo[]) => { + const accountsToImport = recoveryWalletInfos.map((addressInfo, index): SignerMnemonicAccount => { + return { + type: AccountType.SignerMnemonic, + mnemonicId, + name: t('onboarding.wallet.defaultName', { number: index + 1 }), + address: addressInfo.address, + derivationIndex: addressInfo.derivationIndex, + timeImportedMs: dayjs().valueOf(), + } + }) + setRecoveredImportedAccounts(accountsToImport) + }, + [setRecoveredImportedAccounts], + ) + + const finishRecovery = useCallback( + async (mnemonicId: string, recoveryWalletInfos: RecoveryWalletInfo[]) => { + await importAccounts(mnemonicId, recoveryWalletInfos) + + const isBiometricsEnrolled = await isEnrolledAsync() + + const showNotificationScreen = notificationOSPermission !== NotificationPermission.Enabled + const showBiometricsScreen = + (deviceSupportsBiometrics && (!isBiometricAuthEnabled || !isBiometricsEnrolled)) ?? false + + sendAnalyticsEvent(MobileEventName.AutomatedOnDeviceRecoveryTriggered, { + showNotificationScreen, + showBiometricsScreen, + notificationOSPermission, + hasAnyNotificationsEnabled, + deviceSupportsBiometrics, + isBiometricsEnrolled, + isBiometricAuthEnabled, + }) + + hideSplashScreen() + + // Notification screen should always navigate to biometrics screen if supported + // This is acceptable because we're already triggering a setup screen + // and biometrics is the more important one to include + if (showNotificationScreen) { + navigation.replace(OnboardingScreens.Notifications, { + importType: ImportType.OnDeviceRecovery, + entryPoint: OnboardingEntryPoint.FreshInstallOrReplace, + }) + } else if (showBiometricsScreen) { + navigation.replace(OnboardingScreens.Security, { + importType: ImportType.OnDeviceRecovery, + entryPoint: OnboardingEntryPoint.FreshInstallOrReplace, + }) + } else { + await finishOnboarding(ImportType.OnDeviceRecovery) + dispatch(setFinishedOnboarding({ finishedOnboarding: true })) + } + }, + [ + deviceSupportsBiometrics, + dispatch, + finishOnboarding, + hasAnyNotificationsEnabled, + importAccounts, + isBiometricAuthEnabled, + navigation, + notificationOSPermission, + ], + ) + + return { + finishRecovery, + } +} + +const FALLBACK_APP_LOADING_TIMEOUT_MS = 15000 + export function AppLoadingScreen({ navigation }: Props): JSX.Element | null { const dispatch = useAppDispatch() - const { finishOnboarding } = useOnboardingContext() + const onDeviceRecoveryConfig = useDynamicConfig(DynamicConfigs.OnDeviceRecovery) + const appLoadingTimeoutMs = onDeviceRecoveryConfig.get( + 'appLoadingTimeoutMs', + FALLBACK_APP_LOADING_TIMEOUT_MS, + isNumber, + ) + const maxMnemonicsToLoad = onDeviceRecoveryConfig.getValue('maxMnemonicsToLoad', 20) as number + const [finished, setFinished] = useState(false) const [mnemonicIds, setMnemonicIds] = useState() const { significantRecoveryWalletInfos, loading } = useOnDeviceRecoveryData(mnemonicIds?.[0]) + const { finishRecovery } = useFinishAutomatedRecovery(navigation) + + const navigateToLanding = useCallback((): void => { + navigation.replace(OnboardingScreens.Landing, { + importType: ImportType.NotYetSelected, + entryPoint: OnboardingEntryPoint.FreshInstallOrReplace, + }) + }, [navigation]) useEffect(() => { Keyring.getMnemonicIds() @@ -38,15 +154,19 @@ export function AppLoadingScreen({ navigation }: Props): JSX.Element | null { }) }, []) - // Logic to determine what screen to show on app load useEffect(() => { - const navigateToLanding = (): void => { - navigation.replace(OnboardingScreens.Landing, { - importType: ImportType.NotYetSelected, - entryPoint: OnboardingEntryPoint.FreshInstallOrReplace, - }) - } + const timeout = setTimeout(() => { + if (!finished) { + setFinished(true) + navigateToLanding() + logger.warn('AppLoadingScreen', 'useTimeout', `Loading timeout triggered after ${appLoadingTimeoutMs}ms`) + } + }, appLoadingTimeoutMs) + return () => clearTimeout(timeout) + }, [appLoadingTimeoutMs, finished, navigateToLanding]) + // Logic to determine what screen to show on app load + useEffect(() => { async function checkOnDeviceRecovery(): Promise { if (!mnemonicIds || loading || finished) { return @@ -60,28 +180,15 @@ export function AppLoadingScreen({ navigation }: Props): JSX.Element | null { if (mnemonicIdsCount === 1 && firstMnemonicId) { if (significantRecoveryWalletInfos.length) { - await finishOnboarding( - ImportType.OnDeviceRecovery, - significantRecoveryWalletInfos.map((addressInfo, index) => { - return { - type: AccountType.SignerMnemonic, - mnemonicId: firstMnemonicId, - name: t('onboarding.wallet.defaultName', { number: index + 1 }), - address: addressInfo.address, - derivationIndex: addressInfo.derivationIndex, - timeImportedMs: dayjs().valueOf(), - } - }) - ) - dispatch(setFinishedOnboarding({ finishedOnboarding: true })) + finishRecovery(firstMnemonicId, significantRecoveryWalletInfos) } else { navigateToLanding() } } else if (mnemonicIdsCount > 1) { navigation.replace(OnboardingScreens.OnDeviceRecovery, { - importType: ImportType.RestoreMnemonic, + importType: ImportType.OnDeviceRecovery, entryPoint: OnboardingEntryPoint.FreshInstallOrReplace, - mnemonicIds, + mnemonicIds: mnemonicIds.slice(0, maxMnemonicsToLoad), }) } else { navigateToLanding() @@ -92,10 +199,12 @@ export function AppLoadingScreen({ navigation }: Props): JSX.Element | null { }) }, [ dispatch, - finishOnboarding, + finishRecovery, finished, loading, + maxMnemonicsToLoad, mnemonicIds, + navigateToLanding, navigation, significantRecoveryWalletInfos, ]) diff --git a/apps/mobile/src/screens/DevScreen.tsx b/apps/mobile/src/screens/DevScreen.tsx index 2e19efebdb3..b7260c6dce2 100644 --- a/apps/mobile/src/screens/DevScreen.tsx +++ b/apps/mobile/src/screens/DevScreen.tsx @@ -35,7 +35,7 @@ export function DevScreen(): JSX.Element { dispatch( createAccountsActions.trigger({ accounts: [await createOnboardingAccount(sortedMnemonicAccounts)], - }) + }), ) } @@ -46,11 +46,7 @@ export function DevScreen(): JSX.Element { const onPressShowError = (): void => { const address = activeAccount?.address if (!address) { - logger.debug( - 'DevScreen', - 'onPressShowError', - 'Cannot show error if activeAccount is undefined' - ) + logger.debug('DevScreen', 'onPressShowError', 'Cannot show error if activeAccount is undefined') return } @@ -59,7 +55,7 @@ export function DevScreen(): JSX.Element { type: AppNotificationType.Error, address, errorMessage: 'A scary new error has happened. Be afraid!!', - }) + }), ) } @@ -92,11 +88,7 @@ export function DevScreen(): JSX.Element { {Object.values(MobileScreens).map((s) => ( - activateWormhole(s)}> + activateWormhole(s)}> {s} ))} diff --git a/apps/mobile/src/screens/EducationScreen.tsx b/apps/mobile/src/screens/EducationScreen.tsx index 391c93650ac..ff1163a905c 100644 --- a/apps/mobile/src/screens/EducationScreen.tsx +++ b/apps/mobile/src/screens/EducationScreen.tsx @@ -17,7 +17,7 @@ export function EducationScreen({ importType, entryPoint, }), - [entryPoint, importType, type] + [entryPoint, importType, type], ) return ( diff --git a/apps/mobile/src/screens/ExchangeTransferConnecting.tsx b/apps/mobile/src/screens/ExchangeTransferConnecting.tsx index 72e21e439d4..dcb551c5474 100644 --- a/apps/mobile/src/screens/ExchangeTransferConnecting.tsx +++ b/apps/mobile/src/screens/ExchangeTransferConnecting.tsx @@ -2,19 +2,19 @@ import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useAppDispatch } from 'src/app/hooks' import { Screen } from 'src/components/layout/Screen' -import { FiatOnRampConnectingView } from 'src/features/fiatOnRamp/FiatOnRampConnecting' -import { ServiceProviderLogoStyles } from 'src/features/fiatOnRamp/constants' import { useFiatOnRampTransactionCreator } from 'src/features/fiatOnRamp/hooks' import { Flex, useIsDarkMode } from 'ui/src' import { uniswapUrls } from 'uniswap/src/constants/urls' +import { FiatOnRampConnectingView } from 'uniswap/src/features/fiatOnRamp/FiatOnRampConnectingView' import { useFiatOnRampAggregatorTransferWidgetQuery } from 'uniswap/src/features/fiatOnRamp/api' +import { ServiceProviderLogoStyles } from 'uniswap/src/features/fiatOnRamp/constants' import { FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types' +import { getServiceProviderLogo } from 'uniswap/src/features/fiatOnRamp/utils' import { InstitutionTransferEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { UniverseChainId } from 'uniswap/src/types/chains' import { ONE_SECOND_MS } from 'utilities/src/time/time' import { useTimeout } from 'utilities/src/time/timing' -import { getServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils' import { ImageUri } from 'wallet/src/features/images/ImageUri' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' @@ -36,15 +36,12 @@ export function ExchangeTransferConnecting({ const activeAccountAddress = useActiveAccountAddressWithThrow() const [timeoutElapsed, setTimeoutElapsed] = useState(false) - const initialTypeInfo = useMemo( - () => ({ serviceProviderLogo: serviceProvider.logos }), - [serviceProvider.logos] - ) + const initialTypeInfo = useMemo(() => ({ serviceProviderLogo: serviceProvider.logos }), [serviceProvider.logos]) const { externalTransactionId, dispatchAddTransaction } = useFiatOnRampTransactionCreator( activeAccountAddress, UniverseChainId.Mainnet, - initialTypeInfo + initialTypeInfo, ) const onError = useCallback((): void => { @@ -52,7 +49,7 @@ export function ExchangeTransferConnecting({ pushNotification({ type: AppNotificationType.Error, errorMessage: t('common.error.general'), - }) + }), ) onClose() }, [dispatch, onClose, t]) @@ -113,7 +110,8 @@ export function ExchangeTransferConnecting({ alignItems="center" height={ServiceProviderLogoStyles.icon.height} justifyContent="center" - width={ServiceProviderLogoStyles.icon.width}> + width={ServiceProviderLogoStyles.icon.width} + > } diff --git a/apps/mobile/src/screens/ExploreScreen.tsx b/apps/mobile/src/screens/ExploreScreen.tsx index 6481e3fd4f6..b6b0e91cc05 100644 --- a/apps/mobile/src/screens/ExploreScreen.tsx +++ b/apps/mobile/src/screens/ExploreScreen.tsx @@ -11,7 +11,6 @@ import { SearchEmptySection } from 'src/components/explore/search/SearchEmptySec import { SearchResultsSection } from 'src/components/explore/search/SearchResultsSection' import { Screen } from 'src/components/layout/Screen' import { VirtualizedList } from 'src/components/layout/VirtualizedList' -import { useReduxModalBackHandler } from 'src/features/modals/hooks' import { selectModalState } from 'src/features/modals/selectModalState' import { ColorTokens, Flex, flexStyles, useIsDarkMode } from 'ui/src' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' @@ -29,8 +28,6 @@ export function ExploreScreen(): JSX.Element { const { isSheetReady } = useBottomSheetContext() - useReduxModalBackHandler(ModalName.Explore) - // The ExploreStack is not directly accessible from outside // (e.g., navigating from Home to NFTItem within ExploreStack), due to its mount within BottomSheetModal. // To bypass this limitation, we use an initialState to define a specific screen within ExploreStack. diff --git a/apps/mobile/src/screens/ExternalProfileScreen.tsx b/apps/mobile/src/screens/ExternalProfileScreen.tsx index 9e1cbf201ed..9c34dcfd34d 100644 --- a/apps/mobile/src/screens/ExternalProfileScreen.tsx +++ b/apps/mobile/src/screens/ExternalProfileScreen.tsx @@ -43,7 +43,7 @@ export function ExternalProfileScreen({ { key: SectionName.ProfileNftsTab, title: t('home.nfts.title') }, { key: SectionName.ProfileActivityTab, title: t('home.activity.title') }, ], - [t] + [t], ) const containerStyle = useMemo>( @@ -51,7 +51,7 @@ export function ExternalProfileScreen({ ...TAB_STYLES.tabListInner, paddingBottom: insets.bottom + TAB_STYLES.tabListInner.paddingBottom, }), - [insets.bottom] + [insets.bottom], ) const emptyComponentStyle = useMemo>( @@ -60,7 +60,7 @@ export function ExternalProfileScreen({ paddingHorizontal: spacing.spacing36, paddingBottom: insets.bottom, }), - [insets.bottom] + [insets.bottom], ) const sharedProps = useMemo( @@ -69,7 +69,7 @@ export function ExternalProfileScreen({ loadingContainerStyle: containerStyle, emptyComponentStyle, }), - [containerStyle, emptyComponentStyle] + [containerStyle, emptyComponentStyle], ) const renderTab = useCallback( @@ -93,12 +93,7 @@ export function ExternalProfileScreen({ ) case SectionName.ProfileNftsTab: return ( - + ) case SectionName.ProfileTokensTab: return ( @@ -112,7 +107,7 @@ export function ExternalProfileScreen({ } return null }, - [address, sharedProps, renderedInModal] + [address, sharedProps, renderedInModal], ) const renderTabBar = useCallback( @@ -138,7 +133,7 @@ export function ExternalProfileScreen({ /> ) }, - [colors.surface1, colors.surface3, tabIndex, tabs] + [colors.surface1, colors.surface3, tabIndex, tabs], ) const traceProperties = useMemo( @@ -147,17 +142,13 @@ export function ExternalProfileScreen({ walletName: displayName?.name, displayNameType: displayName?.type ? DisplayNameType[displayName.type] : undefined, }), - [address, displayName?.name, displayName?.type] + [address, displayName?.name, displayName?.type], ) return ( - + ({ serviceProviderLogo: serviceProvider?.logos }), - [serviceProvider?.logos] - ) + const initialTypeInfo = useMemo(() => ({ serviceProviderLogo: serviceProvider?.logos }), [serviceProvider?.logos]) const { externalTransactionId, dispatchAddTransaction } = useFiatOnRampTransactionCreator( activeAccountAddress, quoteCurrency.currencyInfo?.currency.chainId ?? UniverseChainId.Mainnet, - initialTypeInfo + initialTypeInfo, ) const onError = useCallback((): void => { @@ -70,7 +66,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | pushNotification({ type: AppNotificationType.Error, errorMessage: t('common.error.general'), - }) + }), ) navigation.goBack() }, [dispatch, navigation, t]) @@ -91,7 +87,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | externalSessionId: externalTransactionId, redirectUrl: `${uniswapUrls.redirectUrlBase}/?screen=transaction&fiatOnRamp=true&userAddress=${activeAccountAddress}`, } - : skipToken + : skipToken, ) useTimeout(() => { @@ -105,12 +101,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | } async function navigateToWidget(widgetUrl: string): Promise { dispatch(closeModal({ name: ModalName.FiatOnRampAggregator })) - if ( - serviceProvider && - quoteCurrency?.meldCurrencyCode && - baseCurrencyInfo && - quotesSections?.[0]?.data?.[0] - ) { + if (serviceProvider && quoteCurrency?.meldCurrencyCode && baseCurrencyInfo && quotesSections?.[0]?.data?.[0]) { sendAnalyticsEvent(FiatOnRampEventName.FiatOnRampWidgetOpened, { externalTransactionId, serviceProvider: serviceProvider.serviceProvider, @@ -166,7 +157,8 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | alignItems="center" height={ServiceProviderLogoStyles.icon.height} justifyContent="center" - width={ServiceProviderLogoStyles.icon.width}> + width={ServiceProviderLogoStyles.icon.width} + > } @@ -178,7 +170,8 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | position="absolute" px="$spacing24" textAlign="center" - variant="body3"> + variant="body3" + > {t('fiatOnRamp.connection.terms', { serviceProvider: serviceProvider.name })} diff --git a/apps/mobile/src/screens/FiatOnRampScreen.tsx b/apps/mobile/src/screens/FiatOnRampScreen.tsx index 5ed695dbf09..e5854f6b8b7 100644 --- a/apps/mobile/src/screens/FiatOnRampScreen.tsx +++ b/apps/mobile/src/screens/FiatOnRampScreen.tsx @@ -34,6 +34,7 @@ import { FiatOnRampCurrency, InitialQuoteSelection, } from 'uniswap/src/features/fiatOnRamp/types' +import { getServiceProviderLogo } from 'uniswap/src/features/fiatOnRamp/utils' import { FiatOnRampEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { UniverseEventProperties } from 'uniswap/src/features/telemetry/types' @@ -43,7 +44,6 @@ import { DEFAULT_DELAY, useDebounce } from 'utilities/src/time/timing' import { DecimalPadLegacy } from 'wallet/src/components/legacy/DecimalPadLegacy' import { useLocalFiatToUSDConverter } from 'wallet/src/features/fiatCurrency/hooks' import { useFiatOnRampAggregatorTransactionQuery } from 'wallet/src/features/fiatOnRamp/api' -import { getServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' @@ -51,7 +51,7 @@ type Props = NativeStackScreenProps ({ uri: getServiceProviderLogo(sp.logos, isDarkMode) })) - .filter((sp) => !!sp.uri) + serviceProviders.map((sp) => ({ uri: getServiceProviderLogo(sp.logos, isDarkMode) })).filter((sp) => !!sp.uri), ) } @@ -120,8 +115,7 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { setSelection({ start, end: end ?? start }) } - const { showNativeKeyboard, onDecimalPadLayout, isLayoutPending, onInputPanelLayout } = - useShouldShowNativeKeyboard() + const { showNativeKeyboard, onDecimalPadLayout, isLayoutPending, onInputPanelLayout } = useShouldShowNativeKeyboard() const { appFiatCurrencySupportedInMeld, meldSupportedFiatCurrency, supportedFiatCurrencies } = useMeldFiatCurrencySupportInfo(countryCode) @@ -161,7 +155,7 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { if (serviceProvidersResponse?.serviceProviders && quotes) { const quotesServiceProviderNames = quotes.map((q) => q.serviceProvider) const serviceProviders = serviceProvidersResponse.serviceProviders.filter( - (sp) => quotesServiceProviderNames.indexOf(sp.serviceProvider) !== -1 + (sp) => quotesServiceProviderNames.indexOf(sp.serviceProvider) !== -1, ) preloadServiceProviderLogos(serviceProviders, isDarkMode) } @@ -178,24 +172,13 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { } if (type === InitialQuoteSelection.MostRecent) { const otherQuotes = quotes.filter((item) => item !== quote) - setQuotesSections([ - { data: [quote], type }, - ...(otherQuotes.length ? [{ data: otherQuotes }] : []), - ]) + setQuotesSections([{ data: [quote], type }, ...(otherQuotes.length ? [{ data: otherQuotes }] : [])]) } else { setQuotesSections([{ data: quotes, type }]) } setSelectedQuote(quote) } - }, [ - prevQuotes, - quotes, - selectedQuote, - setQuotesSections, - setSelectedQuote, - t, - transactionResponse, - ]) + }, [prevQuotes, quotes, selectedQuote, setQuotesSections, setSelectedQuote, t, transactionResponse]) useEffect(() => { if (!quotes && (quotesError || serviceProvidersError || !amount)) { @@ -204,15 +187,13 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { } }, [amount, quotesError, serviceProvidersError, quotes, setQuotesSections, setSelectedQuote]) - const onSelectCountry: ComponentProps['onSelectCountry'] = ( - country - ): void => { + const onSelectCountry: ComponentProps['onSelectCountry'] = (country): void => { dispatch( pushNotification({ type: AppNotificationType.ChooseCountry, countryName: country.displayName, countryCode: country.countryCode, - }) + }), ) setSelectingCountry(false) // UI does not allow to set the state @@ -285,14 +266,14 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { // We only support predefined amounts for certain currencies. const predefinedAmountsSupported = PREDEFINED_AMOUNTS_SUPPORTED_CURRENCIES.includes( - meldSupportedFiatCurrency.code.toLowerCase() + meldSupportedFiatCurrency.code.toLowerCase(), ) const notAvailableInThisRegion = supportedFiatCurrencies?.length === 0 const { errorText, errorColor } = useParseFiatOnRampError( !notAvailableInThisRegion && (quotesError || serviceProvidersError), - meldSupportedFiatCurrency.code + meldSupportedFiatCurrency.code, ) return ( @@ -300,12 +281,7 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { {isSheetReady && ( - + {t('common.button.buy')} + onLayout={onDecimalPadLayout} + > {!showNativeKeyboard && ( ): JSX.Element => { const onPress = (): void => { @@ -69,10 +68,7 @@ export function FiatOnRampServiceProvidersScreen({ navigation }: Props): JSX.Ele return ( {baseCurrencyInfo && ( - + )} ) @@ -85,11 +81,7 @@ export function FiatOnRampServiceProvidersScreen({ navigation }: Props): JSX.Ele }): JSX.Element => ( {type === InitialQuoteSelection.Best ? null : type === InitialQuoteSelection.MostRecent ? ( - + ) : ( @@ -106,13 +98,7 @@ export function FiatOnRampServiceProvidersScreen({ navigation }: Props): JSX.Ele - + {t('fiatOnRamp.checkout.title')} diff --git a/apps/mobile/src/screens/HomeScreen.tsx b/apps/mobile/src/screens/HomeScreen.tsx index 266e2fc37ec..580a5305ecd 100644 --- a/apps/mobile/src/screens/HomeScreen.tsx +++ b/apps/mobile/src/screens/HomeScreen.tsx @@ -48,15 +48,7 @@ import { useWalletRestore } from 'src/features/wallet/hooks' import { removePendingSession } from 'src/features/walletConnect/walletConnectSlice' import { HomeScreenTabIndex } from 'src/screens/HomeScreenTabIndex' import { hideSplashScreen } from 'src/utils/splashScreen' -import { - Flex, - HapticFeedback, - Text, - TouchableArea, - useDeviceInsets, - useMedia, - useSporeColors, -} from 'ui/src' +import { Flex, HapticFeedback, Text, TouchableArea, useDeviceInsets, useMedia, useSporeColors } from 'ui/src' import ReceiveIcon from 'ui/src/assets/icons/arrow-down-circle.svg' import BuyIcon from 'ui/src/assets/icons/buy.svg' import ScanIcon from 'ui/src/assets/icons/scan-home.svg' @@ -85,10 +77,7 @@ import { useSelectAddressHasNotifications } from 'wallet/src/features/notificati import { setNotificationStatus } from 'wallet/src/features/notifications/slice' import { PortfolioBalance } from 'wallet/src/features/portfolio/PortfolioBalance' import { TokenBalanceListRow } from 'wallet/src/features/portfolio/TokenBalanceListContext' -import { - useCanActiveAddressClaimUnitag, - useShowExtensionPromoBanner, -} from 'wallet/src/features/unitags/hooks' +import { useCanActiveAddressClaimUnitag, useShowExtensionPromoBanner } from 'wallet/src/features/unitags/hooks' import { AccountType } from 'wallet/src/features/wallet/accounts/types' import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' @@ -154,7 +143,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. } setTabIndex(newTabIndex) }, - [props?.route.params?.tab] + [props?.route.params?.tab], ) const [isLayoutReady, setIsLayoutReady] = useState(false) @@ -165,7 +154,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. heightCollapsed: insets.top, heightExpanded: headerHeight, }), - [headerHeight, insets.top] + [headerHeight, insets.top], ) const { heightCollapsed, heightExpanded } = headerConfig const headerHeightDiff = heightExpanded - heightCollapsed @@ -177,20 +166,16 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. const tokensTabScrollValue = useSharedValue(0) const tokensTabScrollHandler = useAnimatedScrollHandler( - (event) => (tokensTabScrollValue.value = event.contentOffset.y) + (event) => (tokensTabScrollValue.value = event.contentOffset.y), ) const nftsTabScrollValue = useSharedValue(0) - const nftsTabScrollHandler = useAnimatedScrollHandler( - (event) => (nftsTabScrollValue.value = event.contentOffset.y) - ) + const nftsTabScrollHandler = useAnimatedScrollHandler((event) => (nftsTabScrollValue.value = event.contentOffset.y)) const activityTabScrollValue = useSharedValue(0) const activityTabScrollHandler = useAnimatedScrollHandler( - (event) => (activityTabScrollValue.value = event.contentOffset.y) + (event) => (activityTabScrollValue.value = event.contentOffset.y), ) const feedTabScrollValue = useSharedValue(0) - const feedTabScrollHandler = useAnimatedScrollHandler( - (event) => (feedTabScrollValue.value = event.contentOffset.y) - ) + const feedTabScrollHandler = useAnimatedScrollHandler((event) => (feedTabScrollValue.value = event.contentOffset.y)) const tokensTabScrollRef = useAnimatedRef>() // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -209,13 +194,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. return activityTabScrollValue.value } return feedTabScrollValue.value - }, [ - activityTabScrollValue, - feedTabScrollValue, - nftsTabScrollValue, - tabIndex, - tokensTabScrollValue, - ]) + }, [activityTabScrollValue, feedTabScrollValue, nftsTabScrollValue, tabIndex, tokensTabScrollValue]) // clear the notification indicator if the user is on the activity tab const hasNotifications = useSelectAddressHasNotifications(activeAccount.address) @@ -259,10 +238,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. setTabIndex(HomeScreenTabIndex.Tokens) } else if (currentTabIndex.value === HomeScreenTabIndex.NFTs) { nftsTabScrollRef.current?.scrollToOffset({ offset: 0, animated: true }) - } else if ( - currentTabIndex.value === HomeScreenTabIndex.Activity && - isActivityTabAtTop.value - ) { + } else if (currentTabIndex.value === HomeScreenTabIndex.Activity && isActivityTabAtTop.value) { setTabIndex(HomeScreenTabIndex.NFTs) } else if (currentTabIndex.value === HomeScreenTabIndex.Activity) { activityTabScrollRef.current?.scrollToOffset({ offset: 0, animated: true }) @@ -270,7 +246,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. tokensTabScrollRef.current?.scrollToOffset({ offset: 0, animated: true }) } }, - }) + }), ) const translateY = useDerivedValue(() => { @@ -298,7 +274,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. nftsTabScrollValue, tokensTabScrollRef, tokensTabScrollValue, - ] + ], ) const { sync } = useScrollSync(currentTabIndex, scrollPairs, headerConfig) @@ -312,16 +288,14 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. dispatch( openModal({ name: forAggregatorEnabled ? ModalName.FiatOnRampAggregator : ModalName.FiatOnRamp, - }) + }), ), - [dispatch, forAggregatorEnabled] + [dispatch, forAggregatorEnabled], ) const onPressScan = useCallback(() => { // in case we received a pending session from a previous scan after closing modal dispatch(removePendingSession()) - dispatch( - openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.ScanQr }) - ) + dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.ScanQr })) }, [dispatch]) const onPressSend = useCallback(() => dispatch(openModal({ name: ModalName.Send })), [dispatch]) const onPressReceive = useCallback(() => { @@ -329,14 +303,11 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. openModal( cexTransferProviders.length > 0 ? { name: ModalName.ReceiveCryptoModal, initialState: cexTransferProviders } - : { name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr } - ) + : { name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }, + ), ) }, [dispatch, cexTransferProviders]) - const onPressViewOnlyLabel = useCallback( - () => dispatch(openModal({ name: ModalName.ViewOnlyExplainer })), - [dispatch] - ) + const onPressViewOnlyLabel = useCallback(() => dispatch(openModal({ name: ModalName.ViewOnlyExplainer })), [dispatch]) // Hide actions when active account isn't a signer account. const isSignerAccount = activeAccount.type === AccountType.SignerMnemonic @@ -380,16 +351,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. onPress: onPressScan, }, ], - [ - buyLabel, - sendLabel, - scanLabel, - receiveLabel, - onPressBuy, - onPressScan, - onPressSend, - onPressReceive, - ] + [buyLabel, sendLabel, scanLabel, receiveLabel, onPressBuy, onPressScan, onPressSend, onPressReceive], ) const { canClaimUnitag } = useCanActiveAddressClaimUnitag() @@ -412,9 +374,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. } else if (showExtensionPromoBanner) { return ( - setShowExtensionPromoModal(true)} - /> + setShowExtensionPromoModal(true)} /> ) } @@ -432,13 +392,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. ) : ( - + {viewOnlyLabel} @@ -448,22 +402,14 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. {promoBanner} ) - }, [ - activeAccount.address, - isSignerAccount, - actions, - onPressViewOnlyLabel, - viewOnlyLabel, - promoBanner, - ]) + }, [activeAccount.address, isSignerAccount, actions, onPressViewOnlyLabel, viewOnlyLabel, promoBanner]) const paddingTop = headerHeight + TAB_BAR_HEIGHT + TAB_STYLES.tabListInner.paddingTop - const paddingBottom = - insets.bottom + SWAP_BUTTON_HEIGHT + TAB_STYLES.tabListInner.paddingBottom + spacing.spacing12 + const paddingBottom = insets.bottom + SWAP_BUTTON_HEIGHT + TAB_STYLES.tabListInner.paddingBottom + spacing.spacing12 const contentContainerStyle = useMemo>( () => ({ paddingTop, paddingBottom }), - [paddingTop, paddingBottom] + [paddingTop, paddingBottom], ) const emptyComponentStyle = useMemo>( @@ -473,7 +419,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. paddingLeft: media.short ? spacing.none : spacing.spacing12, paddingRight: media.short ? spacing.none : spacing.spacing12, }), - [dimensions.fullHeight, media.short, paddingBottom, paddingTop] + [dimensions.fullHeight, media.short, paddingBottom, paddingTop], ) const sharedProps = useMemo( @@ -484,24 +430,24 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. onScrollEndDrag: sync, scrollEventThrottle: TAB_VIEW_SCROLL_THROTTLE, }), - [contentContainerStyle, emptyComponentStyle, sync] + [contentContainerStyle, emptyComponentStyle, sync], ) const tabBarStyle = useMemo>( () => [{ top: headerHeight }, translatedStyle], - [headerHeight, translatedStyle] + [headerHeight, translatedStyle], ) const headerContainerStyle = useMemo>( () => [TAB_STYLES.headerContainer, { paddingTop: insets.top }, translatedStyle], - [insets.top, translatedStyle] + [insets.top, translatedStyle], ) const statusBarStyle = useAnimatedStyle(() => ({ backgroundColor: interpolateColor( currentScrollValue.value, [0, headerHeightDiff], - [colors.surface1.val, colors.surface1.val] + [colors.surface1.val, colors.surface1.val], ), })) @@ -552,7 +498,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. routes, tabBarStyle, tabIndex, - ] + ], ) const [refreshing, setRefreshing] = useState(false) @@ -662,7 +608,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. activityTabScrollHandler, feedTabScrollRef, feedTabScrollHandler, - ] + ], ) // Hides lock screen on next js render cycle, ensuring this component is loaded when the screen is hidden @@ -693,9 +639,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX. width="100%" zIndex="$sticky" /> - {showExtensionPromoModal && ( - setShowExtensionPromoModal(false)} /> - )} + {showExtensionPromoModal && setShowExtensionPromoModal(false)} />} ) } @@ -760,7 +704,8 @@ function ActionButton({ fill backgroundColor="$DEP_backgroundActionButton" borderRadius="$rounded20" - p="$spacing16"> + p="$spacing16" + > + shadowRadius={!isDarkMode ? '$spacing8' : undefined} + > {importOptions.map(({ title, blurb, icon, nav, importType, name }, i) => ( - + => - handleOnPress(OnboardingScreens.WatchWallet, ImportType.Watch) - }> + onPress={(): Promise => handleOnPress(OnboardingScreens.WatchWallet, ImportType.Watch)} + > {t('account.wallet.button.watch')} diff --git a/apps/mobile/src/screens/Import/OnDeviceRecoveryScreen.tsx b/apps/mobile/src/screens/Import/OnDeviceRecoveryScreen.tsx index 194977a9807..84f9f900f90 100644 --- a/apps/mobile/src/screens/Import/OnDeviceRecoveryScreen.tsx +++ b/apps/mobile/src/screens/Import/OnDeviceRecoveryScreen.tsx @@ -1,6 +1,7 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack' import dayjs from 'dayjs' -import React, { useState } from 'react' +import { isNumber } from 'lodash' +import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { ScrollView } from 'react-native-gesture-handler' import { OnboardingStackParamList } from 'src/app/navigation/types' @@ -15,6 +16,8 @@ import { Flex, Image, Text, TouchableArea, useSporeColors } from 'ui/src' import { UNISWAP_LOGO } from 'ui/src/assets' import { PapersText } from 'ui/src/components/icons' import { iconSizes } from 'ui/src/theme' +import { DynamicConfigs } from 'uniswap/src/features/gating/configs' +import { useDynamicConfig } from 'uniswap/src/features/gating/hooks' import Trace from 'uniswap/src/features/telemetry/Trace' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' import { ImportType, OnboardingEntryPoint } from 'uniswap/src/types/onboarding' @@ -25,13 +28,12 @@ import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningM import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext' import { WarningSeverity } from 'wallet/src/features/transactions/WarningModal/types' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { AccountType } from 'wallet/src/features/wallet/accounts/types' -import { setFinishedOnboarding } from 'wallet/src/features/wallet/slice' -import { useAppDispatch } from 'wallet/src/state' +import { AccountType, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' type Props = NativeStackScreenProps const LOADING_COUNT = 3 +const FALLBACK_RECOVERY_LOADING_TIMEOUT_MS = 60000 export function OnDeviceRecoveryScreen({ navigation, @@ -41,14 +43,19 @@ export function OnDeviceRecoveryScreen({ }: Props): JSX.Element { const { t } = useTranslation() const colors = useSporeColors() - const dispatch = useAppDispatch() - const { finishOnboarding } = useOnboardingContext() + const { setRecoveredImportedAccounts } = useOnboardingContext() + const recoveryLoadingTimeoutMs = useDynamicConfig(DynamicConfigs.OnDeviceRecovery).get( + 'recoveryLoadingTimeoutMs', + FALLBACK_RECOVERY_LOADING_TIMEOUT_MS, + isNumber, + ) const [selectedMnemonicId, setSelectedMnemonicId] = useState() - const [selectedRecoveryWalletInfos, setSelectedRecoveryWalletInfos] = useState< - RecoveryWalletInfo[] - >([]) + const [selectedRecoveryWalletInfos, setSelectedRecoveryWalletInfos] = useState([]) + + const [hasAnySignificantWallets, setHasAnySignificantWallets] = useState(false) const [loadedWallets, setLoadedWallets] = useState(0) + const [screenLoading, setScreenLoading] = useState(true) const [showConfirmationModal, setShowConfirmationModal] = useState(false) @@ -64,7 +71,7 @@ export function OnDeviceRecoveryScreen({ if (mnemonicId !== selectedMnemonicId) { return Keyring.removeMnemonic(mnemonicId) } - }) + }), ) } @@ -75,10 +82,25 @@ export function OnDeviceRecoveryScreen({ if (!selectedRecoveryWalletInfos.find((walletInfo) => walletInfo.address === address)) { return Keyring.removePrivateKey(address) } - }) + }), ) } + const onWalletLoad = useCallback( + (significantWalletCount: number) => { + setLoadedWallets((prev) => { + const loaded = prev + 1 + logger.debug('OnDeviceRecoveryScreen', 'onLoadComplete', `${loaded} of ${mnemonicIds.length} loaded`) + return loaded + }) + + if (significantWalletCount > 0) { + setHasAnySignificantWallets(true) + } + }, + [mnemonicIds.length], + ) + const onPressClose = (): void => { setSelectedMnemonicId(undefined) setSelectedRecoveryWalletInfos([]) @@ -91,9 +113,8 @@ export function OnDeviceRecoveryScreen({ setShowConfirmationModal(false) if (selectedMnemonicId && selectedRecoveryWalletInfos.length) { - await finishOnboarding( - ImportType.OnDeviceRecovery, - selectedRecoveryWalletInfos.map((walletInfo, index) => { + setRecoveredImportedAccounts( + selectedRecoveryWalletInfos.map((walletInfo, index): SignerMnemonicAccount => { return { type: AccountType.SignerMnemonic, mnemonicId: selectedMnemonicId, @@ -102,9 +123,12 @@ export function OnDeviceRecoveryScreen({ derivationIndex: walletInfo.derivationIndex, timeImportedMs: dayjs().valueOf(), } - }) + }), ) - dispatch(setFinishedOnboarding({ finishedOnboarding: true })) + navigation.navigate(OnboardingScreens.Notifications, { + importType: ImportType.OnDeviceRecovery, + entryPoint: OnboardingEntryPoint.FreshInstallOrReplace, + }) } else { navigation.navigate(OnboardingScreens.Landing, { importType: ImportType.NotYetSelected, @@ -116,14 +140,35 @@ export function OnDeviceRecoveryScreen({ //Hides lock screen on next js render cycle, ensuring this component is loaded when the screen is hidden useTimeout(hideSplashScreen, 1) - const screenLoading = loadedWallets < mnemonicIds.length + useEffect(() => { + if (loadedWallets >= mnemonicIds.length) { + setScreenLoading(false) + } + }, [loadedWallets, mnemonicIds.length]) + + useEffect(() => { + const timeout = setTimeout(() => { + if (screenLoading) { + setScreenLoading(false) + logger.warn( + 'OnDeviceRecoveryScreen', + 'useTimeout', + `Loading timeout triggered after ${recoveryLoadingTimeoutMs}ms`, + ) + } + }, recoveryLoadingTimeoutMs) + return () => clearTimeout(timeout) + }, [recoveryLoadingTimeoutMs, screenLoading]) + + // If all wallets are loaded and there are no wallets with a balance/usernames, show them all + const showAllWallets = !screenLoading && !hasAnySignificantWallets return ( - + - + {t('onboarding.import.onDeviceRecovery.title')} {t('onboarding.import.onDeviceRecovery.subtitle')} @@ -136,17 +181,8 @@ export function OnDeviceRecoveryScreen({ key={mnemonicId} mnemonicId={mnemonicId} screenLoading={screenLoading} - onLoadComplete={() => { - setLoadedWallets((prev) => { - const loaded = prev + 1 - logger.debug( - 'OnDeviceRecoveryScreen', - 'onLoadComplete', - `${loaded} of ${mnemonicIds.length} loaded` - ) - return loaded - }) - }} + showAllWallets={showAllWallets} + onLoadComplete={onWalletLoad} onPressCard={(recoveryAddressesInfos) => { setSelectedMnemonicId(mnemonicId) setSelectedRecoveryWalletInfos(recoveryAddressesInfos) @@ -166,10 +202,7 @@ export function OnDeviceRecoveryScreen({ .fill(0) .map((_, index) => ( - + )) : null} @@ -181,11 +214,7 @@ export function OnDeviceRecoveryScreen({ {t('onboarding.import.onDeviceRecovery.other_options.label')} - + {t('onboarding.import.onDeviceRecovery.other_options')} diff --git a/apps/mobile/src/screens/Import/OnDeviceRecoveryViewSeedPhraseScreen.tsx b/apps/mobile/src/screens/Import/OnDeviceRecoveryViewSeedPhraseScreen.tsx index 398cee2e1b5..68809cc125f 100644 --- a/apps/mobile/src/screens/Import/OnDeviceRecoveryViewSeedPhraseScreen.tsx +++ b/apps/mobile/src/screens/Import/OnDeviceRecoveryViewSeedPhraseScreen.tsx @@ -5,14 +5,12 @@ import { OnboardingStackParamList } from 'src/app/navigation/types' import { BackHeader } from 'src/components/layout/BackHeader' import { Screen } from 'src/components/layout/Screen' import { SeedPhraseDisplay } from 'src/components/mnemonic/SeedPhraseDisplay' +import { useLockScreenOnBlur } from 'src/features/authentication/lockScreenContext' import { Text } from 'ui/src' import Trace from 'uniswap/src/features/telemetry/Trace' import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' -type Props = NativeStackScreenProps< - OnboardingStackParamList, - OnboardingScreens.OnDeviceRecoveryViewSeedPhrase -> +type Props = NativeStackScreenProps export function OnDeviceRecoveryViewSeedPhraseScreen({ navigation, @@ -26,6 +24,8 @@ export function OnDeviceRecoveryViewSeedPhraseScreen({ navigation.goBack() } + useLockScreenOnBlur() + return ( diff --git a/apps/mobile/src/screens/Import/OnDeviceRecoveryWalletCard.tsx b/apps/mobile/src/screens/Import/OnDeviceRecoveryWalletCard.tsx index 095719fc702..53194659c57 100644 --- a/apps/mobile/src/screens/Import/OnDeviceRecoveryWalletCard.tsx +++ b/apps/mobile/src/screens/Import/OnDeviceRecoveryWalletCard.tsx @@ -1,10 +1,7 @@ import React, { useEffect } from 'react' import { useTranslation } from 'react-i18next' import { ViewProps } from 'react-native' -import { - RecoveryWalletInfo, - useOnDeviceRecoveryData, -} from 'src/screens/Import/useOnDeviceRecoveryData' +import { RecoveryWalletInfo, useOnDeviceRecoveryData } from 'src/screens/Import/useOnDeviceRecoveryData' import { Button, Flex, FlexProps, Loader, Text, TouchableArea } from 'ui/src' import { fonts, iconSizes } from 'ui/src/theme' import { NumberType } from 'utilities/src/format/types' @@ -22,41 +19,41 @@ const cardProps: FlexProps & ViewProps = { export function OnDeviceRecoveryWalletCard({ mnemonicId, screenLoading, + showAllWallets, onLoadComplete, onPressCard, onPressViewRecoveryPhrase, }: { mnemonicId: string screenLoading: boolean - onLoadComplete: () => void + showAllWallets: boolean + onLoadComplete: (significantWalletCount: number) => void onPressCard: (walletInfos: RecoveryWalletInfo[]) => void onPressViewRecoveryPhrase: () => void }): JSX.Element | null { const { t } = useTranslation() const { convertFiatAmountFormatted } = useLocalizationContext() - const { - significantRecoveryWalletInfos: significantRecoveryWalletInfos, - totalBalance, - loading, - } = useOnDeviceRecoveryData(mnemonicId) + const { recoveryWalletInfos, significantRecoveryWalletInfos, totalBalance, loading } = + useOnDeviceRecoveryData(mnemonicId) + + const targetWalletInfos = showAllWallets ? recoveryWalletInfos.slice(0, 1) : significantRecoveryWalletInfos + const firstWalletInfo = targetWalletInfos[0] + const remainingWalletCount = targetWalletInfos.length - 1 useEffect(() => { if (!loading && screenLoading) { - onLoadComplete() + onLoadComplete(significantRecoveryWalletInfos.length) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [loading, screenLoading]) - const firstWalletInfo = significantRecoveryWalletInfos[0] - const remainingWalletCount = significantRecoveryWalletInfos.length - 1 - if (screenLoading || !firstWalletInfo) { return null } return ( - onPressCard(significantRecoveryWalletInfos)}> + onPressCard(targetWalletInfos)}> + p="$spacing12" + > @@ -94,7 +92,8 @@ export function OnDeviceRecoveryWalletCard({ py="$spacing8" theme="secondary" width="100%" - onPress={() => onPressViewRecoveryPhrase()}> + onPress={() => onPressViewRecoveryPhrase()} + > {t('onboarding.import.onDeviceRecovery.wallet.button')} @@ -114,10 +113,6 @@ export function OnDeviceRecoveryWalletCardLoader({ totalCount: number }): JSX.Element { return ( - + ) } diff --git a/apps/mobile/src/screens/Import/RestoreCloudBackupLoadingScreen.tsx b/apps/mobile/src/screens/Import/RestoreCloudBackupLoadingScreen.tsx index c6ea778bc85..5803b6854f7 100644 --- a/apps/mobile/src/screens/Import/RestoreCloudBackupLoadingScreen.tsx +++ b/apps/mobile/src/screens/Import/RestoreCloudBackupLoadingScreen.tsx @@ -23,19 +23,13 @@ import { logger } from 'utilities/src/logger/logger' import { ONE_SECOND_MS } from 'utilities/src/time/time' import { useSignerAccounts } from 'wallet/src/features/wallet/hooks' -type Props = NativeStackScreenProps< - OnboardingStackParamList, - OnboardingScreens.RestoreCloudBackupLoading -> +type Props = NativeStackScreenProps const MIN_LOADING_UI_MS = ONE_SECOND_MS // 10s timeout time for query for backups, since we don't know when the query completes const MAX_LOADING_TIMEOUT_MS = ONE_SECOND_MS * 10 -export function RestoreCloudBackupLoadingScreen({ - navigation, - route: { params }, -}: Props): JSX.Element { +export function RestoreCloudBackupLoadingScreen({ navigation, route: { params } }: Props): JSX.Element { const { t } = useTranslation() const dispatch = useAppDispatch() const entryPoint = params.entryPoint @@ -64,8 +58,6 @@ export function RestoreCloudBackupLoadingScreen({ await startFetchingCloudStorageBackups() } catch (e) { setIsError(true) - } finally { - setIsLoading(false) } }, 0) }, []) @@ -86,14 +78,14 @@ export function RestoreCloudBackupLoadingScreen({ logger.debug( 'RestoreCloudBackupLoadingScreen', 'fetchCloudStorageBackups', - `Timed out fetching cloud backups after ${MAX_LOADING_TIMEOUT_MS}ms` + `Timed out fetching cloud backups after ${MAX_LOADING_TIMEOUT_MS}ms`, ) } // eslint-disable-next-line @typescript-eslint/no-floating-promises stopFetchingCloudStorageBackups() setIsLoading(false) }, - backups.length === 0 ? MAX_LOADING_TIMEOUT_MS : MIN_LOADING_UI_MS + backups.length === 0 ? MAX_LOADING_TIMEOUT_MS : MIN_LOADING_UI_MS, ) return () => { @@ -113,7 +105,7 @@ export function RestoreCloudBackupLoadingScreen({ dispatch(clearCloudBackups()) fetchCloudStorageBackups() }) - }, [dispatch, fetchCloudStorageBackups, navigation]) + }, [dispatch, fetchCloudStorageBackups, navigation]), ) /** diff --git a/apps/mobile/src/screens/Import/RestoreCloudBackupPasswordScreen.test.tsx b/apps/mobile/src/screens/Import/RestoreCloudBackupPasswordScreen.test.tsx index 62add03f64f..ef4da090e33 100644 --- a/apps/mobile/src/screens/Import/RestoreCloudBackupPasswordScreen.test.tsx +++ b/apps/mobile/src/screens/Import/RestoreCloudBackupPasswordScreen.test.tsx @@ -8,10 +8,7 @@ import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' import { TamaguiProvider } from 'wallet/src/provider/tamagui-provider' const setOptionsSpy = jest.fn() -const routeProp = { params: {} } as RouteProp< - OnboardingStackParamList, - OnboardingScreens.RestoreCloudBackupPassword -> +const routeProp = { params: {} } as RouteProp describe(RestoreCloudBackupPasswordScreen, () => { it('renders correctly', () => { @@ -32,7 +29,7 @@ describe(RestoreCloudBackupPasswordScreen, () => { } route={routeProp} /> - + , ).toJSON() expect(tree).toMatchSnapshot() diff --git a/apps/mobile/src/screens/Import/RestoreCloudBackupPasswordScreen.tsx b/apps/mobile/src/screens/Import/RestoreCloudBackupPasswordScreen.tsx index e06b9457a40..2dfc0fc13db 100644 --- a/apps/mobile/src/screens/Import/RestoreCloudBackupPasswordScreen.tsx +++ b/apps/mobile/src/screens/Import/RestoreCloudBackupPasswordScreen.tsx @@ -26,10 +26,7 @@ import { MINUTES_IN_HOUR, ONE_HOUR_MS, ONE_MINUTE_MS } from 'utilities/src/time/ import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext' import { BackupType } from 'wallet/src/features/wallet/accounts/types' -type Props = NativeStackScreenProps< - OnboardingStackParamList, - OnboardingScreens.RestoreCloudBackupPassword -> +type Props = NativeStackScreenProps /** * If the attempt count does not correspond to a lockout then returns undefined. Otherwise returns the lockout time based on attempts. The lockout time logic is as follows: @@ -69,10 +66,7 @@ function useLockoutTimeMessage(remainingLockoutTime: number): string { return t('account.cloud.lockout.time.minutes', { count: Math.floor(minutes) }) } -export function RestoreCloudBackupPasswordScreen({ - navigation, - route: { params }, -}: Props): JSX.Element { +export function RestoreCloudBackupPasswordScreen({ navigation, route: { params } }: Props): JSX.Element { const { t } = useTranslation() const inputRef = useRef(null) const dispatch = useAppDispatch() @@ -103,7 +97,7 @@ export function RestoreCloudBackupPasswordScreen({ return () => clearTimeout(timer) } - }, [isLockedOut, lockoutMessage, remainingLockoutTime, dispatch]) + }, [isLockedOut, lockoutMessage, remainingLockoutTime, dispatch]), ) useAddBackButton(navigation) @@ -148,7 +142,8 @@ export function RestoreCloudBackupPasswordScreen({ return ( + title={t('account.cloud.password.title')} + > )} - diff --git a/apps/mobile/src/screens/Import/RestoreCloudBackupScreen.test.tsx b/apps/mobile/src/screens/Import/RestoreCloudBackupScreen.test.tsx index 78969be3e11..df4780fd161 100644 --- a/apps/mobile/src/screens/Import/RestoreCloudBackupScreen.test.tsx +++ b/apps/mobile/src/screens/Import/RestoreCloudBackupScreen.test.tsx @@ -7,10 +7,7 @@ import { render } from 'src/test/test-utils' import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' const setOptionsSpy = jest.fn() -const routeProp = { params: {} } as RouteProp< - OnboardingStackParamList, - OnboardingScreens.RestoreCloudBackup -> +const routeProp = { params: {} } as RouteProp describe(RestoreCloudBackupScreen, () => { it('renders correctly', () => { @@ -29,7 +26,7 @@ describe(RestoreCloudBackupScreen, () => { > } route={routeProp} - /> + />, ).toJSON() expect(tree).toMatchSnapshot() diff --git a/apps/mobile/src/screens/Import/RestoreCloudBackupScreen.tsx b/apps/mobile/src/screens/Import/RestoreCloudBackupScreen.tsx index 51a88249314..82bcc0b73d8 100644 --- a/apps/mobile/src/screens/Import/RestoreCloudBackupScreen.tsx +++ b/apps/mobile/src/screens/Import/RestoreCloudBackupScreen.tsx @@ -11,12 +11,9 @@ import { Flex, Text, TouchableArea, Unicon, useIsDarkMode } from 'ui/src' import { RotatableChevron } from 'ui/src/components/icons' import { iconSizes } from 'ui/src/theme' import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName' -import { - FORMAT_DATE_TIME_SHORT, - useLocalizedDayjs, -} from 'wallet/src/features/language/localizedDayjs' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' +import { FORMAT_DATE_TIME_SHORT, useLocalizedDayjs } from 'wallet/src/features/language/localizedDayjs' type Props = NativeStackScreenProps @@ -42,7 +39,8 @@ export function RestoreCloudBackupScreen({ navigation, route: { params } }: Prop return ( + title={t('account.cloud.backup.title')} + > {sortedBackups.map((backup) => { @@ -57,7 +55,8 @@ export function RestoreCloudBackupScreen({ navigation, route: { params } }: Prop p="$spacing16" shadowColor="$surface3" shadowRadius={!isDarkMode ? '$spacing4' : undefined} - onPress={(): Promise => onPressRestoreBackup(backup)}> + onPress={(): Promise => onPressRestoreBackup(backup)} + > diff --git a/apps/mobile/src/screens/Import/SeedPhraseInput.tsx b/apps/mobile/src/screens/Import/SeedPhraseInput.tsx index bd97ff7babb..7af3a0bbddb 100644 --- a/apps/mobile/src/screens/Import/SeedPhraseInput.tsx +++ b/apps/mobile/src/screens/Import/SeedPhraseInput.tsx @@ -1,12 +1,6 @@ import { NativeStackNavigationProp } from '@react-navigation/native-stack' import { forwardRef, RefObject, useEffect, useState } from 'react' -import { - findNodeHandle, - NativeSyntheticEvent, - requireNativeComponent, - StyleSheet, - UIManager, -} from 'react-native' +import { findNodeHandle, NativeSyntheticEvent, requireNativeComponent, StyleSheet, UIManager } from 'react-native' import { useNativeComponentKey } from 'src/app/hooks' import { OnboardingStackParamList } from 'src/app/navigation/types' import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' @@ -89,6 +83,6 @@ export const SeedPhraseInput = forwardRef ) - } + }, ) SeedPhraseInput.displayName = 'NativeSeedPhraseInput' diff --git a/apps/mobile/src/screens/Import/SeedPhraseInputScreen.tsx b/apps/mobile/src/screens/Import/SeedPhraseInputScreen.tsx index b1368140b5a..40971533d4b 100644 --- a/apps/mobile/src/screens/Import/SeedPhraseInputScreen.tsx +++ b/apps/mobile/src/screens/Import/SeedPhraseInputScreen.tsx @@ -80,15 +80,7 @@ export function SeedPhraseInputScreen({ navigation, route: { params } }: Props): if (!isRestoringMnemonic) { navigation.navigate({ name: OnboardingScreens.SelectWallet, params, merge: true }) } - }, [ - value, - mnemonicId, - generateImportedAccountsByMnemonic, - isRestoringMnemonic, - t, - navigation, - params, - ]) + }, [value, mnemonicId, generateImportedAccountsByMnemonic, isRestoringMnemonic, t, navigation, params]) const onBlur = useCallback(() => { const { error, invalidWord } = validateMnemonic(value) @@ -114,8 +106,7 @@ export function SeedPhraseInputScreen({ navigation, route: { params } }: Props): setValue(text) } - const onPressRecoveryHelpButton = (): Promise => - openUri(uniswapUrls.helpArticleUrls.recoveryPhraseHowToImport) + const onPressRecoveryHelpButton = (): Promise => openUri(uniswapUrls.helpArticleUrls.recoveryPhraseHowToImport) const onPressTryAgainButton = (): void => { navigation.replace(OnboardingScreens.RestoreCloudBackupLoading, params) @@ -129,10 +120,9 @@ export function SeedPhraseInputScreen({ navigation, route: { params } }: Props): : t('account.recoveryPhrase.subtitle.import') } title={ - isRestoringMnemonic - ? t('account.recoveryPhrase.title.restoring') - : t('account.recoveryPhrase.title.import') - }> + isRestoringMnemonic ? t('account.recoveryPhrase.title.restoring') : t('account.recoveryPhrase.title.import') + } + > + onPress={isRestoringMnemonic ? onPressTryAgainButton : onPressRecoveryHelpButton} + > {isRestoringMnemonic @@ -166,10 +157,7 @@ export function SeedPhraseInputScreen({ navigation, route: { params } }: Props): - diff --git a/apps/mobile/src/screens/Import/SeedPhraseInputScreenV2.tsx b/apps/mobile/src/screens/Import/SeedPhraseInputScreenV2.tsx index 900aaf6c4da..c06c8eeca58 100644 --- a/apps/mobile/src/screens/Import/SeedPhraseInputScreenV2.tsx +++ b/apps/mobile/src/screens/Import/SeedPhraseInputScreenV2.tsx @@ -60,11 +60,10 @@ export function SeedPhraseInputScreenV2({ navigation, route: { params } }: Props navigation.navigate({ name: OnboardingScreens.SelectWallet, params, merge: true }) } }, - [generateImportedAccounts, isRestoringMnemonic, navigation, params] + [generateImportedAccounts, isRestoringMnemonic, navigation, params], ) - const onPressRecoveryHelpButton = (): Promise => - openUri(uniswapUrls.helpArticleUrls.recoveryPhraseHowToImport) + const onPressRecoveryHelpButton = (): Promise => openUri(uniswapUrls.helpArticleUrls.recoveryPhraseHowToImport) const onPressTryAgainButton = (): void => { navigation.replace(OnboardingScreens.RestoreCloudBackupLoading, params) @@ -82,7 +81,8 @@ export function SeedPhraseInputScreenV2({ navigation, route: { params } }: Props testID="seed-input-submit" onPress={(): void => { handleSubmit(seedPhraseInputRef) - }}> + }} + > {t('common.button.continue')} @@ -93,10 +93,9 @@ export function SeedPhraseInputScreenV2({ navigation, route: { params } }: Props : t('account.recoveryPhrase.subtitle.import') } title={ - isRestoringMnemonic - ? t('account.recoveryPhrase.title.restoring') - : t('account.recoveryPhrase.title.import') - }> + isRestoringMnemonic ? t('account.recoveryPhrase.title.restoring') : t('account.recoveryPhrase.title.import') + } + > - + diff --git a/apps/mobile/src/screens/Import/SelectWalletScreen.tsx b/apps/mobile/src/screens/Import/SelectWalletScreen.tsx index 8be6ece3fe7..6ee01537266 100644 --- a/apps/mobile/src/screens/Import/SelectWalletScreen.tsx +++ b/apps/mobile/src/screens/Import/SelectWalletScreen.tsx @@ -64,9 +64,7 @@ export function SelectWalletScreen({ navigation, route: { params } }: Props): JS })) .filter(isImportableAccount) - const accountsWithBalance = filteredAccounts?.filter( - (address) => address.balance && address.balance > 0 - ) + const accountsWithBalance = filteredAccounts?.filter((address) => address.balance && address.balance > 0) if (accountsWithBalance?.length) { return accountsWithBalance @@ -95,10 +93,7 @@ export function SelectWalletScreen({ navigation, route: { params } }: Props): JS await selectImportedAccounts(selectedAddresses) navigation.navigate({ - name: - params?.importType === ImportType.Restore - ? OnboardingScreens.Notifications - : OnboardingScreens.Backup, + name: params?.importType === ImportType.Restore ? OnboardingScreens.Notifications : OnboardingScreens.Backup, params, merge: true, }) @@ -118,9 +113,7 @@ export function SelectWalletScreen({ navigation, route: { params } }: Props): JS return ( <> - + {showError ? ( diff --git a/apps/mobile/src/screens/Import/WatchWalletScreen.tsx b/apps/mobile/src/screens/Import/WatchWalletScreen.tsx index 55c17460b46..d91c288625b 100644 --- a/apps/mobile/src/screens/Import/WatchWalletScreen.tsx +++ b/apps/mobile/src/screens/Import/WatchWalletScreen.tsx @@ -16,6 +16,7 @@ import { ElementName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { UniverseChainId } from 'uniswap/src/types/chains' import { OnboardingScreens } from 'uniswap/src/types/screens/mobile' +import { areAddressesEqual, getValidAddress } from 'uniswap/src/utils/addresses' import { normalizeTextInput } from 'utilities/src/primitives/string' import { usePortfolioBalances } from 'wallet/src/features/dataApi/balances' import { useENS } from 'wallet/src/features/ens/useENS' @@ -23,7 +24,6 @@ import { createViewOnlyAccount } from 'wallet/src/features/onboarding/createView import { useIsSmartContractAddress } from 'wallet/src/features/transactions/transfer/hooks/useIsSmartContractAddress' import { createAccountsActions } from 'wallet/src/features/wallet/create/createAccountsSaga' import { useAccounts } from 'wallet/src/features/wallet/hooks' -import { areAddressesEqual, getValidAddress } from 'wallet/src/utils/addresses' type Props = NativeStackScreenProps @@ -44,12 +44,7 @@ const validateForm = ({ isSmartContractAddress: boolean isValidSmartContract: boolean }): boolean => { - return ( - (!!isAddress || !!name) && - !walletExists && - !loading && - (!isSmartContractAddress || isValidSmartContract) - ) + return (!!isAddress || !!name) && !walletExists && !loading && (!isSmartContractAddress || isValidSmartContract) } const getErrorText = ({ @@ -88,15 +83,11 @@ export function WatchWalletScreen({ navigation, route: { params } }: Props): JSX // ENS and address parsing. const normalizedValue = normalizeTextInput(value ?? '') const hasSuffixIncluded = normalizedValue.includes('.') - const { address: resolvedAddress, name } = useENS( - UniverseChainId.Mainnet, - normalizedValue, - !hasSuffixIncluded - ) + const { address: resolvedAddress, name } = useENS(UniverseChainId.Mainnet, normalizedValue, !hasSuffixIncluded) const isAddress = getValidAddress(normalizedValue, true, false) const { isSmartContractAddress, loading } = useIsSmartContractAddress( (isAddress || resolvedAddress) ?? undefined, - UniverseChainId.Mainnet + UniverseChainId.Mainnet, ) // Allow smart contracts with non-null balances const { data: balancesById } = usePortfolioBalances({ @@ -109,8 +100,7 @@ export function WatchWalletScreen({ navigation, route: { params } }: Props): JSX const walletExists = Object.keys(initialAccounts).some( (accountAddress) => - areAddressesEqual(accountAddress, resolvedAddress) || - areAddressesEqual(accountAddress, normalizedValue) + areAddressesEqual(accountAddress, resolvedAddress) || areAddressesEqual(accountAddress, normalizedValue), ) // Form validation. @@ -123,9 +113,7 @@ export function WatchWalletScreen({ navigation, route: { params } }: Props): JSX isValidSmartContract, }) - const errorText = !isValid - ? getErrorText({ walletExists, isSmartContractAddress, loading, t }) - : undefined + const errorText = !isValid ? getErrorText({ walletExists, isSmartContractAddress, loading, t }) : undefined const onSubmit = useCallback(async () => { if (isValid && value) { @@ -135,7 +123,7 @@ export function WatchWalletScreen({ navigation, route: { params } }: Props): JSX dispatch( createAccountsActions.trigger({ accounts: [viewOnlyAccount], - }) + }), ) sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, { @@ -188,7 +176,8 @@ export function WatchWalletScreen({ navigation, route: { params } }: Props): JSX backgroundColor="$surface2" borderRadius="$rounded16" gap="$spacing16" - p="$spacing16"> + p="$spacing16" + > {t('account.wallet.watch.message')} diff --git a/apps/mobile/src/screens/Import/useOnDeviceRecoveryData.ts b/apps/mobile/src/screens/Import/useOnDeviceRecoveryData.ts index 8ae444e49e0..d06e24e243c 100644 --- a/apps/mobile/src/screens/Import/useOnDeviceRecoveryData.ts +++ b/apps/mobile/src/screens/Import/useOnDeviceRecoveryData.ts @@ -1,12 +1,12 @@ import { useEffect, useMemo, useState } from 'react' import { useMultiplePortfolioBalancesQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' // eslint-disable-next-line no-restricted-imports import { usePortfolioValueModifiers } from 'wallet/src/features/dataApi/balances' import { useENSName } from 'wallet/src/features/ens/api' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { areAddressesEqual } from 'wallet/src/utils/addresses' export type RecoveryWalletInfo = AddressWithIndex & { balance?: number @@ -45,15 +45,14 @@ function useStoredAddressesForMnemonic(mnemonicId: string | undefined): { }) return undefined } - }) + }), ) const filteredAddresses = possibleAddresses .map((address, index): AddressWithIndex | undefined => - address && - storedAddresses.find((storedAddress) => areAddressesEqual(storedAddress, address)) + address && storedAddresses.find((storedAddress) => areAddressesEqual(storedAddress, address)) ? { address, derivationIndex: index } - : undefined + : undefined, ) .filter((address): address is AddressWithIndex => !!address) @@ -79,8 +78,7 @@ export function useOnDeviceRecoveryData(mnemonicId: string | undefined): { totalBalance: number | undefined loading: boolean } { - const { addressesWithIndex, loading: addressesLoading } = - useStoredAddressesForMnemonic(mnemonicId) + const { addressesWithIndex, loading: addressesLoading } = useStoredAddressesForMnemonic(mnemonicId) const addresses = addressesWithIndex.map((address) => address.address) const valueModifiers = usePortfolioValueModifiers(addresses) @@ -91,9 +89,7 @@ export function useOnDeviceRecoveryData(mnemonicId: string | undefined): { }, skip: !addresses.length, }) - const balances = balancesData?.portfolios?.map( - (portfolio) => portfolio?.tokensTotalDenominatedValue?.value ?? 0 - ) + const balances = balancesData?.portfolios?.map((portfolio) => portfolio?.tokensTotalDenominatedValue?.value ?? 0) const totalBalance = balances?.reduce((acc, balance) => acc + balance, 0) // Need to fetch ENS names and unitags for each deriviation index @@ -146,9 +142,9 @@ export function useOnDeviceRecoveryData(mnemonicId: string | undefined): { () => recoveryWalletInfos?.filter( (recoveryAddressInfo) => - recoveryAddressInfo.balance || recoveryAddressInfo.ensName || recoveryAddressInfo.unitag + recoveryAddressInfo.balance || recoveryAddressInfo.ensName || recoveryAddressInfo.unitag, ) ?? [], - [recoveryWalletInfos] + [recoveryWalletInfos], ) const loading = addressesLoading || ensLoading || unitagLoading || balancesLoading diff --git a/apps/mobile/src/screens/NFTCollectionScreen.tsx b/apps/mobile/src/screens/NFTCollectionScreen.tsx index 76eaa647120..c653314b8b2 100644 --- a/apps/mobile/src/screens/NFTCollectionScreen.tsx +++ b/apps/mobile/src/screens/NFTCollectionScreen.tsx @@ -10,16 +10,10 @@ import { ScrollHeader } from 'src/components/layout/screens/ScrollHeader' import { Loader } from 'src/components/loading' import { ListPriceBadge } from 'src/features/nfts/collection/ListPriceCard' import { NFTCollectionContextMenu } from 'src/features/nfts/collection/NFTCollectionContextMenu' -import { - NFTCollectionHeader, - NFT_BANNER_HEIGHT, -} from 'src/features/nfts/collection/NFTCollectionHeader' +import { NFTCollectionHeader, NFT_BANNER_HEIGHT } from 'src/features/nfts/collection/NFTCollectionHeader' import { ExploreModalAwareView } from 'src/screens/ModalAwareView' import { Flex, ImpactFeedbackStyle, Text, TouchableArea, useDeviceInsets } from 'ui/src' -import { - AnimatedBottomSheetFlashList, - AnimatedFlashList, -} from 'ui/src/components/AnimatedFlashList/AnimatedFlashList' +import { AnimatedBottomSheetFlashList, AnimatedFlashList } from 'ui/src/components/AnimatedFlashList/AnimatedFlashList' import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' import { iconSizes, spacing } from 'ui/src/theme' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' @@ -44,9 +38,7 @@ const LOADING_BUFFER_AMOUNT = 9 const LOADING_ITEMS_ARRAY: NFTItem[] = Array(LOADING_BUFFER_AMOUNT).fill(LOADING_ITEM) const keyExtractor = (item: NFTItem | string, index: number): string => - typeof item === 'string' - ? `${LOADING_ITEM}-${index}` - : getNFTAssetKey(item.contractAddress ?? '', item.tokenId ?? '') + typeof item === 'string' ? `${LOADING_ITEM}-${index}` : getNFTAssetKey(item.contractAddress ?? '', item.tokenId ?? '') function gqlNFTAssetToNFTItem(data: NftCollectionScreenQuery | undefined): NFTItem[] | undefined { const items = data?.nftAssets?.edges?.flatMap((item) => item.node) @@ -161,7 +153,8 @@ export function NFTCollectionScreen({ backgroundColor="$surface3" borderRadius="$rounded16" overflow="hidden" - style={containerStyle}> + style={containerStyle} + > {typeof item === 'string' ? ( ) : ( @@ -171,7 +164,8 @@ export function NFTCollectionScreen({ alignItems="center" flex={1} hapticStyle={ImpactFeedbackStyle.Light} - onPress={(): void => onPressItem(item)}> + onPress={(): void => onPressItem(item)} + > - collectionData?.name - ? { collectionAddress, collectionName: collectionData?.name } - : undefined, - [collectionAddress, collectionData?.name] + () => (collectionData?.name ? { collectionAddress, collectionName: collectionData?.name } : undefined), + [collectionAddress, collectionData?.name], ) if (isError(networkStatus, !!data)) { @@ -248,29 +237,21 @@ export function NFTCollectionScreen({ directFromPage logImpression={!!traceProperties} properties={traceProperties} - screen={MobileScreens.NFTCollection}> + screen={MobileScreens.NFTCollection} + > {collectionData.name} : undefined - } + centerElement={collectionData?.name ? {collectionData.name} : undefined} listRef={listRef} - rightElement={ - - } + rightElement={} scrollY={scrollY} showHeaderScrollYDistance={NFT_BANNER_HEIGHT} /> - ) + gridDataLoading ? null : } ListHeaderComponent={ asset?.name ?? fallbackData?.name, [asset?.name, fallbackData?.name]) const description = useMemo( () => asset?.description ?? fallbackData?.description, - [asset?.description, fallbackData?.description] + [asset?.description, fallbackData?.description], ) const imageUrl = useMemo( () => asset?.image?.url ?? fallbackData?.imageUrl, - [asset?.image?.url, fallbackData?.imageUrl] + [asset?.image?.url, fallbackData?.imageUrl], ) const imageHeight = asset?.image?.dimensions?.height const imageWidth = asset?.image?.dimensions?.width const imageDimensionsExist = imageHeight && imageWidth - const imageDimensions = imageDimensionsExist - ? { height: imageHeight, width: imageWidth } - : undefined + const imageDimensions = imageDimensionsExist ? { height: imageHeight, width: imageWidth } : undefined const imageAspectRatio = imageDimensions ? imageDimensions.width / imageDimensions.height : 1 const onPressCollection = (): void => { const collectionAddress = asset?.nftContract?.address ?? fallbackData?.contractAddress @@ -135,7 +121,7 @@ function NFTItemScreenContents({ // Disable navigation to profile if user owns NFT or invalid owner const disableProfileNavigation = Boolean( - owner && (areAddressesEqual(owner, activeAccountAddress) || !isAddress(owner)) + owner && (areAddressesEqual(owner, activeAccountAddress) || !isAddress(owner)), ) const onPressOwner = (): void => { @@ -177,10 +163,7 @@ function NFTItemScreenContents({ const { colorLight, colorDark } = useNearestThemeColorFromImageUri(imageUrl) // check if colorLight passes contrast against card bg color, if not use fallback const accentTextColor = useMemo(() => { - if ( - colorLight && - passesContrast(colorLight, colors.surface1.val, MIN_COLOR_CONTRAST_THRESHOLD) - ) { + if (colorLight && passesContrast(colorLight, colors.surface1.val, MIN_COLOR_CONTRAST_THRESHOLD)) { return colorLight } return colors.neutral2.val @@ -193,32 +176,28 @@ function NFTItemScreenContents({ pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.Image, - }) + }), ) } const rightElement = useMemo( () => , - [asset, isSpam, owner] + [asset, isSpam, owner], ) return ( <> - {isIOS ? ( - - ) : null} + {isIOS ? : null} + screen={MobileScreens.NFTItem} + > <> {isIOS ? ( - + ) : ( )} @@ -232,7 +211,8 @@ function NFTItemScreenContents({ maxHeight={getTokenValue('$icon.40')} maxWidth={getTokenValue('$icon.40')} ml="$spacing16" - overflow="hidden"> + overflow="hidden" + > ) : ( @@ -242,21 +222,18 @@ function NFTItemScreenContents({ ) } renderedInModal={inModal} - rightElement={rightElement}> + rightElement={rightElement} + > {/* Content wrapper */} - + + shadowRadius={16} + > {nftLoading ? ( @@ -272,16 +249,11 @@ function NFTItemScreenContents({ /> ) : ( - + > => - refetch?.() - } + onRetry={(): Promise> => refetch?.()} /> )} @@ -315,11 +287,7 @@ function NFTItemScreenContents({ ) : description ? ( - + ) : null} @@ -373,10 +341,7 @@ function NFTItemScreenContents({ color={accentTextColor} title={t('tokens.nfts.details.owner')} valueComponent={ - + + {menuActions.length > 0 ? ( onlyShare ? ( - + ) : ( - + ) diff --git a/apps/mobile/src/screens/Onboarding/BackupScreen.test.tsx b/apps/mobile/src/screens/Onboarding/BackupScreen.test.tsx index d29ddd3ec31..df76bd60137 100644 --- a/apps/mobile/src/screens/Onboarding/BackupScreen.test.tsx +++ b/apps/mobile/src/screens/Onboarding/BackupScreen.test.tsx @@ -12,6 +12,14 @@ import { MobileScreens, OnboardingScreens } from 'uniswap/src/types/screens/mobi import { TamaguiProvider } from 'wallet/src/provider/tamagui-provider' import { ACCOUNT, preloadedSharedState } from 'wallet/src/test/fixtures' +jest.mock('wallet/src/features/onboarding/OnboardingContext', () => ({ + useOnboardingContext: jest.fn().mockReturnValue({ + getOnboardingAccountAddress: jest.fn().mockReturnValue('mockedAccountAddress'), + getImportedAccountsAddresses: jest.fn(), + hasBackup: jest.fn(), + }), +})) + const navigationProp = {} as CompositeNavigationProp< StackNavigationProp, NativeStackNavigationProp @@ -39,7 +47,7 @@ describe(BackupScreen, () => { , - { preloadedState: preloadedSharedState({ account: ACCOUNT }) } + { preloadedState: preloadedSharedState({ account: ACCOUNT }) }, ) await act(async () => { diff --git a/apps/mobile/src/screens/Onboarding/BackupScreen.tsx b/apps/mobile/src/screens/Onboarding/BackupScreen.tsx index 4b3a8e6da1b..9a197ccac78 100644 --- a/apps/mobile/src/screens/Onboarding/BackupScreen.tsx +++ b/apps/mobile/src/screens/Onboarding/BackupScreen.tsx @@ -4,11 +4,7 @@ import { StackScreenProps } from '@react-navigation/stack' import React, { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { Alert } from 'react-native' -import { - AppStackParamList, - OnboardingStackParamList, - useOnboardingStackNavigation, -} from 'src/app/navigation/types' +import { AppStackParamList, OnboardingStackParamList, useOnboardingStackNavigation } from 'src/app/navigation/types' import { BackButton } from 'src/components/buttons/BackButton' import { EducationContentType } from 'src/components/education' import { isCloudStorageAvailable } from 'src/features/CloudBackup/RNCloudStorageBackupsManager' @@ -27,7 +23,6 @@ import { isAndroid } from 'utilities/src/platform' import { useAsyncData } from 'utilities/src/react/hooks' import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext' import { BackupType } from 'wallet/src/features/wallet/accounts/types' -import { useActiveSignerAccount } from 'wallet/src/features/wallet/hooks' import { openSettings } from 'wallet/src/utils/linking' type Props = CompositeScreenProps< @@ -43,11 +38,15 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem const { data: cloudStorageAvailable } = useAsyncData(isCloudStorageAvailable) - const activeAccount = useActiveSignerAccount() - const { getImportedAccountsAddresses, getOnboardingAccountAddress } = useOnboardingContext() + const { getImportedAccountsAddresses, getOnboardingAccountAddress, hasBackup } = useOnboardingContext() const onboardingAccountAddress = getOnboardingAccountAddress() const importedAccountsAddresses = getImportedAccountsAddresses() - const activeAccountBackups = activeAccount?.backups + + const address = onboardingAccountAddress || importedAccountsAddresses?.[0] + + if (!address) { + throw Error('No account available to backup') + } const renderHeaderLeft = useCallback( () => ( @@ -57,7 +56,7 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem }} /> ), - [navigation] + [navigation], ) useEffect(() => { @@ -89,9 +88,7 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem const onPressCloudBackup = (): void => { if (!cloudStorageAvailable) { Alert.alert( - isAndroid - ? t('account.cloud.error.unavailable.title.android') - : t('account.cloud.error.unavailable.title.ios'), + isAndroid ? t('account.cloud.error.unavailable.title.android') : t('account.cloud.error.unavailable.title.ios'), isAndroid ? t('account.cloud.error.unavailable.message.android') : t('account.cloud.error.unavailable.message.ios'), @@ -102,18 +99,11 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem style: 'default', }, { text: t('account.cloud.error.unavailable.button.cancel'), style: 'cancel' }, - ] + ], ) return } - const address = - activeAccount?.address || onboardingAccountAddress || importedAccountsAddresses?.[0] - - if (!address) { - throw Error('No account available to backup') - } - navigate({ name: OnboardingScreens.BackupCloudPasswordCreate, params: { ...params, address }, @@ -126,16 +116,13 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem } const showSkipOption = - !activeAccountBackups?.length && - (params?.importType === ImportType.SeedPhrase || params?.importType === ImportType.Restore) + !hasBackup(address) && (params?.importType === ImportType.SeedPhrase || params?.importType === ImportType.Restore) - const hasCloudBackup = activeAccountBackups?.some((backup) => backup === BackupType.Cloud) - const hasManualBackup = activeAccountBackups?.some((backup) => backup === BackupType.Manual) + const hasCloudBackup = hasBackup(address, BackupType.Cloud) + const hasManualBackup = hasBackup(address, BackupType.Manual) const isCreatingNew = params?.importType === ImportType.CreateNew - const screenTitle = isCreatingNew - ? t('onboarding.backup.title.new') - : t('onboarding.backup.title.existing') + const screenTitle = isCreatingNew ? t('onboarding.backup.title.new') : t('onboarding.backup.title.existing') const options = [] options.push( + />, ) if (isCreatingNew) { options.push( @@ -160,7 +147,7 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem icon={} title={t('onboarding.backup.option.manual.title')} onPress={onPressManualBackup} - /> + />, ) } @@ -168,21 +155,14 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem - + {options} - {!isCreatingNew && ( - - )} + {!isCreatingNew && } - {isCreatingNew && ( - - )} + {isCreatingNew && } {showSkipOption && ( - {!seedWarningAcknowledged && ( - setSeedWarningAcknowledged(true)} /> - )} + {!seedWarningAcknowledged && setSeedWarningAcknowledged(true)} />} ) case View.SeedPhraseConfirm: @@ -127,7 +122,8 @@ export function ManualBackupScreen({ navigation, route: { params } }: Props): JS ? t('onboarding.recoveryPhrase.confirm.subtitle.combined') : t('onboarding.recoveryPhrase.confirm.subtitle.default') } - title={media.short ? undefined : t('onboarding.recoveryPhrase.confirm.title')}> + title={media.short ? undefined : t('onboarding.recoveryPhrase.confirm.title')} + > - @@ -157,14 +150,11 @@ const SeedWarningModal = ({ onPress }: { onPress: () => void }): JSX.Element => backgroundColor={colors.surface1.get()} hideHandlebar={true} isDismissible={false} - name={ModalName.SeedPhraseWarningModal}> + name={ModalName.SeedPhraseWarningModal} + > - + {t('onboarding.recoveryPhrase.warning.final.title')} @@ -178,7 +168,8 @@ const SeedWarningModal = ({ onPress }: { onPress: () => void }): JSX.Element => testID={ElementName.Confirm} theme="primary" width="100%" - onPress={onPress}> + onPress={onPress} + > {t('onboarding.recoveryPhrase.warning.final.button')} diff --git a/apps/mobile/src/screens/Onboarding/NotificationsSetupScreen.tsx b/apps/mobile/src/screens/Onboarding/NotificationsSetupScreen.tsx index c3e05af8ac7..528ede385a7 100644 --- a/apps/mobile/src/screens/Onboarding/NotificationsSetupScreen.tsx +++ b/apps/mobile/src/screens/Onboarding/NotificationsSetupScreen.tsx @@ -32,7 +32,7 @@ export const showNotificationSettingsAlert = (): void => { { text: i18n.t('common.button.cancel'), }, - ] + ], ) } @@ -47,27 +47,21 @@ export function NotificationsSetupScreen({ navigation, route: { params } }: Prop const renderBackButton = useCallback( (nav: OnboardingScreens): JSX.Element => ( - navigation.navigate({ name: nav, params, merge: true })} - /> + navigation.navigate({ name: nav, params, merge: true })} /> ), - [navigation, params] + [navigation, params], ) /* For some screens, we want to override the back button to go to a different screen. * This helps avoid re-visiting loading states or confirmation views. */ useEffect(() => { - const shouldOverrideBackButton = [ - ImportType.SeedPhrase, - ImportType.Restore, - ImportType.CreateNew, - ].includes(params.importType) + const shouldOverrideBackButton = [ImportType.SeedPhrase, ImportType.Restore, ImportType.CreateNew].includes( + params.importType, + ) if (shouldOverrideBackButton) { const nextScreen = - params.importType === ImportType.Restore - ? OnboardingScreens.RestoreCloudBackup - : OnboardingScreens.Backup + params.importType === ImportType.Restore ? OnboardingScreens.RestoreCloudBackup : OnboardingScreens.Backup navigation.setOptions({ headerLeft: () => renderBackButton(nextScreen), }) @@ -85,14 +79,7 @@ export function NotificationsSetupScreen({ navigation, route: { params } }: Prop } else { navigation.navigate({ name: OnboardingScreens.Security, params, merge: true }) } - }, [ - deviceSupportsBiometrics, - hasSeedPhrase, - isBiometricAuthEnabled, - navigation, - onCompleteOnboarding, - params, - ]) + }, [deviceSupportsBiometrics, hasSeedPhrase, isBiometricAuthEnabled, navigation, onCompleteOnboarding, params]) const onPressEnableNotifications = useCallback(async () => { promptPushPermission(() => { @@ -103,9 +90,7 @@ export function NotificationsSetupScreen({ navigation, route: { params } }: Prop }, [enableNotifications, navigateToNextScreen]) return ( - + @@ -133,9 +118,7 @@ const NotificationsBackgroundImage = (): JSX.Element => { diff --git a/apps/mobile/src/screens/Onboarding/SecuritySetupScreen.tsx b/apps/mobile/src/screens/Onboarding/SecuritySetupScreen.tsx index a831faf1eda..2288f85836a 100644 --- a/apps/mobile/src/screens/Onboarding/SecuritySetupScreen.tsx +++ b/apps/mobile/src/screens/Onboarding/SecuritySetupScreen.tsx @@ -73,15 +73,12 @@ export function SecuritySetupScreen({ route: { params } }: Props): JSX.Element { [ { text: t('common.navigation.systemSettings'), onPress: openSettings }, { text: t('common.button.notNow') }, - ] + ], ) : Alert.alert( t('onboarding.security.alert.biometrics.title.android'), t('onboarding.security.alert.biometrics.message.android'), - [ - { text: t('onboarding.security.button.setup'), onPress: enroll }, - { text: t('common.button.notNow') }, - ] + [{ text: t('onboarding.security.button.setup'), onPress: enroll }, { text: t('common.button.notNow') }], ) return } @@ -99,20 +96,10 @@ export function SecuritySetupScreen({ route: { params } }: Props): JSX.Element { return ( <> {showWarningModal && ( - + )} {isLoadingAccount && ( - + )} @@ -125,7 +112,8 @@ export function SecuritySetupScreen({ route: { params } }: Props): JSX.Element { }) : t('onboarding.security.subtitle.android') } - title={t('onboarding.security.title')}> + title={t('onboarding.security.title')} + > @@ -140,24 +128,13 @@ export function SecuritySetupScreen({ route: { params } }: Props): JSX.Element { borderColor: opacify(15, colors.sporeWhite.val), backgroundColor: opacify(35, colors.surface1.val), }} - top={0}> - + top={0} + > + {isTouchIdDevice ? ( - + ) : ( - + )} diff --git a/apps/mobile/src/screens/Onboarding/WelcomeWalletScreen.tsx b/apps/mobile/src/screens/Onboarding/WelcomeWalletScreen.tsx index eeecf0314c9..0012fd21f59 100644 --- a/apps/mobile/src/screens/Onboarding/WelcomeWalletScreen.tsx +++ b/apps/mobile/src/screens/Onboarding/WelcomeWalletScreen.tsx @@ -57,9 +57,7 @@ export function WelcomeWalletScreen({ navigation, route: { params } }: Props): J const zeroBalance = convertFiatAmountFormatted(0, NumberType.PortfolioBalance) - const displayName = unitagClaim - ? { type: DisplayNameType.Unitag, name: unitagClaim.username } - : walletName + const displayName = unitagClaim ? { type: DisplayNameType.Unitag, name: unitagClaim.username } : walletName return ( @@ -81,11 +79,7 @@ export function WelcomeWalletScreen({ navigation, route: { params } }: Props): J size={iconSizes.icon64} /> )} - + + variant="heading3" + > {t('onboarding.wallet.title')} + variant="subheading2" + > {t('onboarding.wallet.description.full')} @@ -123,12 +119,9 @@ export function WelcomeWalletScreen({ navigation, route: { params } }: Props): J - + style={{ backgroundColor: opacify(10, colors.sporeWhite.val) }} + > + {t('onboarding.wallet.continue')} diff --git a/apps/mobile/src/screens/Onboarding/__snapshots__/BackupScreen.test.tsx.snap b/apps/mobile/src/screens/Onboarding/__snapshots__/BackupScreen.test.tsx.snap index 07c7474feae..5169139afae 100644 --- a/apps/mobile/src/screens/Onboarding/__snapshots__/BackupScreen.test.tsx.snap +++ b/apps/mobile/src/screens/Onboarding/__snapshots__/BackupScreen.test.tsx.snap @@ -749,15 +749,18 @@ exports[`BackupScreen renders backup options when some are completed 1`] = ` } > void }): JSX.Element { pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.Address, - }) + }), ) } const onPressShowWalletQr = (): void => { onClose() - dispatch( - openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }) - ) + dispatch(openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })) } return ( - + void }): JSX.Element { borderRadius="$rounded20" borderWidth="$spacing1" gap="$spacing12" - p="$spacing12"> + p="$spacing12" + > void }): JSX.Element { /> - + + width={ICON_SIZE} + > @@ -93,7 +79,8 @@ function AccountCardItem({ onClose }: { onClose: () => void }): JSX.Element { backgroundColor="$surface3" borderRadius={ICON_BORDER_RADIUS} height={ICON_SIZE} - width={ICON_SIZE}> + width={ICON_SIZE} + > @@ -120,7 +107,8 @@ export function ReceiveCryptoModal(): JSX.Element { hideKeyboardOnSwipeDown backgroundColor={colors.surface1.get()} name={ModalName.ReceiveCryptoModal} - onClose={onClose}> + onClose={onClose} + > diff --git a/apps/mobile/src/screens/SettingsAppearanceScreen.tsx b/apps/mobile/src/screens/SettingsAppearanceScreen.tsx index eb96c4982ee..bc0f5b4ca55 100644 --- a/apps/mobile/src/screens/SettingsAppearanceScreen.tsx +++ b/apps/mobile/src/screens/SettingsAppearanceScreen.tsx @@ -12,10 +12,7 @@ import MoonIcon from 'ui/src/assets/icons/moon.svg' import SunIcon from 'ui/src/assets/icons/sun.svg' import { iconSizes } from 'ui/src/theme' import { useCurrentAppearanceSetting } from 'wallet/src/features/appearance/hooks' -import { - AppearanceSettingType, - setSelectedAppearanceSettings, -} from 'wallet/src/features/appearance/slice' +import { AppearanceSettingType, setSelectedAppearanceSettings } from 'wallet/src/features/appearance/slice' export function SettingsAppearanceScreen(): JSX.Element { const { t } = useTranslation() @@ -61,13 +58,7 @@ interface AppearanceOptionProps { Icon: React.FC } -function AppearanceOption({ - active, - title, - subtitle, - Icon, - option, -}: AppearanceOptionProps): JSX.Element { +function AppearanceOption({ active, title, subtitle, Icon, option }: AppearanceOptionProps): JSX.Element { const colors = useSporeColors() const dispatch = useAppDispatch() @@ -79,13 +70,9 @@ function AppearanceOption({ flexDirection="row" justifyContent="space-between" py="$spacing12" - onPress={(): Action => dispatch(setSelectedAppearanceSettings(option))}> - + onPress={(): Action => dispatch(setSelectedAppearanceSettings(option))} + > + {title} diff --git a/apps/mobile/src/screens/SettingsBiometricAuthScreen.tsx b/apps/mobile/src/screens/SettingsBiometricAuthScreen.tsx index 66bdd4fc439..0435f5c303d 100644 --- a/apps/mobile/src/screens/SettingsBiometricAuthScreen.tsx +++ b/apps/mobile/src/screens/SettingsBiometricAuthScreen.tsx @@ -41,31 +41,27 @@ export function SettingsBiometricAuthScreen(): JSX.Element { const dispatch = useAppDispatch() const [showUnsafeWarningModal, setShowUnsafeWarningModal] = useState(false) - const [unsafeWarningModalType, setUnsafeWarningModalType] = useState( - null - ) + const [unsafeWarningModalType, setUnsafeWarningModalType] = useState(null) const onCloseModal = useCallback(() => setShowUnsafeWarningModal(false), []) const { touchId } = useDeviceSupportsBiometricAuth() const biometricsMethod = useBiometricName(touchId) const { requiredForAppAccess, requiredForTransactions } = useBiometricAppSettings() - const { trigger } = useBiometricPrompt( - (args?: BiometricPromptTriggerArgs) => { - if (!args) { - return - } - const { biometricAppSettingType, newValue } = args - switch (biometricAppSettingType) { - case BiometricSettingType.RequiredForAppAccess: - dispatch(setRequiredForAppAccess(newValue)) - break - case BiometricSettingType.RequiredForTransactions: - dispatch(setRequiredForTransactions(newValue)) - break - } + const { trigger } = useBiometricPrompt((args?: BiometricPromptTriggerArgs) => { + if (!args) { + return } - ) + const { biometricAppSettingType, newValue } = args + switch (biometricAppSettingType) { + case BiometricSettingType.RequiredForAppAccess: + dispatch(setRequiredForAppAccess(newValue)) + break + case BiometricSettingType.RequiredForTransactions: + dispatch(setRequiredForTransactions(newValue)) + break + } + }) const options: BiometricAuthSetting[] = useMemo((): BiometricAuthSetting[] => { const handleOSBiometricAuthTurnedOff = (): void => { @@ -80,7 +76,7 @@ export function SettingsBiometricAuthScreen(): JSX.Element { [ { text: t('common.navigation.systemSettings'), onPress: openSettings }, { text: t('common.button.cancel') }, - ] + ], ) : Alert.alert( isAndroid @@ -89,10 +85,7 @@ export function SettingsBiometricAuthScreen(): JSX.Element { isAndroid ? t('settings.setting.biometrics.unavailable.message.android') : t('settings.setting.biometrics.unavailable.message.ios', { biometricsMethod }), - [ - { text: t('common.button.setup'), onPress: enroll }, - { text: t('common.button.cancel') }, - ] + [{ text: t('common.button.setup'), onPress: enroll }, { text: t('common.button.cancel') }], ) } @@ -182,7 +175,8 @@ export function SettingsBiometricAuthScreen(): JSX.Element { activeOpacity={1} onPress={(): void => { onValueChange(!value) - }}> + }} + > @@ -213,9 +207,7 @@ export function SettingsBiometricAuthScreen(): JSX.Element { )} - - {isAndroid ? t('settings.setting.biometrics.title') : biometricsMethod} - + {isAndroid ? t('settings.setting.biometrics.title') : biometricsMethod} +type Props = NativeStackScreenProps -export function SettingsCloudBackupPasswordConfirmScreen({ - navigation, - route: { params }, -}: Props): JSX.Element { +export function SettingsCloudBackupPasswordConfirmScreen({ navigation, route: { params } }: Props): JSX.Element { const { t } = useTranslation() const { password } = params diff --git a/apps/mobile/src/screens/SettingsCloudBackupPasswordCreateScreen.tsx b/apps/mobile/src/screens/SettingsCloudBackupPasswordCreateScreen.tsx index 15c13979421..300223146bf 100644 --- a/apps/mobile/src/screens/SettingsCloudBackupPasswordCreateScreen.tsx +++ b/apps/mobile/src/screens/SettingsCloudBackupPasswordCreateScreen.tsx @@ -13,10 +13,7 @@ import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants import { MobileScreens } from 'uniswap/src/types/screens/mobile' import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName' -type Props = NativeStackScreenProps< - SettingsStackParamList, - MobileScreens.SettingsCloudBackupPasswordCreate -> +type Props = NativeStackScreenProps // This screen is visited when no iCloud backup exists (checked from settings) export function SettingsCloudBackupPasswordCreateScreen({ @@ -59,9 +56,7 @@ export function SettingsCloudBackupPasswordCreateScreen({ {showCloudBackupInfoModal && ( - + @@ -82,10 +77,7 @@ export function SettingsCloudBackupPasswordCreateScreen({ - diff --git a/apps/mobile/src/screens/SettingsCloudBackupProcessingScreen.tsx b/apps/mobile/src/screens/SettingsCloudBackupProcessingScreen.tsx index 2a3babd3e8d..f104db4911f 100644 --- a/apps/mobile/src/screens/SettingsCloudBackupProcessingScreen.tsx +++ b/apps/mobile/src/screens/SettingsCloudBackupProcessingScreen.tsx @@ -5,10 +5,7 @@ import { Screen } from 'src/components/layout/Screen' import { CloudBackupProcessingAnimation } from 'src/features/CloudBackup/CloudBackupProcessingAnimation' import { MobileScreens } from 'uniswap/src/types/screens/mobile' -type Props = NativeStackScreenProps< - SettingsStackParamList, - MobileScreens.SettingsCloudBackupProcessing -> +type Props = NativeStackScreenProps export function SettingsCloudBackupProcessingScreen({ navigation, diff --git a/apps/mobile/src/screens/SettingsCloudBackupStatus.tsx b/apps/mobile/src/screens/SettingsCloudBackupStatus.tsx index daedaf0e8f5..a4416534200 100644 --- a/apps/mobile/src/screens/SettingsCloudBackupStatus.tsx +++ b/apps/mobile/src/screens/SettingsCloudBackupStatus.tsx @@ -18,15 +18,8 @@ import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudPro import { logger } from 'utilities/src/logger/logger' import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal' -import { - EditAccountAction, - editAccountActions, -} from 'wallet/src/features/wallet/accounts/editAccountSaga' -import { - AccountType, - BackupType, - SignerMnemonicAccount, -} from 'wallet/src/features/wallet/accounts/types' +import { EditAccountAction, editAccountActions } from 'wallet/src/features/wallet/accounts/editAccountSaga' +import { AccountType, BackupType, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' import { useAccounts } from 'wallet/src/features/wallet/hooks' type Props = NativeStackScreenProps @@ -44,7 +37,7 @@ export function SettingsCloudBackupStatus({ const mnemonicId = (accounts[address] as SignerMnemonicAccount)?.mnemonicId const backups = useCloudBackups(mnemonicId) const associatedAccounts = Object.values(accounts).filter( - (a) => a.type === AccountType.SignerMnemonic && a.mnemonicId === mnemonicId + (a) => a.type === AccountType.SignerMnemonic && a.mnemonicId === mnemonicId, ) const [showBackupDeleteWarning, setShowBackupDeleteWarning] = useState(false) @@ -64,7 +57,7 @@ export function SettingsCloudBackupStatus({ type: EditAccountAction.RemoveBackupMethod, address, backupMethod: BackupType.Cloud, - }) + }), ) setShowBackupDeleteWarning(false) navigation.navigate(MobileScreens.Settings) @@ -75,7 +68,7 @@ export function SettingsCloudBackupStatus({ Alert.alert( t('settings.setting.backup.error.title', { cloudProviderName: getCloudProviderName() }), t('settings.setting.backup.error.message.short'), - [{ text: t('common.button.ok'), style: 'default' }] + [{ text: t('common.button.ok'), style: 'default' }], ) } } @@ -115,11 +108,7 @@ export function SettingsCloudBackupStatus({ {/* @TODO: [MOB-249] Add non-backed up state once we have more options on this page */} - + {googleDriveEmail && ( @@ -134,7 +123,8 @@ export function SettingsCloudBackupStatus({ theme="detrimental" onPress={(): void => { setShowBackupDeleteWarning(true) - }}> + }} + > {t('settings.setting.backup.status.action.delete')} @@ -151,7 +141,8 @@ export function SettingsCloudBackupStatus({ onClose={(): void => { setShowBackupDeleteWarning(false) }} - onConfirm={onConfirmDeleteBackup}> + onConfirm={onConfirmDeleteBackup} + > {associatedAccounts.length > 1 && ( diff --git a/apps/mobile/src/screens/SettingsFiatCurrencyModal.tsx b/apps/mobile/src/screens/SettingsFiatCurrencyModal.tsx index 0b4e75d9593..9ed87f7be38 100644 --- a/apps/mobile/src/screens/SettingsFiatCurrencyModal.tsx +++ b/apps/mobile/src/screens/SettingsFiatCurrencyModal.tsx @@ -20,7 +20,8 @@ export function SettingsFiatCurrencyModal(): JSX.Element { dispatch(closeModal({ name: ModalName.FiatCurrencySelector }))}> + onClose={(): Action => dispatch(closeModal({ name: ModalName.FiatCurrencySelector }))} + > {t('settings.setting.currency.title')} @@ -41,11 +42,7 @@ function FiatCurrencySelection({ onClose }: { onClose: () => void }): JSX.Elemen return ( {ORDERED_CURRENCIES.map((currency) => ( - + ))} ) @@ -68,12 +65,7 @@ function FiatCurrencyOption({ active, currency, onPress }: FiatCurrencyOptionPro }, [dispatch, onPress, currency]) return ( - + {name} diff --git a/apps/mobile/src/screens/SettingsPrivacyScreen.tsx b/apps/mobile/src/screens/SettingsPrivacyScreen.tsx index c58a155c805..fb555744126 100644 --- a/apps/mobile/src/screens/SettingsPrivacyScreen.tsx +++ b/apps/mobile/src/screens/SettingsPrivacyScreen.tsx @@ -1,36 +1,19 @@ import React from 'react' import { useTranslation } from 'react-i18next' -import { useAppDispatch, useAppSelector } from 'src/app/hooks' import { BackHeader } from 'src/components/layout/BackHeader' import { Screen } from 'src/components/layout/Screen' -import { selectAllowAnalytics } from 'src/features/telemetry/selectors' -import { setAllowAnalytics } from 'src/features/telemetry/slice' -import { Flex, Text } from 'ui/src' -import { Switch } from 'wallet/src/components/buttons/Switch' +import { Text } from 'ui/src' +import { AnalyticsToggleLineSwitch } from 'wallet/src/components/settings/AnalyticsToggleLineSwitch' export function SettingsPrivacyScreen(): JSX.Element { const { t } = useTranslation() - const dispatch = useAppDispatch() - const analyticsAllowed = useAppSelector(selectAllowAnalytics) - - const onChangeAllowAnalytics = (enabled: boolean): void => { - dispatch(setAllowAnalytics({ enabled })) - } return ( {t('settings.setting.privacy.title')} - - - {t('settings.setting.privacy.analytics.title')} - - {t('settings.setting.privacy.analytics.description')} - - - - + ) } diff --git a/apps/mobile/src/screens/SettingsScreen.tsx b/apps/mobile/src/screens/SettingsScreen.tsx index ec62a042924..da09e9bff34 100644 --- a/apps/mobile/src/screens/SettingsScreen.tsx +++ b/apps/mobile/src/screens/SettingsScreen.tsx @@ -1,4 +1,3 @@ -/* eslint-disable max-lines */ import { useNavigation } from '@react-navigation/core' import { default as React, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -24,16 +23,7 @@ import { useBiometricContext } from 'src/features/biometrics/context' import { useBiometricName, useDeviceSupportsBiometricAuth } from 'src/features/biometrics/hooks' import { useWalletRestore } from 'src/features/wallet/hooks' import { getFullAppVersion } from 'src/utils/version' -import { - Button, - Flex, - IconProps, - Text, - TouchableArea, - useDeviceInsets, - useIsDarkMode, - useSporeColors, -} from 'ui/src' +import { Button, Flex, IconProps, Text, TouchableArea, useDeviceInsets, useIsDarkMode, useSporeColors } from 'ui/src' import { AVATARS_DARK, AVATARS_LIGHT } from 'ui/src/assets' import BookOpenIcon from 'ui/src/assets/icons/book-open.svg' import ContrastIcon from 'ui/src/assets/icons/contrast.svg' @@ -69,11 +59,7 @@ import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' import { useCurrentAppearanceSetting } from 'wallet/src/features/appearance/hooks' import { useAppFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' import { useCurrentLanguageInfo } from 'wallet/src/features/language/hooks' -import { - AccountType, - BackupType, - SignerMnemonicAccount, -} from 'wallet/src/features/wallet/accounts/types' +import { AccountType, BackupType, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' import { useAccounts, useHideSmallBalancesSetting, @@ -98,8 +84,7 @@ export function SettingsScreen(): JSX.Element { const currencyConversionEnabled = useFeatureFlag(FeatureFlags.CurrencyConversion) // check if device supports biometric authentication, if not, hide option - const { touchId: isTouchIdSupported, faceId: isFaceIdSupported } = - useDeviceSupportsBiometricAuth() + const { touchId: isTouchIdSupported, faceId: isFaceIdSupported } = useDeviceSupportsBiometricAuth() const biometricsMethod = useBiometricName(isTouchIdSupported) const currentAppearanceSetting = useCurrentAppearanceSetting() @@ -151,8 +136,8 @@ export function SettingsScreen(): JSX.Element { currentAppearanceSetting === 'system' ? t('settings.setting.appearance.option.device.title') : currentAppearanceSetting === 'dark' - ? t('settings.setting.appearance.option.dark.title') - : t('settings.setting.appearance.option.light.title'), + ? t('settings.setting.appearance.option.dark.title') + : t('settings.setting.appearance.option.light.title'), icon: , }, @@ -172,11 +157,6 @@ export function SettingsScreen(): JSX.Element { currentSetting: currentLanguage, icon: , }, - { - screen: MobileScreens.SettingsPrivacy, - text: t('settings.setting.privacy.title'), - icon: , - }, { text: t('settings.setting.smallBalances.title'), icon: , @@ -189,6 +169,11 @@ export function SettingsScreen(): JSX.Element { isToggleEnabled: hideSpamTokens, onToggle: onToggleHideSpamTokens, }, + { + screen: MobileScreens.SettingsPrivacy, + text: t('settings.setting.privacy.title'), + icon: , + }, // @TODO: [MOB-250] add back testnet toggle once we support testnets ], }, @@ -199,15 +184,10 @@ export function SettingsScreen(): JSX.Element { ...(deviceSupportsBiometrics ? [ { - screen: - MobileScreens.SettingsBiometricAuth as MobileScreens.SettingsBiometricAuth, + screen: MobileScreens.SettingsBiometricAuth as MobileScreens.SettingsBiometricAuth, isHidden: !isTouchIdSupported && !isFaceIdSupported, text: isAndroid ? t('settings.setting.biometrics.title') : biometricsMethod, - icon: isTouchIdSupported ? ( - - ) : ( - - ), + icon: isTouchIdSupported ? : , }, ] : []), @@ -222,8 +202,8 @@ export function SettingsScreen(): JSX.Element { screen: walletNeedsRestore ? MobileScreens.OnboardingStack : hasCloudBackup - ? MobileScreens.SettingsCloudBackupStatus - : MobileScreens.SettingsCloudBackupPasswordCreate, + ? MobileScreens.SettingsCloudBackupStatus + : MobileScreens.SettingsCloudBackupPasswordCreate, screenProps: walletNeedsRestore ? { screen: OnboardingScreens.RestoreCloudBackupLoading, @@ -323,9 +303,7 @@ export function SettingsScreen(): JSX.Element { const renderItem = ({ item, - }: ListRenderItemInfo< - SettingsSectionItem | SettingsSectionItemComponent - >): JSX.Element | null => { + }: ListRenderItemInfo): JSX.Element | null => { if (item.isHidden) { return null } @@ -336,9 +314,7 @@ export function SettingsScreen(): JSX.Element { } return ( - {t('settings.title')}}> + {t('settings.title')}}> + }} + > @@ -385,12 +362,7 @@ function OnboardingRow({ iconProps }: { iconProps: SvgProps }): JSX.Element { Onboarding - + ) @@ -434,43 +406,35 @@ function WalletSettings(): JSX.Element { {t('settings.section.wallet.title')} - {allAccounts - .slice(0, showAll ? allAccounts.length : DEFAULT_ACCOUNTS_TO_DISPLAY) - .map((account) => { - const isViewOnlyWallet = account.type === AccountType.Readonly + {allAccounts.slice(0, showAll ? allAccounts.length : DEFAULT_ACCOUNTS_TO_DISPLAY).map((account) => { + const isViewOnlyWallet = account.type === AccountType.Readonly - return ( - handleNavigation(account.address)}> - - - - - - ) - })} + return ( + handleNavigation(account.address)} + > + + + + + + ) + })} {allAccounts.length > DEFAULT_ACCOUNTS_TO_DISPLAY && ( )} @@ -490,18 +454,13 @@ function FooterSettings(): JSX.Element { setShowSignature(false) } : (): void => undefined, - SIGNATURE_VISIBLE_DURATION + SIGNATURE_VISIBLE_DURATION, ) return ( {showSignature ? ( - + {t('settings.footer')} @@ -521,7 +480,8 @@ function FooterSettings(): JSX.Element { variant="body2" onLongPress={(): void => { setShowSignature(true) - }}> + }} + > {t('settings.version', { appVersion: getFullAppVersion() })} diff --git a/apps/mobile/src/screens/SettingsViewSeedPhraseScreen.tsx b/apps/mobile/src/screens/SettingsViewSeedPhraseScreen.tsx index b749ddd1ae8..2e4c69774c0 100644 --- a/apps/mobile/src/screens/SettingsViewSeedPhraseScreen.tsx +++ b/apps/mobile/src/screens/SettingsViewSeedPhraseScreen.tsx @@ -33,11 +33,7 @@ export function SettingsViewSeedPhraseScreen({ {t('settings.setting.recoveryPhrase.title')} - + ) } diff --git a/apps/mobile/src/screens/SettingsWallet.tsx b/apps/mobile/src/screens/SettingsWallet.tsx index ed9488f0395..36d20b0a149 100644 --- a/apps/mobile/src/screens/SettingsWallet.tsx +++ b/apps/mobile/src/screens/SettingsWallet.tsx @@ -39,10 +39,7 @@ import { MobileScreens, UnitagScreens } from 'uniswap/src/types/screens/mobile' import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' import { Switch } from 'wallet/src/components/buttons/Switch' import { useENS } from 'wallet/src/features/ens/useENS' -import { - EditAccountAction, - editAccountActions, -} from 'wallet/src/features/wallet/accounts/editAccountSaga' +import { EditAccountAction, editAccountActions } from 'wallet/src/features/wallet/accounts/editAccountSaga' import { AccountType } from 'wallet/src/features/wallet/accounts/types' import { useAccounts, useSelectAccountNotificationSetting } from 'wallet/src/features/wallet/hooks' @@ -68,9 +65,7 @@ export function SettingsWallet({ const notificationOSPermission = useNotificationOSPermissionsEnabled() const notificationsEnabledOnFirebase = useSelectAccountNotificationSetting(address) - const [notificationSwitchEnabled, setNotificationSwitchEnabled] = useState( - notificationsEnabledOnFirebase - ) + const [notificationSwitchEnabled, setNotificationSwitchEnabled] = useState(notificationsEnabledOnFirebase) const showEditProfile = !readonly @@ -86,11 +81,10 @@ export function SettingsWallet({ useCallback( () => setNotificationSwitchEnabled( - notificationsEnabledOnFirebase && - notificationOSPermission === NotificationPermission.Enabled + notificationsEnabledOnFirebase && notificationOSPermission === NotificationPermission.Enabled, ), - [notificationOSPermission, notificationsEnabledOnFirebase] - ) + [notificationOSPermission, notificationsEnabledOnFirebase], + ), ) const onChangeNotificationSettings = (enabled: boolean): void => { @@ -101,7 +95,7 @@ export function SettingsWallet({ type: EditAccountAction.TogglePushNotification, enabled, address, - }) + }), ) setNotificationSwitchEnabled(enabled) } else { @@ -111,7 +105,7 @@ export function SettingsWallet({ type: EditAccountAction.TogglePushNotification, enabled: true, address, - }) + }), ) setNotificationSwitchEnabled(enabled) }, showNotificationSettingsAlert) @@ -164,9 +158,7 @@ export function SettingsWallet({ const renderItem = ({ item, - }: ListRenderItemInfo< - SettingsSectionItem | SettingsSectionItemComponent - >): JSX.Element | null => { + }: ListRenderItemInfo): JSX.Element | null => { if ('component' in item) { return item.component } @@ -181,7 +173,7 @@ export function SettingsWallet({ openModal({ name: ModalName.RemoveWallet, initialState: { address }, - }) + }), ) } @@ -189,12 +181,7 @@ export function SettingsWallet({ - + @@ -202,9 +189,7 @@ export function SettingsWallet({ : undefined - } + ListHeaderComponent={showEditProfile ? : undefined} keyExtractor={(_item, index): string => 'wallet_settings' + index} renderItem={renderItem} renderSectionFooter={(): JSX.Element => } @@ -265,12 +250,7 @@ function AddressDisplayHeader({ address }: { address: Address }): JSX.Element { /> {(!ensName || !!unitag) && ( - diff --git a/apps/mobile/src/screens/SettingsWalletManageConnection.tsx b/apps/mobile/src/screens/SettingsWalletManageConnection.tsx index aac99ee62da..a7e8212e68e 100644 --- a/apps/mobile/src/screens/SettingsWalletManageConnection.tsx +++ b/apps/mobile/src/screens/SettingsWalletManageConnection.tsx @@ -6,10 +6,7 @@ import { Screen } from 'src/components/layout/Screen' import { useWalletConnect } from 'src/features/walletConnect/useWalletConnect' import { MobileScreens } from 'uniswap/src/types/screens/mobile' -type Props = NativeStackScreenProps< - SettingsStackParamList, - MobileScreens.SettingsWalletManageConnection -> +type Props = NativeStackScreenProps export function SettingsWalletManageConnection({ route: { diff --git a/apps/mobile/src/screens/TokenDetailsScreen.tsx b/apps/mobile/src/screens/TokenDetailsScreen.tsx index bb680cd53ec..9450be0e612 100644 --- a/apps/mobile/src/screens/TokenDetailsScreen.tsx +++ b/apps/mobile/src/screens/TokenDetailsScreen.tsx @@ -19,36 +19,29 @@ import { Loader } from 'src/components/loading' import { selectModalState } from 'src/features/modals/selectModalState' import { disableOnPress } from 'src/utils/disableOnPress' import { useSkeletonLoading } from 'src/utils/useSkeletonLoading' -import { - Flex, - Separator, - Text, - TouchableArea, - useDeviceInsets, - useIsDarkMode, - useSporeColors, -} from 'ui/src' +import { Flex, Separator, Text, TouchableArea, useDeviceInsets, useIsDarkMode, useSporeColors } from 'ui/src' import EllipsisIcon from 'ui/src/assets/icons/ellipsis.svg' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { fonts, iconSizes, spacing } from 'ui/src/theme' import { useExtractedTokenColor } from 'ui/src/utils/colors' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' +import { PollingInterval } from 'uniswap/src/constants/misc' import { SafetyLevel, TokenDetailsScreenQuery, useTokenDetailsScreenQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' import Trace from 'uniswap/src/features/telemetry/Trace' import { ModalName } from 'uniswap/src/features/telemetry/constants' import { UniverseChainId } from 'uniswap/src/types/chains' import { MobileScreens } from 'uniswap/src/types/screens/mobile' +import { currencyIdToAddress, currencyIdToChain } from 'uniswap/src/utils/currencyId' import { NumberType } from 'utilities/src/format/types' -import { PollingInterval } from 'wallet/src/constants/misc' import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { isError, isNonPollingRequestInFlight } from 'wallet/src/data/utils' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { Language } from 'wallet/src/features/language/constants' @@ -57,7 +50,6 @@ import { useTokenContextMenu } from 'wallet/src/features/portfolio/useTokenConte import TokenWarningModal from 'wallet/src/features/tokens/TokenWarningModal' import { useTokenWarningDismissed } from 'wallet/src/features/tokens/safetyHooks' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' -import { currencyIdToAddress, currencyIdToChain } from 'wallet/src/utils/currencyId' function HeaderTitleElement({ data, @@ -79,10 +71,7 @@ function HeaderTitleElement({ const chain = onChainData?.chain return ( - + {convertFiatAmountFormatted(price, NumberType.FiatTokenPrice)} @@ -102,9 +91,7 @@ function HeaderTitleElement({ ) } -export function TokenDetailsScreen({ - route, -}: AppStackScreenProp): JSX.Element { +export function TokenDetailsScreen({ route }: AppStackScreenProp): JSX.Element { const { currencyId: _currencyId } = route.params // Potentially delays loading of perf-heavy content to speed up navigation const showSkeleton = useSkeletonLoading() @@ -144,16 +131,12 @@ export function TokenDetailsScreen({ chain: currencyIdToChain(_currencyId), currencyName: data?.token?.project?.name, }), - [_currencyId, data?.token?.project?.name] + [_currencyId, data?.token?.project?.name], ) return ( - + { @@ -216,9 +196,7 @@ function TokenDetails({ const { navigateToSwapFlow, navigateToSend } = useWalletNavigation() // set if attempting buy or sell, use for warning modal - const [activeTransactionType, setActiveTransactionType] = useState( - undefined - ) + const [activeTransactionType, setActiveTransactionType] = useState(undefined) const [showWarningModal, setShowWarningModal] = useState(false) const { tokenWarningDismissed, dismissWarningCallback } = useTokenWarningDismissed(_currencyId) @@ -238,7 +216,7 @@ function TokenDetails({ navigateToSwapFlow({ currencyField, currencyAddress, currencyChainId }) } }, - [currencyAddress, currencyChainId, navigateToSwapFlow, safetyLevel, tokenWarningDismissed] + [currencyAddress, currencyChainId, navigateToSwapFlow, safetyLevel, tokenWarningDismissed], ) const onPressSend = useCallback(() => { @@ -252,13 +230,7 @@ function TokenDetails({ if (activeTransactionType !== undefined) { navigateToSwapFlow({ currencyField: activeTransactionType, currencyAddress, currencyChainId }) } - }, [ - activeTransactionType, - currencyAddress, - currencyChainId, - dismissWarningCallback, - navigateToSwapFlow, - ]) + }, [activeTransactionType, currencyAddress, currencyChainId, dismissWarningCallback, navigateToSwapFlow]) const inModal = useAppSelector(selectModalState(ModalName.Explore)).isOpen @@ -281,7 +253,8 @@ function TokenDetails({ /> ) } - showHandleBar={inModal}> + showHandleBar={inModal} + > {!loading && !tokenColorLoading ? ( - + - + onPress={disableOnPress} + > + )} diff --git a/apps/mobile/src/screens/WebViewScreen.tsx b/apps/mobile/src/screens/WebViewScreen.tsx index 8eb2a086431..96464583161 100644 --- a/apps/mobile/src/screens/WebViewScreen.tsx +++ b/apps/mobile/src/screens/WebViewScreen.tsx @@ -10,9 +10,7 @@ import { useActiveAccountAddress } from 'wallet/src/features/wallet/hooks' export function WebViewScreen({ route, -}: - | SettingsStackScreenProp - | AppStackScreenProp): JSX.Element { +}: SettingsStackScreenProp | AppStackScreenProp): JSX.Element { const { headerTitle, uriLink } = route.params return ( @@ -23,11 +21,7 @@ export function WebViewScreen({ - {uriLink === uniswapUrls.helpUrl ? ( - - ) : ( - - )} + {uriLink === uniswapUrls.helpUrl ? : } ) } @@ -45,16 +39,10 @@ function ZendeskWebView({ uriLink }: { uriLink: string }): JSX.Element { webviewRef.current?.injectJavaScript(zendeskInjectJs) } }, - [zendeskInjectJs] + [zendeskInjectJs], ) - return ( - - ) + return } const WALLET_ADDRESS_QUERY_SELECTOR = '#request_custom_fields_11041337007757' diff --git a/apps/mobile/src/stories/Introduction.stories.mdx b/apps/mobile/src/stories/Introduction.stories.mdx index 8fc137aa583..430d4ca79a3 100644 --- a/apps/mobile/src/stories/Introduction.stories.mdx +++ b/apps/mobile/src/stories/Introduction.stories.mdx @@ -2,9 +2,7 @@ import { Meta } from '@storybook/addon-docs' - + # `@uniswap/mobile` diff --git a/apps/mobile/src/test/fixtures/explore.ts b/apps/mobile/src/test/fixtures/explore.ts index 1404f583fd6..ab7a4c5f05e 100644 --- a/apps/mobile/src/test/fixtures/explore.ts +++ b/apps/mobile/src/test/fixtures/explore.ts @@ -1,7 +1,7 @@ import { TokenItemData } from 'src/components/explore/TokenItem' import { Token } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { UniverseChainId } from 'uniswap/src/types/chains' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { token } from 'wallet/src/test/fixtures' import { createFixture } from 'wallet/src/test/utils' diff --git a/apps/mobile/src/test/fixtures/redux.ts b/apps/mobile/src/test/fixtures/redux.ts index 9584c19c606..cc92aacb098 100644 --- a/apps/mobile/src/test/fixtures/redux.ts +++ b/apps/mobile/src/test/fixtures/redux.ts @@ -15,10 +15,7 @@ type PreloadedMobileStateOptions = { account: Account | undefined } -export const preloadedMobileState = createFixture< - PreloadedState, - PreloadedMobileStateOptions ->({ +export const preloadedMobileState = createFixture, PreloadedMobileStateOptions>({ account: undefined, })(({ account }) => ({ ...(preloadedSharedState({ account }) as PreloadedState), diff --git a/apps/mobile/src/test/render.tsx b/apps/mobile/src/test/render.tsx index 320881827b2..44bc8e3a359 100644 --- a/apps/mobile/src/test/render.tsx +++ b/apps/mobile/src/test/render.tsx @@ -47,7 +47,7 @@ export function renderWithProviders( middleware: (getDefaultMiddleware) => getDefaultMiddleware(), }), ...renderOptions - }: ExtendedRenderOptions = {} + }: ExtendedRenderOptions = {}, ): RenderResult & { store: EnhancedStore } { @@ -85,13 +85,13 @@ type RenderHookWithProvidersResult = Omit( hook: () => R, - hookOptions?: ExtendedRenderHookOptions + hookOptions?: ExtendedRenderHookOptions, ): RenderHookWithProvidersResult // Require hookOptions if hook takes arguments export function renderHookWithProviders( hook: (args: P) => R, - hookOptions: ExtendedRenderHookOptions

+ hookOptions: ExtendedRenderHookOptions

, ): RenderHookWithProvidersResult /** @@ -103,7 +103,7 @@ export function renderHookWithProviders( */ export function renderHookWithProviders( hook: (args: P) => R, - hookOptions?: ExtendedRenderHookOptions

+ hookOptions?: ExtendedRenderHookOptions

, ): RenderHookWithProvidersResult { const { resolvers, diff --git a/apps/mobile/src/utils/hooks.ts b/apps/mobile/src/utils/hooks.ts index 6737080bf03..0024d2982a4 100644 --- a/apps/mobile/src/utils/hooks.ts +++ b/apps/mobile/src/utils/hooks.ts @@ -1,11 +1,11 @@ import { useFocusEffect, useIsFocused } from '@react-navigation/core' import { useCallback, useRef } from 'react' -import { PollingInterval } from 'wallet/src/constants/misc' +import { PollingInterval } from 'uniswap/src/constants/misc' export function usePollOnFocusOnly( startPolling: (interval: PollingInterval) => void, stopPolling: () => void, - pollingInterval: PollingInterval + pollingInterval: PollingInterval, ): void { useFocusEffect( useCallback(() => { @@ -13,7 +13,7 @@ export function usePollOnFocusOnly( return () => { stopPolling() } - }, [startPolling, stopPolling, pollingInterval]) + }, [startPolling, stopPolling, pollingInterval]), ) } diff --git a/apps/mobile/src/utils/reanimated.ts b/apps/mobile/src/utils/reanimated.ts index 23d9e040240..c970e42fd46 100644 --- a/apps/mobile/src/utils/reanimated.ts +++ b/apps/mobile/src/utils/reanimated.ts @@ -8,10 +8,7 @@ * https://github.com/willsp/polyfill-Number.toLocaleString-with-Locales/blob/master/polyfill.number.toLocaleString.js */ -function replaceSeparators( - sNum: string, - separators: { decimal: string; thousands: string } -): string { +function replaceSeparators(sNum: string, separators: { decimal: string; thousands: string }): string { 'worklet' const sNumParts = sNum.split('.') if (separators && separators.thousands && sNumParts[0]) { @@ -24,10 +21,7 @@ function replaceSeparators( return sNum } -function renderFormat( - template: string, - options: Record & { num?: string; code?: string } -): string { +function renderFormat(template: string, options: Record & { num?: string; code?: string }): string { 'worklet' for (const [option, value] of Object.entries(options)) { let updatedValue = value @@ -60,7 +54,7 @@ const round = (value: number, precision = 0): number => { function mapMatch( map: { [key in Language]: string | ((key: string, options?: OptionsType) => string) }, - locale: Language + locale: Language, ): string | ((key: string, options?: OptionsType) => string) { 'worklet' let match = locale @@ -348,7 +342,7 @@ export function numberToLocaleStringWorklet( value: number, locale: Language = 'en-US', options: OptionsType = {}, - symbol?: string + symbol?: string, ): string { 'worklet' if (locale && locale.length < 2) { @@ -372,10 +366,7 @@ export function numberToLocaleStringWorklet( sNum = value.toFixed(2) } - sNum = (<(key: string, options?: OptionsType) => string>mapMatch(transformForLocale, locale))( - sNum, - options - ) + sNum = (<(key: string, options?: OptionsType) => string>mapMatch(transformForLocale, locale))(sNum, options) if (options && options.currency && options.style === 'currency') { const format = currencyFormats[mapMatch(currencyFormatMap, locale)] @@ -399,7 +390,7 @@ export function numberToPercentWorklet( options: { precision: number absolute: boolean - } = { precision: DEFAULT_PRECISION, absolute: DEFAULT_ABSOLUTE } + } = { precision: DEFAULT_PRECISION, absolute: DEFAULT_ABSOLUTE }, ): string { 'worklet' diff --git a/apps/mobile/src/utils/useAddBackButton.tsx b/apps/mobile/src/utils/useAddBackButton.tsx index 97993f8b4ae..6fb33be2137 100644 --- a/apps/mobile/src/utils/useAddBackButton.tsx +++ b/apps/mobile/src/utils/useAddBackButton.tsx @@ -9,9 +9,7 @@ import { iconSizes } from 'ui/src/theme' * By default react-navigation will only show the back button if the screen is not the first one in the stack. */ export function useAddBackButton( - navigation: - | NativeStackNavigationProp - | NativeStackNavigationProp + navigation: NativeStackNavigationProp | NativeStackNavigationProp, ): void { useEffect((): void => { const shouldRenderBackButton = navigation.getState().index === 0 diff --git a/apps/mobile/src/utils/useAppStateTrigger.ts b/apps/mobile/src/utils/useAppStateTrigger.ts index 21334fa407a..5c5ed332e94 100644 --- a/apps/mobile/src/utils/useAppStateTrigger.ts +++ b/apps/mobile/src/utils/useAppStateTrigger.ts @@ -2,11 +2,7 @@ import { useEffect, useRef } from 'react' import { AppState, AppStateStatus } from 'react-native' /** Invokes `callback` when app state goes from `from` to `to`. */ -export function useAppStateTrigger( - from: AppStateStatus, - to: AppStateStatus, - callback: () => void -): void { +export function useAppStateTrigger(from: AppStateStatus, to: AppStateStatus, callback: () => void): void { const appState = useRef(AppState.currentState) useEffect(() => { diff --git a/apps/mobile/src/utils/useSagaStatus.ts b/apps/mobile/src/utils/useSagaStatus.ts index 3a5060c5c90..02e6c1df5e6 100644 --- a/apps/mobile/src/utils/useSagaStatus.ts +++ b/apps/mobile/src/utils/useSagaStatus.ts @@ -4,11 +4,7 @@ import { monitoredSagas } from 'src/app/saga' import { SagaState, SagaStatus } from 'wallet/src/utils/saga' // Convenience hook to get the status + error of an active saga -export function useSagaStatus( - sagaName: string, - onSuccess?: () => void, - resetSagaOnSuccess = true -): SagaState { +export function useSagaStatus(sagaName: string, onSuccess?: () => void, resetSagaOnSuccess = true): SagaState { const dispatch = useAppDispatch() const sagaState = useAppSelector((s): SagaState | undefined => s.saga[sagaName]) if (!sagaState) { diff --git a/apps/mobile/src/utils/version.ts b/apps/mobile/src/utils/version.ts index e9196833601..4861bab9978 100644 --- a/apps/mobile/src/utils/version.ts +++ b/apps/mobile/src/utils/version.ts @@ -1,5 +1,5 @@ import DeviceInfo from 'react-native-device-info' -import { isBetaEnv, isDevEnv } from 'uniswap/src/utils/env' +import { isBetaEnv, isDevEnv } from 'utilities/src/environment' import { StatsigEnvironmentTier } from 'wallet/src/version' /** diff --git a/apps/web/.depcheckrc b/apps/web/.depcheckrc index 1dc3f5c08fc..c5218e31f77 100644 --- a/apps/web/.depcheckrc +++ b/apps/web/.depcheckrc @@ -29,7 +29,6 @@ ignores: [ ## Linting and Babel "@babel/preset-env", "eslint-plugin-import", - "prettier", "terser-webpack-plugin", ## Testing "@types/testing-library__cypress", @@ -68,4 +67,5 @@ ignores: [ "utils", "i18n", "tamagui.config", + "setupRive" ] diff --git a/apps/web/.eslintrc.js b/apps/web/.eslintrc.js index ebe564cbe11..d3201301d82 100644 --- a/apps/web/.eslintrc.js +++ b/apps/web/.eslintrc.js @@ -14,6 +14,13 @@ module.exports = { rules: { // TODO: had to add this rule to avoid errors on monorepo migration that didnt happen in interface 'cypress/unsafe-to-chain-command': 'off', + + // let prettier do things: + semi: 0, + quotes: 0, + 'comma-dangle': 0, + 'no-trailing-spaces': 0, + 'no-extra-semi': 0, }, overrides: [ @@ -50,9 +57,7 @@ module.exports = { '@typescript-eslint/no-restricted-imports': [ 'error', { - ...restrictedImports, paths: [ - ...restrictedImports.paths, { name: 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks', importNames: ['usePortfolioBalancesQuery', 'usePortfolioBalancesWebLazyQuery'], @@ -147,6 +152,13 @@ module.exports = { ], }, }, + { + files: ['*.ts', '*.tsx'], + excludedFiles: ['*.native.*', '*.ios.*', '*.android.*'], + rules: { + 'no-restricted-imports': ['error', restrictedImports], + }, + }, { files: ['**/*.ts', '**/*.tsx'], excludedFiles: ['src/analytics/*'], diff --git a/apps/web/cypress/e2e/mini-portfolio/accounts.test.ts b/apps/web/cypress/e2e/mini-portfolio/accounts.test.ts index 32ca3af4c50..b8794abbd67 100644 --- a/apps/web/cypress/e2e/mini-portfolio/accounts.test.ts +++ b/apps/web/cypress/e2e/mini-portfolio/accounts.test.ts @@ -1,5 +1,7 @@ import { getTestSelector } from '../../utils' +const HAYDEN_ADDRESS = '0x50EC05ADe8280758E2077fcBC08D878D4aef79C3' + describe('Mini Portfolio account drawer', () => { beforeEach(() => { const portfolioSpy = cy.spy().as('portfolioSpy') @@ -72,7 +74,7 @@ describe('Mini Portfolio account drawer', () => { it('refetches balances when account changes', () => { cy.hardhat().then((hardhat) => { const accountA = hardhat.wallets[0].address - const accountB = hardhat.wallets[1].address + const accountB = HAYDEN_ADDRESS // Opens the account drawer cy.get(getTestSelector('web3-status-connected')).click() @@ -98,14 +100,13 @@ describe('Mini Portfolio account drawer', () => { }) it('fetches ENS name', () => { - const haydenAccount = '0x50EC05ADe8280758E2077fcBC08D878D4aef79C3' const haydenENS = 'hayden.eth' // Opens the account drawer cy.get(getTestSelector('web3-status-connected')).click() // Simulate wallet changing to Hayden's account - cy.window().then((win) => win.ethereum.emit('accountsChanged', [haydenAccount])) + cy.window().then((win) => win.ethereum.emit('accountsChanged', [HAYDEN_ADDRESS])) // Hayden's ENS name should be shown cy.contains(haydenENS).should('exist') @@ -121,7 +122,7 @@ describe('Mini Portfolio account drawer', () => { cy.get(getTestSelector('web3-status-connected')).click() // Simulate wallet changing to Hayden's account - cy.window().then((win) => win.ethereum.emit('accountsChanged', [haydenAccount])) + cy.window().then((win) => win.ethereum.emit('accountsChanged', [HAYDEN_ADDRESS])) // Hayden's ENS name should be shown cy.contains(haydenENS).should('exist') diff --git a/apps/web/cypress/e2e/swap/swap.test.ts b/apps/web/cypress/e2e/swap/swap.test.ts index d5167b8452a..bdefb399fc2 100644 --- a/apps/web/cypress/e2e/swap/swap.test.ts +++ b/apps/web/cypress/e2e/swap/swap.test.ts @@ -111,7 +111,7 @@ describe('Swap', () => { cy.wait('@PortfolioBalancesWeb') cy.get('#swap-currency-input .open-currency-select-button').click() cy.get(getTestSelector('chain-selector')).last().click() - cy.get(getTestSelector('Optimism-selector')).click() + cy.get(getTestSelector('network-button-10')).click() const sendSpy = cy.spy(hardhat.provider, 'send') cy.wrap(sendSpy).should('not.be.calledWith', 'wallet_switchEthereumChain') cy.get(getTestSelector('common-base-ETH')).click() diff --git a/apps/web/functions/api/image/nfts/asset/[[index]].tsx b/apps/web/functions/api/image/nfts/asset/[[index]].tsx index bc66b3a09a0..dc1cb078524 100644 --- a/apps/web/functions/api/image/nfts/asset/[[index]].tsx +++ b/apps/web/functions/api/image/nfts/asset/[[index]].tsx @@ -22,7 +22,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { const data = await getRequest( cacheUrl, () => getAsset(collectionAddress, tokenId, cacheUrl), - (data): data is NonNullable>> => Boolean(data.ogImage) + (data): data is NonNullable>> => Boolean(data.ogImage), ) if (!data) { @@ -67,7 +67,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { style: 'normal', }, ], - } + }, ) as Response } catch (error: any) { return new Response(error.message || error.toString(), { status: 500 }) diff --git a/apps/web/functions/api/image/nfts/collection/[index].tsx b/apps/web/functions/api/image/nfts/collection/[index].tsx index 662a476ac2e..59b5c4a1634 100644 --- a/apps/web/functions/api/image/nfts/collection/[index].tsx +++ b/apps/web/functions/api/image/nfts/collection/[index].tsx @@ -23,7 +23,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { cacheUrl, () => getCollection(collectionAddress, cacheUrl), (data): data is NonNullable>> => - Boolean(data.ogImage && data.name && data.nftCollectionData?.isVerified) + Boolean(data.ogImage && data.name && data.nftCollectionData?.isVerified), ) if (!data) { @@ -111,7 +111,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { style: 'normal', }, ], - } + }, ) as Response } catch (error: any) { return new Response(error.message || error.toString(), { status: 500 }) diff --git a/apps/web/functions/api/image/pools/[[index]].tsx b/apps/web/functions/api/image/pools/[[index]].tsx index ad896050d9f..05ddfd7b717 100644 --- a/apps/web/functions/api/image/pools/[[index]].tsx +++ b/apps/web/functions/api/image/pools/[[index]].tsx @@ -93,7 +93,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { const data = await getRequest( cacheUrl, () => getPool(networkName, poolAddress, cacheUrl), - (data): data is NonNullable>> => Boolean(data.title) + (data): data is NonNullable>> => Boolean(data.title), ) if (!data) { @@ -215,7 +215,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { style: 'normal', }, ], - } + }, ) as Response } catch (error: any) { return new Response(error.message || error.toString(), { status: 500 }) diff --git a/apps/web/functions/api/image/tokens/[[index]].tsx b/apps/web/functions/api/image/tokens/[[index]].tsx index 67ce3e90ec4..72e7ff05dfe 100644 --- a/apps/web/functions/api/image/tokens/[[index]].tsx +++ b/apps/web/functions/api/image/tokens/[[index]].tsx @@ -19,7 +19,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { const data = await getRequest( cacheUrl, () => getToken(networkName, tokenAddress, cacheUrl), - (data): data is NonNullable>> => Boolean(data.tokenData?.symbol && data.name) + (data): data is NonNullable>> => Boolean(data.tokenData?.symbol && data.name), ) if (!data) { @@ -159,7 +159,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { style: 'normal', }, ], - } + }, ) as Response } catch (error: any) { return new Response(error.message || error.toString(), { status: 500 }) diff --git a/apps/web/functions/components/metaTagInjector.ts b/apps/web/functions/components/metaTagInjector.ts index c36480f0457..ae3369df032 100644 --- a/apps/web/functions/components/metaTagInjector.ts +++ b/apps/web/functions/components/metaTagInjector.ts @@ -7,7 +7,10 @@ import { MetaTagInjectorInput } from 'shared-cloud/metatags' export class MetaTagInjector implements HTMLRewriterElementContentHandlers { static SELECTOR = 'head' - constructor(private input: MetaTagInjectorInput, private request: Request) {} + constructor( + private input: MetaTagInjectorInput, + private request: Request, + ) {} append(element: Element, attribute: string, content: string) { // without adding data-rh="true", react-helmet-async doesn't overwrite existing metatags diff --git a/apps/web/functions/explore/pools/__snapshots__/pool.test.ts.snap b/apps/web/functions/explore/pools/__snapshots__/pool.test.ts.snap index e67c645f2b9..90d4a2a0e06 100644 --- a/apps/web/functions/explore/pools/__snapshots__/pool.test.ts.snap +++ b/apps/web/functions/explore/pools/__snapshots__/pool.test.ts.snap @@ -21,18 +21,14 @@ exports[`should inject metadata for valid pools 1`] = ` - - - - - - + + + + + @@ -130,7 +126,7 @@ exports[`should inject metadata for valid pools 1`] = ` } } - + @@ -163,18 +159,14 @@ exports[`should inject metadata for valid pools 2`] = ` - - - - - - + + + + + @@ -272,7 +264,7 @@ exports[`should inject metadata for valid pools 2`] = ` } } - + @@ -305,18 +297,14 @@ exports[`should inject metadata for valid pools 3`] = ` - - - - - - + + + + + @@ -414,7 +402,7 @@ exports[`should inject metadata for valid pools 3`] = ` } } - + diff --git a/apps/web/functions/nfts/asset/__snapshots__/nft.test.ts.snap b/apps/web/functions/nfts/asset/__snapshots__/nft.test.ts.snap index 7ea1b531611..b0ebab009d8 100644 --- a/apps/web/functions/nfts/asset/__snapshots__/nft.test.ts.snap +++ b/apps/web/functions/nfts/asset/__snapshots__/nft.test.ts.snap @@ -21,18 +21,14 @@ exports[`should inject metadata for valid assets: Azuki 1`] = ` - - - - - - + + + + + @@ -130,7 +126,7 @@ exports[`should inject metadata for valid assets: Azuki 1`] = ` } } - + @@ -163,18 +159,14 @@ exports[`should inject metadata for valid assets: Bored Ape Yacht Club 1`] = ` - - - - - - + + + + + @@ -272,7 +264,7 @@ exports[`should inject metadata for valid assets: Bored Ape Yacht Club 1`] = ` } } - + @@ -305,18 +297,14 @@ exports[`should inject metadata for valid assets: CryptoPunk 1`] = ` - - - - - - + + + + + @@ -414,7 +402,7 @@ exports[`should inject metadata for valid assets: CryptoPunk 1`] = ` } } - + diff --git a/apps/web/functions/nfts/collection/__snapshots__/collection.test.ts.snap b/apps/web/functions/nfts/collection/__snapshots__/collection.test.ts.snap index d6f2730ca72..b9b0a77ca83 100644 --- a/apps/web/functions/nfts/collection/__snapshots__/collection.test.ts.snap +++ b/apps/web/functions/nfts/collection/__snapshots__/collection.test.ts.snap @@ -21,18 +21,14 @@ exports[`should inject metadata for collections 1`] = ` - - - - - - + + + + + @@ -130,7 +126,7 @@ exports[`should inject metadata for collections 1`] = ` } } - + @@ -163,18 +159,14 @@ exports[`should inject metadata for collections 2`] = ` - - - - - - + + + + + @@ -272,7 +264,7 @@ exports[`should inject metadata for collections 2`] = ` } } - + @@ -305,18 +297,14 @@ exports[`should inject metadata for collections 3`] = ` - - - - - - + + + + + @@ -414,7 +402,7 @@ exports[`should inject metadata for collections 3`] = ` } } - + diff --git a/apps/web/functions/utils/getRequest.ts b/apps/web/functions/utils/getRequest.ts index 9984fb407f1..474b4ec0c47 100644 --- a/apps/web/functions/utils/getRequest.ts +++ b/apps/web/functions/utils/getRequest.ts @@ -3,7 +3,7 @@ import Cache, { Data } from './cache' export async function getRequest( url: string, getData: () => Promise, - validateData: (data: Data) => data is T + validateData: (data: Data) => data is T, ): Promise { try { const cachedData = await Cache.match(url) diff --git a/apps/web/functions/utils/transformResponse.ts b/apps/web/functions/utils/transformResponse.ts index 18d7ef34a77..4709d969590 100644 --- a/apps/web/functions/utils/transformResponse.ts +++ b/apps/web/functions/utils/transformResponse.ts @@ -6,7 +6,7 @@ import { getRequest } from './getRequest' export async function transformResponse( request: Request, response: Response, - data: (() => Promise) | Data | undefined + data: (() => Promise) | Data | undefined, ) { try { if (typeof data === 'function') { diff --git a/apps/web/package.json b/apps/web/package.json index 4b392956504..b494ba04d74 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -16,12 +16,13 @@ "build:production": "yarn i18n:download:if-missing && craco build", "analyze": "source-map-explorer 'build/static/js/*.js' --no-border-checks --gzip", "serve": "serve build -s -l 3000", + "format": "../../scripts/prettier.sh", "lint": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ .", "lint:fix": "yarn eslint --ignore-path .gitignore --cache --cache-location node_modules/.cache/eslint/ . --fix", "typecheck": "tsc && yarn typecheck:cloud && yarn typecheck:cypress", "typecheck:cloud": "tsc -p functions/tsconfig.json", "typecheck:cypress": "tsc -p cypress/tsconfig.json", - "test": "craco test", + "test": "craco test --watchAll=false", "test:bundle": "node -r esbuild-register ./src/test-utils/bundle-size-test.ts", "snapshots": "craco test -u", "test:cloud": "yarn jest functions --config=functions/jest.config.json", @@ -137,7 +138,6 @@ "mini-css-extract-plugin": "^2.7.6", "path-browserify": "1.0.1", "postinstall-postinstall": "2.1.0", - "prettier": "latest", "process": "0.11.10", "react-scripts": "5.0.1", "resize-observer-polyfill": "1.5.1", @@ -172,6 +172,7 @@ "@reach/dialog": "0.10.5", "@reach/portal": "0.10.5", "@reduxjs/toolkit": "1.9.3", + "@rive-app/canvas": "2.8.3", "@rive-app/react-canvas": "4.6.2", "@sentry/browser": "7.80.0", "@sentry/core": "7.80.0", @@ -180,6 +181,7 @@ "@tamagui/core": "1.95.1", "@tamagui/portal": "1.95.1", "@tamagui/react-native-svg": "1.95.1", + "@tamagui/remove-scroll": "1.95.1", "@tanstack/react-query": "5.28.14", "@tanstack/react-table": "8.10.7", "@types/poisson-disk-sampling": "2.2.4", @@ -282,7 +284,7 @@ "uuid": "9.0.0", "video-extensions": "1.2.0", "viem": "2.x", - "wagmi": "2.5.19", + "wagmi": "2.8.4", "wcag-contrast": "3.0.0", "web-vitals": "2.1.4", "xml2js": "0.6.2", diff --git a/apps/web/public/csp.json b/apps/web/public/csp.json index 4d581910a0a..eda10105b3c 100644 --- a/apps/web/public/csp.json +++ b/apps/web/public/csp.json @@ -4,7 +4,6 @@ ], "scriptSrc": [ "'self'", - "'wasm-unsafe-eval'", "data:", "https://translate.googleapis.com/", "https://www.google-analytics.com", @@ -70,6 +69,7 @@ "https://lh3.googleusercontent.com/", "https://mainnet.base.org/", "https://o1037921.ingest.sentry.io", + "https://browser-intake-datadoghq.com", "https://openseauserdata.com/", "https://performance.radar.cloudflare.com/", "https://polygon-rpc.com/", @@ -89,7 +89,6 @@ "https://statsigapi.net", "https://trustwallet.com", "https://uniswap.org", - "https://unpkg.com/@rive-app/canvas@2.8.3/rive.wasm", "https://us-central1-uniswap-mobile.cloudfunctions.net/", "https://valid.rpki.cloudflare.com", "https://vercel.com", diff --git a/apps/web/public/nfts-sitemap.xml b/apps/web/public/nfts-sitemap.xml index 6b98d91ac5a..eea6c6574b6 100644 --- a/apps/web/public/nfts-sitemap.xml +++ b/apps/web/public/nfts-sitemap.xml @@ -2,677 +2,682 @@ https://app.uniswap.org/nfts/collection/0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x60e4d786628fea6478f785a6d7e704777c86a7c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xed5af388653567af2f388e6224dc7c4b3241c544 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x34d85c9cdeb23fa97cb08333b511ac86e1c4e258 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x99a9b7c1116f9ceeb1652de04d5969cce509b069 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x49cf6f5d44e70224e2e23fdcdd2c053f30ada28b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xb7f7f6c52f2e2fdb1963eab30438024864c313f6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x23581767a106ae21c074b2276d25e5c3e136a68b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x8a90cab2b38dba80c64b7734e58ee1db38b8992e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xba30e5f9bb24caa003e9f2f0497ad287fdf95623 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xbd3531da5cf5857e7cfaa92426877b022e612cf8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x7bd29408f11d2bfc23c34f18275bbf23bb716bc7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x306b1ea3ecdf94ab739f1910bbda052ed4a9f949 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x1a92f7381b9f03921564a437210bb9396471050c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x5cc5b05a8a13e3fbdb0bb9fccd98d38e50f90c38 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x5af0d9827e0c53e4799bb226655a1de152a425a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x3bf2922f4520a8ba0c2efc3d2a1539678dad5e9d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xe785e82358879f061bc3dcac6f0444462d4b5330 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x76be3b62873462d2142405439777e971754e8e77 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xfd43af6d3fe1b916c026f6ac35b3ede068d1ca01 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x1cb1a5e65610aeff2551a50f76a87a7d3fb649c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xff9c1b15b16263c61d017ee9f65c50e4ae0113d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x6339e5e072086621540d0362c4e3cea0d643e114 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xb932a70a57673d89f4acffbe830e8ed7f75fb9e0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x79fcdef22feed20eddacbb2587640e45491b757f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xa3aee8bce55beea1951ef834b99f3ac60d1abeeb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x769272677fab02575e84945f03eca517acc544cc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x4db1f25d3d98600140dfc18deb7515be5bd293af - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x34eebee6942d8def3c125458d1a86e0a897fd6f9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x59468516a8259058bad1ca5f8f4bff190d30e066 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x394e3d3044fc89fcdd966d3cb35ac0b32b0cda91 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x60bb1e2aa1c9acafb4d34f71585d7e959f387769 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x28472a58a490c5e09a238847f66a68a47cc76f0f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x341a1c534248966c4b6afad165b98daed4b964ef - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x82c7a8f707110f5fbb16184a5933e9f78a34c6ab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xccc441ac31f02cd96c153db6fd5fe0a2f4e6a68d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x764aeebcf425d56800ef2c84f2578689415a2daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x160c404b2b49cbc3240055ceaee026df1e8497a0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xd2f668a8461d6761115daf8aeb3cdf5f40c532c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x39ee2c7b3cb80254225884ca001f57118c8f21b6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xd774557b647330c91bf44cfeab205095f7e6c367 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x1792a96e5668ad7c167ab804a100ce42395ce54d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x04afa589e2b933f9463c5639f412b183ec062505 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xe75512aa3bec8f00434bbd6ad8b0a3fbff100ad6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x348fc118bcc65a92dc033a951af153d14d945312 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x892848074ddea461a15f337250da3ce55580ca85 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x5946aeaab44e65eb370ffaa6a7ef2218cff9b47d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x282bdd42f4eb70e7a9d9f40c8fea0825b7f68c5d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x4b15a9c28034dc83db40cd810001427d3bd7163d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x7ea3cca10668b8346aec0bf1844a49e995527c8b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xb852c6b5892256c264cc2c888ea462189154d8d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x9378368ba6b85c1fba5b131b530f5f5bedf21a18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x2acab3dea77832c09420663b0e1cb386031ba17b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x0c2e57efddba8c768147d1fdf9176a0a6ebd5d83 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x08d7c0242953446436f34b4c78fe9da38c73668d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x8943c7bac1914c9a7aba750bf2b6b09fd21037e0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x364c828ee171616a39897688a831c2499ad972ec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x7f36182dee28c45de6072a34d29855bae76dbe2f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xf61f24c2d93bf2de187546b14425bf631f28d6dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x797a48c46be32aafcedcfd3d8992493d8a1f256b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x123b30e25973fecd8354dd5f41cc45a3065ef88c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x6632a9d63e142f17a668064d41a21193b49b41a0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xf4ee95274741437636e748ddac70818b4ed7d043 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x57a204aa1042f6e66dd7730813f4024114d74f37 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xd1258db6ac08eb0e625b75b371c023da478e94a9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x75e95ba5997eb235f40ecf8347cdb11f18ff640b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xd532b88607b1877fe20c181cba2550e3bbd6b31c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xa1d4657e0e6507d5a94d06da93e94dc7c8c44b51 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xedb61f74b0d09b2558f1eeb79b247c1f363ae452 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x7d8820fa92eb1584636f4f5b8515b5476b75171a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x231d3559aa848bf10366fb9868590f01d34bf240 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xad9fd7cb4fc7a0fbce08d64068f60cbde22ed34c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x0e9d6552b85be180d941f1ca73ae3e318d2d4f1f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xb716600ed99b4710152582a124c697a7fe78adbf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xaadc2d4261199ce24a4b0a57370c4fcf43bb60aa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x4e1f41613c9084fdb9e34e11fae9412427480e56 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x79986af15539de2db9a5086382daeda917a9cf0c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xc99c679c50033bbc5321eb88752e89a93e9e83c5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xc36cf0cfcb5d905b8b513860db0cfe63f6cf9f5c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x9c8ff314c9bc7f6e59a9d9225fb22946427edc03 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x3110ef5f612208724ca51f5761a69081809f03b7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x036721e5a769cc48b3189efbb9cce4471e8a48b1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x524cab2ec69124574082676e6f654a18df49a048 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x7ab2352b1d2e185560494d5e577f9d3c238b78c5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x32973908faee0bf825a343000fe412ebe56f802a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x7daec605e9e2a1717326eedfd660601e2753a057 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xc1caf0c19a8ac28c41fe59ba6c754e4b9bd54de9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x33fd426905f149f8376e227d0c9d3340aad17af1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x466cfcd0525189b573e794f554b8a751279213ac - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x6be69b2a9b153737887cfcdca7781ed1511c7e36 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x80336ad7a747236ef41f47ed2c7641828a480baa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x9401518f4ebba857baa879d9f76e1cc8b31ed197 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x4b61413d4392c806e6d0ff5ee91e6073c21d6430 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xc3f733ca98e0dad0386979eb96fb1722a1a05e69 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x09233d553058c2f42ba751c87816a8e9fae7ef10 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x960b7a6bcd451c9968473f7bbfd9be826efd549a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x36d30b3b85255473d27dd0f7fd8f35e36a9d6f06 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x698fbaaca64944376e2cdc4cad86eaa91362cf54 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x497a9a79e82e6fc0ff10a16f6f75e6fcd5ae65a8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x41a322b28d0ff354040e2cbc676f0320d8c8850d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xa9c0a07a7cb84ad1f2ffab06de3e55aab7d523e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x942bc2d3e7a589fe5bd4a5c6ef9727dfd82f5c8a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x8821bee2ba0df28761afff119d66390d594cd280 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x8c6def540b83471664edc6d5cf75883986932674 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x8d9710f0e193d3f95c0723eaaf1a81030dc9116d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x86825dfca7a6224cfbd2da48e85df2fc3aa7c4b1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x629a673a8242c2ac4b7b8c5d8735fbeac21a6205 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x9a534628b4062e123ce7ee2222ec20b86e16ca8f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xc2c747e0f7004f9e8817db2ca4997657a7746928 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x73da73ef3a6982109c4d5bdb0db9dd3e3783f313 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xc92ceddfb8dd984a89fb494c376f9a48b999aafc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x3248e8ba90facc4fdd3814518c14f8cc4d980e4b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x67d9417c9c3c250f61a83c7e8658dac487b56b09 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xb6a37b5d14d502c3ab0ae6f3a0e058bc9517786e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x86c10d10eca1fca9daf87a279abccabe0063f247 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x4b3406a41399c7fd2ba65cbc93697ad9e7ea61e5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xb0640e8b5f24bedc63c33d371923d68fde020303 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xd3d9ddd0cf0a5f0bfb8f7fceae075df687eaebab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xa5c0bd78d1667c13bfb403e2a3336871396713c5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x4d7d2e237d64d1484660b55c0a4cc092fa5e6716 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xfcb1315c4273954f74cb16d5b663dbf479eec62e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x66d1db16101502ed0ca428842c619ca7b62c8fef - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x128675d4fddbc4a0d3f8aa777d8ee0fb8b427c2f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x19b86299c21505cdf59ce63740b240a9c822b5e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xacf63e56fd08970b43401492a02f6f38b6635c91 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x0bebad1ff25c623dff9605dad4a8f782d5da37df - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xdceaf1652a131f32a821468dc03a92df0edd86ea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x273f7f8e6489682df756151f5525576e322d51a3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x77372a4cc66063575b05b44481f059be356964a4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0xf5b0a3efb8e8e4c201e2a935f110eaaf3ffecb8d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x22c36bfdcef207f9c0cc941936eff94d4246d14a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x59325733eb952a92e069c87f0a6168b29e80627f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.7 https://app.uniswap.org/nfts/collection/0x0e3a2a1f2146d86a604adc220b4967a898d7fe07 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z + 0.7 + + + https://app.uniswap.org/nfts/collection/0x3af2a97414d1101e2107a70e7f33955da1346305 + 2024-06-28T19:37:25.115Z 0.7 \ No newline at end of file diff --git a/apps/web/public/pools-sitemap.xml b/apps/web/public/pools-sitemap.xml index 29d3e4a48ea..63bccbce5df 100644 --- a/apps/web/public/pools-sitemap.xml +++ b/apps/web/public/pools-sitemap.xml @@ -2,5272 +2,5392 @@ https://app.uniswap.org/explore/pools/ethereum/0xcbcdf9626bc03e24f779434178a73a0b4bad62ed - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8ad599c3a0ff1de082011efddc58f1908eb6e6d8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4e68ccd3e89f51c3074ca5072bbac773960dfa36 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4585fe77225b41b697c938b018e2ac67ac5a20c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc63b0708e2f7e69cb8a1df0e1389a98c35a76d52 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x99ac8ca7087fa4a2a1fb6357269965a2014abc35 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x11b815efb8f581194ae79006d24e0d814b7697f6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa6cc3c2531fdaa6ae1a3ca84c2855806728693e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x5777d92f208679db4b9778590fa3cab3ac9e2168 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1d42064fc4beb5f8aaf85f4617ae8b3b5b8bd801 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc2e9f25be6257c210d7adf0d4cd6e3e881ba25f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x11950d141ecb863f01007add7d1a342041227b58 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc5c134a1f112efa96003f8559dba6fac0ba77692 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1df4c6e36d61416813b42fe32724ef11e363eddc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x12d6867fa648d269835cf69b49f125147754b54d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x3416cf6c708da44db2624d63ea0aaef7113527c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe8c6c9227491c0a8156a0106a0204d881bb7e531 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x04708077eca6bb527a5bbbd6358ffb043a9c1c14 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9db9e0e53058c89e5b94e29621a205198648425b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xf239009a101b6b930a527deaab6961b6e7dec8a6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xfe0df74636bc25c7f2400f22fe7dae32d39443d2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xf4c5e0f4590b6679b3030d29a84857f226087fef - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x5764a6f2212d502bc5970f9f129ffcd61e5d7563 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa3f558aebaecaf0e11ca4b2199cc5ed341edfd74 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x99132b53ab44694eeb372e87bced3929e4ab8456 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x6c6bc977e13df9b0de53b251522280bb72383700 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9d96880952b4c80a55099b9c258250f2cc5813ec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x3afdc5e6dfc0b0a507a8e023c9dce2cafc310316 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x290a6a7460b308ee3f19023d2d00de604bcf5b42 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xac4b3dacb91461209ae9d41ec517c2b9cb1b7daf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x60594a405d53811d3bc4766596efd80fd545a270 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x331399c614ca67dee86733e5a2fba40dbb16827c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4b5ab61593a2401b1075b90c04cbcdd3f87ce011 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x844eb5c280f38c7462316aad3f338ef9bda62668 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe936f0073549ad8b1fa53583600d629ba9375161 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2f62f2b4c5fcd7570a709dec05d68ea19c82a9ec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x381fe4eb128db1621647ca00965da3f9e09f4fac - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x97e7d56a0408570ba1a7852de36350f7713906ec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xcd423f3ab39a11ff1d9208b7d37df56e902c932b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe15e6583425700993bd08f51bf6e7b73cd5da91b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x69d91b94f0aaf8e8a2586909fa77a5c2c89818d5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe42318ea3b998e8355a3da364eb9d48ec725eb45 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xad9ef19e289dcbc9ab27b83d2df53cdeff60f02d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x3b685307c8611afb2a9e83ebc8743dc20480716e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7bea39867e4169dbe237d55c8242a8f2fcdcc387 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7b1e5d984a43ee732de195628d20d05cfabc3cc7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7858e59e0c01ea06df3af3d20ac7b0003275d4bf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xae2a25cbdb19d0dc0dddd1d2f6b08a6e48c4a9a9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x180efc1349a69390ade25667487a826164c9c6e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x197d7010147df7b99e9025c724f13723b29313f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8dbee21e8586ee356130074aaa789c33159921ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x570febdf89c07f256c75686caca215289bb11cfc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe3d3551bb608e7665472180a20280630d9e938aa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4e34da137f0b317c633838458e0c923a5e088752 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x5281e311734869c64ca60ef047fd87759397efe6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x14af1804dbbf7d621ecc2901eef292a24a0260ea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x80a9ae39310abf666a87c743d6ebbd0e8c42158e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc31e54c7a869b9fcbecc14363cf510d1c41fa443 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2f5e87c9312fa29aed5c179e456625d79015299c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc6962004f452be9203591991d15f6b388e09e8d0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc6f780497a95e246eb9449f5e4770916dcd6396a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x641c00a822e8b671738d32a431a4fb6074e5c79d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x92c63d0e701caae670c9415d91c474f686298f00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1aeedd3727a6431b8f070c0afaa81cc74f273882 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xcda53b1f66614552f834ceef361a8d12a0b8dad8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x35218a1cbac5bbc3e57fd9bd38219d37571b3537 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x17c14d2c404d167802b16c450d3c99f88f2c4f4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x468b88941e7cc0b88c1869d68ab6b570bcef62ff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xdbaeb7f0dfe3a0aafd798ccecb5b22e708f7852c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x149e36e72726e0bcea5c59d40df2c43f60f5a22d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xbaaf1fc002e31cb12b99e4119e5e350911ec575b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa67f72f21bd9f91db2da2d260590da5e6c437009 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x92fd143a8fa0c84e016c2765648b9733b0aa519e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7cf803e8d82a50504180f417b8bc7a493c0a0503 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x81c48d31365e6b526f6bbadc5c9aafd822134863 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x446bf9748b4ea044dd759d9b9311c70491df8f29 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc82819f72a9e77e2c0c3a69b3196478f44303cf4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x50c7390dfdd3756139e6efb5a461c2eb7331ceb4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1dfc1054e0e2a10e33c9ca21aad5aa8a1cce91e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc91b7b39bbb2c733f0e7459348fd0c80259c8471 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x59d72ddb29da32847a4665d08ffc8464a7185fae - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x09ba302a3f5ad2bf8853266e271b005a5b3716fe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa77d77c9773c35e910acc2e30cefe52b54a58414 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8da66e470403b3d3eee66c67e2c61fda6e248ad1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2f020e708811c054f146eebcc4d5a215fd4eec26 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7e7fb3cceca5f2ac952edf221fd2a9f62e411980 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x68c685fd52a56f04665b491d491355a624540e85 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa8328bf492ba1b77ad6381b3f7567d942b000baf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc0cf0f380ddb44dbcaf19a86d094c8bba3efa04a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa169d1ab5c948555954d38700a6cdaa7a4e0c3a0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1862200e8e7ce1c0827b792d0f9546156f44f892 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x05bbaaa020ff6bea107a9a1e06d2feb7bfd79ed2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xd02a4969dc12bb889754361f8bcf3385ac1b2077 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc24f7d8e51a64dc1238880bd00bb961d54cbeb29 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7c06736e41236fecd681dd3353aa77ecd19ea565 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc473e2aee3441bf9240be85eb122abb059a3b57c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x14353445c8329df76e6f15e9ead18fa2d45a8bb6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2039f8c9cd32ba9cd2ea7e575d5b1abea93f7527 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xd3e11119d2680c963f1cdcffece0c4ade823fb58 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8e295789c9465487074a65b1ae9ce0351172393f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x97bca422ec0ee4851f2110ea743c1cd0a14835a1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xbe3ad6a5669dc0b8b12febc03608860c31e2eef6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x56ebd63a756b94d3de9cea194896b4920b64fb01 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe2ddd33585b441b9245085588169f35108f85a6e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x84436a2af97f37018db116ae8e1b691666db3d00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x180efc1349a69390ade25667487a826164c9c6e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x197d7010147df7b99e9025c724f13723b29313f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8dbee21e8586ee356130074aaa789c33159921ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x570febdf89c07f256c75686caca215289bb11cfc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe3d3551bb608e7665472180a20280630d9e938aa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x4e34da137f0b317c633838458e0c923a5e088752 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x5281e311734869c64ca60ef047fd87759397efe6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x68f5c0a2de713a54991e01858fd27a3832401849 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x4533bad2dc588f0fadf8d2e72386d4cd6a19b519 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x85149247691df622eaf1a8bd0cafd40bc45154a9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0392b358ce4547601befa962680bede836606ae2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x1c3140ab59d6caf9fa7459c6f83d4b52ba881d36 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd1f1bad4c9e6c44dec1e9bf3b94902205c5cd6c3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x03af20bdaaffb4cc0a521796a223f7d85e2aac31 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x73b14a78a0d396c521f954532d43fd5ffe385216 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xac85eaf55e9c60ed40a683de7e549d23fdfbeb33 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x04f6c85a1b00f6d9b75f91fd23835974cc07e65c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x730691cdac3cbd4d41fc5eb9d8abbb0cea795b94 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x535541f1aa08416e69dc4d610131099fa2ae7222 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xfc1f3296458f9b2a27a0b91dd7681c4020e09d05 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x85c31ffa3706d1cce9d525a00f1c7d4a2911754c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd52533a3309b393afebe3176620e8ccfb6159f8a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xff7fbdf7832ae524deda39ca402e03d92adff7a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb589969d38ce76d3d7aa319de7133bc9755fd840 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xf334f6104a179207ddacfb41fa3567feea8595c2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x1fb3cf6e48f1e7b10213e7b6d87d4c073c7fdb7b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd4344ea0c5ade7e22b9b275f0bde7a145dec5a23 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x5b42a63d6741416ce9a7b9f4f16d8c9231ccddd4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x252cbdff917169775be2b552ec9f6781af95e7f6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2ab22ac86b25bd448a4d9dc041bd2384655299c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc858a329bf053be78d6239c4a4343b8fbd21472b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa73c628eaf6e283e26a7b1f8001cf186aa4c0e8e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb533c12fb4e7b53b5524eab9b47d93ff6c7a456f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2ae3d6096d8215ac2acddf30c60caa984ea5debe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x19ea026886cbb7a900ecb2458636d72b5cae223b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x6f32061f59a21086c334d0d45f804089ce374aaf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xfaf037caafa9620bfaebc04c298bf4a104963613 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xadb35413ec50e0afe41039eac8b930d313e94fa4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe9e3893921de87b1194a8108f9d70c24bde71c27 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xf1f199342687a7d78bcc16fce79fa2665ef870e1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xf44acaa38be5e965c5ddf374e7a2ba270e580684 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x36e42931a765022790b797963e42c5522d6b585a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x5adba6c5589c50791dd65131df29677595c7efa7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x3249e3e3e4133ee18e65347daf586610cc265f54 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xca1b837c87c6563910c2befa48834fa2a8c3d72d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x6ef7b14bcd8d989cef8f8ec8ba4bf371b2ac95fd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x37ffd11972128fd624337ebceb167c8c0a5115ff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe62bd99a9501ca33d98913105fc2bec5bae6e5dd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb2ac2e5a3684411254d58b1c5a542212b782114d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb0efaf46a1de55c54f333f93b1f0641e73bc16d0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd0fa3b5264ccde31e8b094b86bca4a1e97d3c603 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xad4c666fc170b468b19988959eb931a3676f0e9f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x790fde1fd6d2568050061a88c375d5c2e06b140b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xaefc1edaede6adadcdf3bb344577d45a80b19582 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa8a5356ee5d02fe33d72355e4f698782f8f199e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x55bc964fe3b0c8cc2d4c63d65f1be7aef9bb1a3c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x95d9d28606ee55de7667f0f176ebfc3215cfd9c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x180efc1349a69390ade25667487a826164c9c6e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x197d7010147df7b99e9025c724f13723b29313f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8dbee21e8586ee356130074aaa789c33159921ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x570febdf89c07f256c75686caca215289bb11cfc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe3d3551bb608e7665472180a20280630d9e938aa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x4e34da137f0b317c633838458e0c923a5e088752 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x5281e311734869c64ca60ef047fd87759397efe6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x45dda9cb7c25131df268515131f647d726f50608 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x50eaedb835021e4a108b7290636d62e9765cc6d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x167384319b41f7094e62f7506409eb38079abff8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa374094527e1673a86de625aa59517c5de346d32 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x86f1d8390222a3691c28938ec7404a1661e618e0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xeda1094f59a4781456734e5d258b95e6be20b983 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x847b64f9d3a95e977d157866447a5c0a5dfa0ee5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x94ab9e4553ffb839431e37cc79ba8905f45bfbea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0e44ceb592acfc5d3f09d996302eb4c499ff8c10 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x1e5bd2ab4c308396c06c182e1b7e7ba8b2935b83 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9b08288c3be4f62bbf8d1c20ac9c5e6f9467d8b7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb6e57ed85c4c9dbfef2a68711e9d6f36c56e0fcb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x3e31ab7f37c048fc6574189135d108df80f0ea26 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xd36ec33c8bed5a9f7b6630855f1533455b98a418 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xdac8a8e6dbf8c690ec6815e0ff03491b2770255d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xfe343675878100b344802a6763fd373fdeed07a4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0a28c2f5e0e8463e047c203f00f649812ae67e4f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x88f3c15523544835ff6c738ddb30995339ad57d6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x98b9162161164de1ed182a0dfa08f5fbf0f733ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xeef1a9507b3d505f0062f2be9453981255b503c8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc4c06c9a239f94fc0a1d3e04d23c159ebe8316f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x849ec65748107aedc518dbc42961f358ea1361a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2db87c4831b2fec2e35591221455834193b50d1b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa4d8c89f0c20efbe54cba9e7e7a7e509056228d9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x642f28a89fa9d0fa30e664f71804bfdd7341d21f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2aceda63b5e958c45bd27d916ba701bc1dc08f7a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x781067ef296e5c4a4203f81c593274824b7c185d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4ccd010148379ea531d6c587cfdd60180196f9b1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xd866fac7db79994d08c0ca2221fee08935595b4b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x941061770214613ba0ca3db9a700c39587bb89b6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa9077cdb3d13f45b8b9d87c43e11bce0e73d8631 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa01f64fa1b923dd9c5c7618b39a6ba8098a88863 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa830ff28bb7a46570a7e43dc24a35a663b9cfc2e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8837a61644d523cbe5216dde226f8f85e3aa9be3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xca5d44977d6de1846530eb434167b208752fba7d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4d05f2a005e6f36633778416764e82d1d12e7fbb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x41e64a5bc929fa8e6a9c8d7e3b81a13b21ff3045 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x3ea34cfc9322273311f7843826a2581c4a00fd39 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x785061ed819414dc4269d2a5d5974069c0daea96 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x3f5228d0e7d75467366be7de2c31d0d098ba2c23 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2e3f22e9a1c2470b2e293351f48c99e1fd788f32 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2a08c38c7e1fa969325e2b64047abb085dec3756 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe6c36eed27c2e8ecb9a233bf12da06c9730b5955 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xefa98fdf168f372e5e9e9b910fcdfd65856f3986 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x76fa081e510f43ac8335efdb4db88c9ff1894413 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc6832ef0af793336aa44a936e54b992bff47e7cd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x865f456479a21e2b3d866561d7171a3d0a7b112d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xbd934a7778771a7e2d9bf80596002a214d8c9304 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9ab9f658104467604b5afa9a3e1df62f35f7b208 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x6e430d59ba145c59b73a6db674fe3d53c1f31cae - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x180efc1349a69390ade25667487a826164c9c6e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x197d7010147df7b99e9025c724f13723b29313f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8dbee21e8586ee356130074aaa789c33159921ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x570febdf89c07f256c75686caca215289bb11cfc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe3d3551bb608e7665472180a20280630d9e938aa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4e34da137f0b317c633838458e0c923a5e088752 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x5281e311734869c64ca60ef047fd87759397efe6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x9e37cb775a047ae99fc5a24dded834127c4180cd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x48413707b70355597404018e7c603b261fcadf3f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xade9bcd4b968ee26bed102dd43a55f6a8c2416df - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xda679706ff21114ac9fac5198bff24543f357a16 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xba3f945812a83471d709bce9c3ca699a19fb46f7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc9034c3e7f58003e6ae0c8438e7c8f4598d5acaa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x4c36388be6f416a29c8d8eee81c771ce6be14b18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa1b2457c0b627f97f6cc892946a382451e979014 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x4b0aaf3ebb163dd45f663b38b6d93f6093ebc2d3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xae2ce200bdb67c472030b31f602f0756c9aeb61c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x3bc5180d5439b500f381f9a46f15dd6608101671 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x5122e02898ece3bc62df8c1efdb29a9e914244d3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x24e1cbd6fed006ceed9af0dce688acc7951d57a9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x2556230ac694093d4d3b7b965a2f2d77d4c403a4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xdaca082c2c7d052a96fa83ea9d3a7b6839e39586 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa555149210075702a734968f338d5e1cbd509354 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x10648ba41b8565907cfa1496765fa4d95390aa0d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x00bcec1526dae1e170a53017b8775a93b7810d7c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x20e068d76f9e90b90604500b84c7e19dcb923e7e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x6b93950a9b589bc32b82a5df4e5148f98a7fae27 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd9caa6dbe6791fcb7fc9fb59d1a6b3dd8c1c2339 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x62e81e93136ac42a1ada48d4098f5f9e703e7455 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x84206d33845c9d811438b6fe4e7a0c634748dc50 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd0b53d9277642d899df5c87a3966a349a798f224 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xcfa7c4bb565915f1c4f9475e2a0536d31efad776 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa7de21f28ca460b45373b217cd4eb111c3faeff8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xb64dff20dd5c47e6dbb56ead80d23568006dec1e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xad4e969f4193878e5cc89cefb57faf6c7c0048da - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xdf5eb97e3e23ca7f5a5fd2264680377c211310ba - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xf16baaae8eb7b37f4280e72924479f69e7a61f32 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xe745a591970e0fa981204cf525e170a2b9e4fb93 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x64b74c66b9ba60ca668b781289767ae7298f37ae - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x17e1ebd791e7253a5e606fd94c5b66c14d873136 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x46715bd57b9ec01deadb35fe096fb44acda79414 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x3447accd4b8e735329d1065244aad2ed630f0122 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x2feb7f3ffc243f7de94d5ea5975533d301584e07 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x0d5959a52e7004b601f0be70618d01ac3cdce976 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x2170ca774e48a3f51559917ada6f9d7ae8f7bfea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x62a76dfa8951aefcff787e790782db3633ebf422 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x8073679e0b3b2d1d665777cf1b2b5b1c2d3d2d0c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x143f1a6f3fb32e6ab3f22d3cc6b417b5c2197599 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x82ad659c2f152aad59bb37cbc5e7663a2de0c607 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa4efe9e8e2a2d5a2ac46805f233b8e49d0e11955 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xfcc89a1f250d76de198767d33e1ca9138a7fb54b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x2faa2b42b782d578a160f61bb7cd763a17476730 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xdd44c0e83c2570062d1e6fdd440b4724862e8f31 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xe3930a14641786e123e7bbe842d701fa1cbfe2df - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x6d03360ce4764e862ed81660c1f76cc2711b14b6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc055f66f228105072315247785c00299d0ce27e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xcae1d141ab11cef0a415cf0440025e1e5e962e06 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x180efc1349a69390ade25667487a826164c9c6e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x197d7010147df7b99e9025c724f13723b29313f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x8dbee21e8586ee356130074aaa789c33159921ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x570febdf89c07f256c75686caca215289bb11cfc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xe3d3551bb608e7665472180a20280630d9e938aa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x4e34da137f0b317c633838458e0c923a5e088752 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x5281e311734869c64ca60ef047fd87759397efe6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0f338ec12d3f7c3d77a4b9fcc1f95f3fb6ad0ea6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4eaa90264d6a3567228dcb5cfc242200da586437 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x6fe9e9de56356f7edbfcbb29fab7cd69471a4869 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf420603317a0996a3fce1b1a80993eaef6f7ae1a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x47a90a2d92a8367a91efa1906bfc8c1e05bf10c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x41bf5eeae051fbd2e97b76b5f8f0fdcc1a1e526b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x28df0835942396b7a1b7ae1cd068728e6ddbbafd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa3f3664a52f01b42557524bd14556e379daf5669 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x1fd22fa7274bafebdfb1881321709f1219744829 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe39cfc1a2e51a09ecbd060a24ee4eef5a97697bb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x06396509195eb9e07c38a016694dc9ff535b128a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5a1c486edefda2f09d3b349fadc38524f1743826 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5bf1cf153c102a79d9e18b7fb7c79ba57fa70d0c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2c3c320d49019d4f9a92352e947c7e5acfe47d68 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4141325bac36affe9db165e854982230a14e6d48 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x17507bef4c3abc1bc715be723ee1baf571256e05 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8149b92ea743cc382aada523b68b8834733b9015 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc98f01bf2141e1140ef8f8cad99d4b021d10718f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x7f9d307973cdabe42769d9712df8ee1cc1a28d10 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5c87da28a45e5089b762dcbbd86f743d14c54317 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2cd97604ef77bbcb1fa0cff47545dff8ec7def08 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x7862d9b4be2156b15d54f41ee4ede2d5b0b455e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x554548b404213c7efcdbab933f52edfe3c581834 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x63008c5ea4e47f5421e0e1428b1c5043a507d0d0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0350ca994791c4b07a5b02b08aaf9d6fc8ab510e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x32776ed4d96ed069a2d812773f0ad8ad9ef83cf8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x84f3ca9b7a1579ff74059bd0e8929424d3fa330e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5289a8dbf7029ee0b0498a84777ed3941d9acfec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb2bc284ab4c953b7f7a06d59c0ceb2de26405f22 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x508acf810857fefa86281499068ad5d19ebce325 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xccdfcd1aac447d5b29980f64b831c532a6a33726 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4fb87838a29b37598099ef5aa6b3fbeeef987c50 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x515e94dc736b9d8b7d28ecf1cece0aba3d75da97 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xfd6e5b7c30538dff2752058e425ad01a56b831cc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xcb99fe720124129520f7a09ca3cbef78d58ed934 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xd2f21358c1549be193537b2a4c5dc7f0228ae011 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x93094ed1c907e4bca7eb041cb659da94f7e1b58e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xd37e6ecb991d1a0e7610c89666817665713362a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x73234630bd159384c8d43f145407312d64614f43 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xad1ddf00c4ae50573e4dc98e6c5ee93baa04a0c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa765593c821f7df9ad81119509a37961e7ffa6c5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9b501a7ad3087d603ceb34424b7b2a6c348ad0b7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xafebb7cfa1a15fcac4121b609b456cbce3137c20 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0adaf134ae0c4583b3a38fc3168a83e33162651e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf9878a5dd55edc120fde01893ea713a4f032229c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x84e47c7f2fe86f6b5efbe14fee46b8bb871b2e05 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf3e5bec78654049990965f666b0612e116b94fb2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x33e59edd3214e97cb68450c6d3d6c167de072aba - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2ca76c7e466e560e0cb11a91269bb953e41254bc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xbb124e35ab9e85f8d59ba83500e559dc052b9368 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x180efc1349a69390ade25667487a826164c9c6e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x197d7010147df7b99e9025c724f13723b29313f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8dbee21e8586ee356130074aaa789c33159921ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x570febdf89c07f256c75686caca215289bb11cfc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe3d3551bb608e7665472180a20280630d9e938aa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4e34da137f0b317c633838458e0c923a5e088752 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x5281e311734869c64ca60ef047fd87759397efe6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xd88d5f9e6c10e6febc9296a454f6c2589b1e8fae - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb90fe7da36ac89448e6dfd7f2bb1e90a66659977 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xbd6313d0796984c578cae6bc5b5e23b27c5540c5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1f18cd7d1c7ba0dbe3d9abe0d3ec84ce1ad10066 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7da99753ff017f1b7afb2c8c0542718dc9f15f21 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x079e7a44f42e9cd2442c3b9536244be634e8f888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1c8dafd358d308b880f71edb5170b010b106ca60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xbd0f6f34baa3c1329448a69bab90111a20756f01 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x3420720e561f3082f1e514a4545f0f2e0c955a5d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xea3fb6e3313a2a90757e4ca3d6749efd0107b0b6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xf130f72f8190f662522774c3367e6e8814f5e219 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4a46c053bd5c10a959aea258228217b9d3405f3d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb83258bf5940c98abf54f26c5a02710bd6b83b2c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x6a209c5329f0a225fa1890d4177823c096016f34 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xdb24905b1b080f65dedb0ad978aad5c76363d3c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xddff2cdad11898b901a661e32e9fa010780263a0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x72dd8fe09b5b493012e5816068dfc6fb26a2a9e6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x54fc722a66abfb6500a36d8b7b2646129d0e836a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x53b612b32233c80ec439a64325a29766ce95be7f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe5edcbe72d1bc223097a1bed1fe6c0e404b4290c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb928c37b8bd9754d321dc3d3c6ef374d332fe761 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2d70cbabf4d8e61d5317b62cbe912935fd94e0fe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x953e2937f0515c43ca7995e80c84aedcbbb9385e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x84394d80830ae963b599ded7d9149b90059f182f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa1777e082fa1746eb78dd9c1fbb515419cf6e538 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x112466c8b6e5abe42c78c47eb1b9d40baa3f943c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9491d57c5687ab75726423b55ac2d87d1cda2c3f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x978799f1845c00c9a4d9fd2629b9ce18df66e488 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xdc55d1fd1c04e005051a40bd59c5f95623257bc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x34757893070b0fc5de37aaf2844255ff90f7f1e0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7faf167615419228f3f7d71d52d840dab154913c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa4d7b6a50dd4c55334ca6f175dbc6561f269d264 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0ed413cefde954d8e5c54d981d7d182b587e98e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x524375d0c6a04439128428f400b00eae81a2e9e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4b7a4530d56ff55a4dce089d917ede812e543307 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x84bb5b9bf1b6782c87cfa3e396f2f571c8e49646 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x723292eea7e1576ae482a5c317934054c0199e24 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9b42940e8184d866aac6595a91f8d8952a59d3b9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x37622453c614f625d288151101ffe48fd222ced1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4a94130b9e8eb0a0959c2c0f1ee9583213773fd9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x51514b3dc24afc1db95586242b99f0063bea17c5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc130254e9196d48bbd9f91240390a6e8203132e9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x60ac25da2ada3be14a2a8c04e45b072bed965966 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4e392a3883a84225260ff857318517eb50e5d128 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xca0aa06385a42242fe9523cd7015f6d01cd8f6b2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x3e448c17043ce1481bbe53c0fd19481bad8b98a6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x81060e6bf2a683f208b8799a33c7c09830cabed1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x463fe9f646b61ccfb43a022bf947075411cd71c7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x21b8065d10f73ee2e260e5b47d3344d3ced7596e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x517f9dd285e75b599234f7221227339478d0fcc8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa43fe16908251ee70ef74718545e4fe6c5ccec9f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0af81cd5d9c124b4859d65697a4cd10ee223746a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xca7c2771d248dcbe09eabe0ce57a62e18da178c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x09d1d767edf8fa23a64c51fa559e0688e526812f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7b73644935b8e68019ac6356c40661e1bc315860 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x180efc1349a69390ade25667487a826164c9c6e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9c4fe5ffd9a9fc5678cfbd93aa2d4fd684b67c4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa478c2975ab1ea89e8196811f51a7b7ade33eb11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xbb2b8038a1640196fbe3e38816f3e67cba72d940 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9ec9367b8c4dd45ec8e7b800b1f719251053ad60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc91ef786fbf6d62858262c82c63de45085dea659 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x197d7010147df7b99e9025c724f13723b29313f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x25647e01bd0967c1b9599fa3521939871d1d0888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2f0b1417aa42ebf0b4ca1154212847f6094d708d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x6ada49aeccf6e556bb7a35ef0119cc8ca795294a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2a6c340bcbb0a79d3deecd3bc5cbc2605ea9259f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xda2d09fbbf8ee4b5051a0e9b562c5fcb4b393b18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x48d20b3e529fb3dd7d91293f80638df582ab2daa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4028daac072e492d34a3afdbef0ba7e35d8b55c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc2eab7d33d3cb97692ecb231a5d0e4a649cb539d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc5be99a02c6857f9eac67bbce58df5572498f40c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe4b8583ccb95b25737c016ac88e539d0605949e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x8dbee21e8586ee356130074aaa789c33159921ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x43de4318b6eb91a7cf37975dbb574396a7b5b5c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x9ff68f61ca5eb0c6606dc517a9d44001e564bb66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa29fe6ef9592b5d408cca961d0fb9b1faf497d6d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1b1137dd16faa651e38a9dfb5d9ffff7767fdf62 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x470e8de2ebaef52014a47cb5e6af86884947f08c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x8fb8e9921922d2ffb529a95d28a0d06d275d7a59 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xd3d2e2692501a5c9ca623199d38826e513033a17 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x97e1fcb93ae7267dbafad23f7b9afaa08264cfd8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa5e9c917b4b821e4e0a5bbefce078ab6540d6b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2cc846fff0b08fb3bffad71f53a60b4b6e6d6482 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x959873fb4fc11825fba83c80c4c632db1e936e15 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa7480aafa8ad2af3ce24ac6853f960ae6ac7f0c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xc7e6b676bfc73ae40bcc4577f22aab1682c691c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x570febdf89c07f256c75686caca215289bb11cfc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x343fd171caf4f0287ae6b87d75a8964dc44516ab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xcaa004418eb42cdf00cb057b7c9e28f0ffd840a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe3d3551bb608e7665472180a20280630d9e938aa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb6b0c651c37ec4ca81c0a128420e02001a57fac2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4e34da137f0b317c633838458e0c923a5e088752 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xfe9e7931e55c514c33d489c88582fa36e84bd8e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x5281e311734869c64ca60ef047fd87759397efe6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x149148acc3b06b8cc73af3a10e84189243a35925 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x8ef79d6c328c25da633559c20c75f638a4863462 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xbf16ef186e715668aa29cef57e2fd7f9d48adfe6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x5645dcb64c059aa11212707fbf4e7f984440a8cf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x3ad4913fa896391c9822a81d8d869cc0d783bdd7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0f23d49bc92ec52ff591d091b3e16c937034496e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7a415b19932c0105c82fdb6b720bb01b0cc2cae3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x9b3423373e6e786c9ac367120533abe4ee398373 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4a25dbdf9629b1782c3e2c7de3bdce41f1c7f801 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xbe80225f09645f172b079394312220637c440a63 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x059615ebf32c946aaab3d44491f78e4f8e97e1d3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x435664008f38b0650fbc1c9fc971d0a3bc2f1e47 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4b62fa30fea125e43780dc425c2be5acb4ba743b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc3db44adc1fcdfd5671f555236eae49f4a8eea18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe5cf22ee4988d54141b77050967e1052bd9c7f7a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7f580f8a02b759c350e6b8340e7c2d4b8162b6a9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x48b0ab72c2591849e678e7d6f272b75ef9b863f7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x74d0ae8b8e1fca6039707564704a25ad2ee036b0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x5969efdde3cf5c0d9a88ae51e47d721096a97203 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe32efff8f8b5fdc53803405aa3f623f03f8a8767 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe8629b6a488f366d27dad801d1b5b445199e2ada - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x066b28f0c160935cf285f75ed600967bf8417035 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x146b020399769339509c98b7b353d19130c150ec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd28f71e383e93c570d3edfe82ebbceb35ec6c412 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xadab76dd2dca7ae080a796f0ce86170e482afb4a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0fb07e6d6e1f52c839608e1436d2ea810cf07257 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x95d2483d2a0fff034004f91c53d649623d993896 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x19c5505638383337d2972ce68b493ad78e315147 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc143161ed3ed8049bb63d8da42907c08a10e2269 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xc3286373599dd5af2a17a572ebb7561f05f88bec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xbb98b3d2b18aef63a3178023a920971cf5f29be4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x647fb01a63de9a551b39c7915693b25e6bcec502 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa90c1c009dc8292bd04ced30f9b53a5ff7a806a0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xfb765ff72a14735550f1d798a5efd1311f2ddee7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x3537f2a5f99f08f59eb1417073db1fadbebf0c74 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xde8ed0277ee0e84c25756a73ffa7374e4aeadf46 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd8f3a72d2b2220a5067abe8c38aea57dc2d69a5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x7ec18abf80e865c6799069df91073335935c4185 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x14b1911dd6b451c2771661ae8cd70637d726c356 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x9ae8084c21752971d867597c07f2673765d949a1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xcfaf75a3d292c3535ea3acdb16ed2ee58c2bb091 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x8055e6de251e414e8393b20adab096afb3cf8399 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xffec10fe1355c2d8df4f62affcdeffdb04f06569 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc16454420f100b2e771d8bc4c5b6200068129a34 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x046f405e4ae1d0e786eda4959adadbd417d13ad8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xeccb34691c06c1c9c31ceb2228b22cbd242b5879 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xe22a2dfaaaaec8a7b2b7acb4909eaaa5c5bd6e64 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xe2dda0911e227e73d9fd94745b851c8bc6504610 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x0f082a7870908f8cebbb2cd27a42a9225c19f898 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x69d667281778db0c3bc8177efea3a91ee95c3068 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x30d61bb28a6789f9f49d8c7fb198d63b6aba4b61 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x090f3fd9110621df127c3f9be5c6f58c02f2d5eb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd56f086e7b796b313d49f2bc926fac4bdd2a2b0b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x7eb847a214192aab8fa1b503f4d4c9ddd2a08db6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x81b3bc0ef974c16d71b8614adb8c22ccc045da01 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc9b44ca4159dbaf5722a3dc8618e9d4b5f39d5b2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xbeef35a63fc62a3334630d9d3b4db27093d95317 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x3d5d143381916280ff91407febeb52f2b60f33cf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x68c9325cc268df8b9ed4a06429587f28471b5f84 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa00cc1fb7ac185222294777c6b23a13c013f07ce - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x77021e63bcbd3c5296b0cdd8a3c3770fb0ea8fa2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xcc28456d4ff980cee3457ca809a257e52cd9cdb0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xec0b7e8e44c9d60efd67a89dba1d4a6e02a7a4a0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x0c8fed5dd65542ca5f0add1acab14c2e470c9110 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd56da2b74ba826f19015e6b7dd9dae1903e85da1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x5482c2b11951bbb92b87858242e17abde802b398 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd95bae63641d822dc591bd4aca7a64e53eac76f9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x06959273e9a65433de71f5a452d529544e07ddd0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x24bf2ee2e09477082d1ddf2f0603baa460b3f5f3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x56d8f846415e08c5e663d89505e79f522d33f947 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x548e923281f372d28a40287d3a2d30dce482fc66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x9d744d3d905897608d24c1b8c1c7db0d30c36cd4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xab46d39cb398fb3649ecba781180016fef75f50b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x25048028ad87484b7fce99bc4e22dcb6c3307470 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xdb2177fee5b0ebdc7b8038cb70f3964bb6d14143 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x42d749f736051d8933b118324cded52d1f92bec1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb1a1b707b143b911c36e1a0f4f901c5017791aca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x3319a81a316abd4c086f7048904e31ff86648b38 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4a978a2d4fb7393063babfb0cee741b8bcd4dd4b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xea403e36fb592fdfdc342c38e94284ddbb0d2105 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe3fb01794d6912f0773171e32e723471ee8df061 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x916d7f23ccbb1d10118dcfc6ad5a10b6446ff73e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x6cde5f5a192fbf3fd84df983aa6dc30dbd9f8fac - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xd80d28850bebe6208433c298334392bc940b4fc7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7f7c4335ccac291ddedcef4429a626c442b627ed - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x628cb3a5a206956423d158009612813b64b19dab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x116361f4f45e310347b43cd098fdfa459760ea7f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x5dc631ad6c26bea1a59fbf2c2680cf3df43d249f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1a810e0b6c2dd5629afa2f0c898b9512c6f78846 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xac1cb6d3d419da9ead0b53e62d6fb4bb53473523 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0115d04a88990889471a88e85817aac9e961c07b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xd3409b7f3f54bb097433d0f4cd31c48ac33e569b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x493bfc1adb2e60805693197f23132350ffd2a04e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xcf4f103759770c21f945413781ca787620316988 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xb135ebde27d366b0d62e579bae4118cb991b820e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xecbc2f008c20729b9239317408367377c5473812 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x96e0c440d3377c2dfe4f2a82add0b045e46cbe64 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x6f5304c22ac77e228e8af4732ac6677c46e09030 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xcb037f27eb3952222810966e28e0ceb650c65cd9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xddd23787a6b80a794d952f5fb036d0b31a8e6aff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa86aca6d7c393c06dcdc30473ea3d1b05c358dff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x1ffec7119e315b15852557f654ae0052f76e6ae1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0f027d40c80d8f70f77d3884776531f80b21d20e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x69c66beafb06674db41b22cfc50c34a93b8d82a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xeedff72a683058f8ff531e8c98575f920430fdc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x811cfb75567a252bea23474e2ccd1286927bfe0a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x2caccf71bdf8fff97c06a46eca29b611b1a74b5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xf07a84f0732dfe8eea0d3961bcd8f62c761ff508 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x8c1c499b1796d7f3c2521ac37186b52de024e58c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7baece5d47f1bc5e1953fbe0e9931d54dab6d810 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x83abecf7204d5afc1bea5df734f085f2535a9976 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xb2eb5849e2606f99fc492e9add0103c667f806d3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x53c6ca2597711ca7a73b6921faf4031eedf71339 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xd35937ecd47b04a1474f8569f457fc5ac395921a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x6b75f2189f0e11c52e814e09e280eb1a9a8a094a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb372b5abdb7c2ab8ad9e614be9835a42d0009153 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xf369277650ad6654f25412ea8bfbd5942733babc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x4898cf312fbff8814cab80a8d7f6ee5ad0dc73fb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x5e78afc6c804d4382bede3a0712d210e657e9b4f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x86b211ca7915a0c8d4659dd98242d9e801d88ab4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xb637f7c82fd774c280e23cebc725e7cd807c66d0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd249c43faabc58d6dd4b0a4de598b5a956c5d8d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x1fbae785ce68b79f7ed4f7b27c3af3ef0e0bc3d4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x3c1376fb8487da57d4ffb263d9d01b578c7b586b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x7b24bed19856f4bb1d4c0421cfb328026cd936bd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x7cf887a863d81e6a483ee947dee05cb51914923c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x588c8cf031809486f015908864ee8699b44017e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x3987d38a4ff8520a8ef6bcc6f98d6da8bcd69b89 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xde67d05242b18af00b28678db34feec883cc9cd6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4a5a8b0108f446df7c1c8a459fcfb54e844b7343 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xf6ba006abf768ab2d1b5bba2d22d9f13eb1269d4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xaf996125e98b5804c00ffdb4f7ff386307c99a00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x7924a818013f39cf800f5589ff1f1f0def54f31f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xc1738d90e2e26c35784a0d3e3d8a9f795074bca4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xda908c0bf14ad0b61ea5ebe671ac59b2ce091cbf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x254aa3a898071d6a2da0db11da73b02b4646078f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x41824081f2e7beb83048bf52465ddd7c8e471da2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa0c2ce1723b3939f47ad01a293292f2f75dc629d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xc42442f6402b68626e791a447d87b35cb1c6236e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x84537db6f6aaa2afdb71f325d14b9f5f7825bef1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x13933689ed2c6c66e83aed64336df14896efb7e2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x039df62583ddc1c5fda75db152b87113d863b6d6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x92c2fc5f306405eab0ff0958f6d85d7f8892cf4d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xcbe856765eeec3fdc505ddebf9dc612da995e593 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc39e83fe4e412a885c0577c08eb53bdb6548004a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xdbac78be00503d10ae0074e5e5873a61fc56647c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xc1cd3d0913f4633b43fcddbcd7342bc9b71c676f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x6c4c7f46d9d4ef6bc5c9e155f011ad19fc4ef321 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xb2c86ff752f18499b70e8f642b3421405d50d6e9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x16588709ca8f7b84829b43cc1c5cb7e84a321b16 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xd0a4c8a1a14530c7c9efdad0ba37e8cf4204d230 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xf92f2e3fca01491baba0975264362cc38b1cab7b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x3e6e23198679419cd73bb6376518dcc5168c8260 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x531b6a4b3f962208ea8ed5268c642c84bb29be0b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x553e9c493678d8606d6a5ba284643db2110df823 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe3170d65018882a336743a9c396c52ea4b9c5563 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x1385fc1fe0418ea0b4fcf7adc61fc7535ab7f80d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x5cd0ad98ba6288ed7819246a1ebc0386c32c314b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x0ad1e922e764df5ab6d636f5d21ecc2e41e827f0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x6b3a3d6ed64faf933a7a4b1bd44b2efba47614ac - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x4ce4a1a593ea9f2e6b2c05016a00a2d300c9ffd8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0843e0f56b9e7fdc4fb95fabba22a01ef4088f41 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x8323d063b1d12acce4742f1e3ed9bc46d71f4222 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe30e4dfdbb10949c27501922f845e20cfa579f09 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x7e02ae3f794ebade542c92973eb1c46d7e2e935d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xfa22d298e3b0bc1752e5ef2849cec1149d596674 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x8066ee17156e4184d69277e26fa8cbca3a845edf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x418de8e0ab58abfe916a47821a055c59b9502deb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xfb9caae5a5c0ab91f68542124c05d1efbb97d151 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb68606a75b117906e06caa0755896ad2b3dd0272 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x6e33c0f5e16b45114679eac217e0c0138cefcd2e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xd64fb39a5681908ad488b487d65f5d8479cb235c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x0217fc17c642d29b890bcf888e21be2378493e01 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x099d23a43da5a8a9282266dbefeaaef958150300 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xd92e0767473d1e3ff11ac036f2b1db90ad0ae55f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/base/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x40c547e7fd88f60d94788953b83d9342d8d133c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x397433498c7befde4b4049b98a7ff081a2c17387 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf9be03505869d719ba194757943575ed2af001f2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x18c40bb9281a07627ff25cea45b7511f68fd0076 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x270d89e983d9821a418bf193684736414fab78c5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xb125aa15ad943d96e813e4a06d0c34716f897e26 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x813c0decbb1097fff46d0ed6a39fb5f6a83043f4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x9a7ac628ba9f330341486380af729c8975388959 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xf2c9339945bff71dd0bffd3c142164112cd05dc6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x12a4619c0bd9710732fbc458e9baa73df6c3d35f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x96530dac7817f186390b64ba63d13becd079b28d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x18fc1e95adb68b556212ebbad777f3fbb644db98 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xabbeb324b090550ca6d15ec71019915813f54f90 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x86d708404d0db1d97843e66d4ed6b86d11be705b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xbfbba3de6a260c8374f8299c38898312c2d6e9a6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe945683b3462d2603a18bdfbb19261c6a4f03ad1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xa1bf0e900fb272089c9fd299ea14bfccb1d1c2c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0xe46935ae80e05cdebd4a4008b6ccaa36d2845370 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/celo/0x3041cbd36888becc7bbcbc0045e3b1f144466f5f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xd31d41dffa3589bb0c0183e46a1eed983a5e5978 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x391e8501b626c623d39474afca6f9e46c2686649 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0xd0fc8ba7e267f2bc56044a7715a489d851dc6d78 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x4fd47e5102dfbf95541f64ed6fe13d4ed26d2546 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe9033c0011f35547fa90d3f8a6ad4b666a590759 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x0c3561d3b72e17378d99684414aa8669daeb8bd0 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x14653ce9f406ba7f35a7ffa43c81fa7ecd99c788 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x3204e9734a56a4d7c6f4f5822e14182d9d1a43c4 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x43faefd4c0c25e969ac211cd97a4a51e52c729b7 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0xa652ab3be697c7a01fbdce4d73f8e8acd990251c - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x29962083891241aad61ad97bae46d032c9c0c55c - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x26bf3601b77be9c31b13b22ebca02914db9c7468 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x0d2edd335982f56662d772b93d86901eb9bd2ff9 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0xbaed273edd493930711fe88690ebd1f30f7f55ab - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x16033643947bf4d8a1ae37b055edf57cb183106a - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/celo/0xf59abf32c1e8c5d2c6e3faa2131533bbcd466194 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/celo/0x0312187403bf72b8d2d80729894d6ac3300bd63f - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x416fdbc4fb8d4d1f48d0d3778c59dfa5352e9b15 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/pools/ethereum/0x90908e414d3525e33733d320798b5681508255ea - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x5918aca9ae924e6eaaa3d293bb92bdec9ab79338 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x8270e64d22cf13e92c641c4006408c7d7e3ff341 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x16503510c58da73486950b72a12ead3d1d8355dd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/arbitrum/0x90908e414d3525e33733d320798b5681508255ea - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x7505159f644ddc5eae21c119e328d0d5bee574b0 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/optimism/0xe870bfe4aacb6e234b645e535d26c53790d50e78 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x2e2d190ad4e0d7be9569baebd4d33298379b0502 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/optimism/0x90908e414d3525e33733d320798b5681508255ea - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb834093d7e46f7644be45e77281394d31003e866 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/polygon/0xb5a1fd804342cfb679bd8ada75718bc3ec43097e - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/polygon/0x90908e414d3525e33733d320798b5681508255ea - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x9e71e2b14d7e6d30811628ab0965f28e4e2edbce - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0xa011da4a0c9261ecf4694bf73a74d113aa261133 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x7ab922c1bfdf7df977c7531c5782074d866f3adc - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0xe2d2050430e341a8f3988e2726e44d9370f8cd3a - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0xed66ba3ea44425805a085b1ca80d00467b055b38 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x40dade19adc198125ec237a2c48b3408568b2f81 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x166bc40da621d3cb978e24334f844b84ddef25f8 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x76bf0abd20f1e0155ce40a62615a90a709a6c3d8 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/base/0x90908e414d3525e33733d320798b5681508255ea - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x6948d6c8532c6b0006cb67c6fb9c399792c8ac91 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/bnb/0x90908e414d3525e33733d320798b5681508255ea - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/celo/0x4e40cf4a7d8724e5adc2b791bbf9451d1e260b93 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/pools/celo/0x90908e414d3525e33733d320798b5681508255ea - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z + 0.8 + + + https://app.uniswap.org/explore/pools/ethereum/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/ethereum/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/arbitrum/0xd6b4cce96ddf8aab2e5750983af9a901f17fbc36 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/arbitrum/0x4cef551255ec96d89fec975446301b5c4e164c59 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/arbitrum/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/arbitrum/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/optimism/0xdd0c6bae8ad5998c358b823df15a2a4181da1b80 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/optimism/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/optimism/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/polygon/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/polygon/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/base/0x5e6ff2fa4ca244b6b33c7286d368120822eacc11 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/base/0x98efd62b4bfbde6393b18b063c506ce5a77f4810 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/base/0x3c5096df639262db0a6cd0172f08709d4161094b + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/base/0xae31f0e673fc5f33cfc0e9abb426d8051404a7c5 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/base/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/base/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/bnb/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/bnb/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/celo/0xd10456ce05b9af05c8eede0f93ea8aa80a0daa2f + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/celo/0x065c22a16f6531706681fabbc8df135fe6eb1c2e + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/celo/0x8ab8d851c6b31d8a4d42fd7d3e47b20861b025f2 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/celo/0xc0067d751fb1172dbab1fa003efe214ee8f419b6 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/pools/celo/0xc3d7aa944105d3fafe07fc1822102449c916a8d0 + 2024-06-28T19:37:25.115Z 0.8 \ No newline at end of file diff --git a/apps/web/public/tokens-sitemap.xml b/apps/web/public/tokens-sitemap.xml index 39ede465316..3e824bf69c4 100644 --- a/apps/web/public/tokens-sitemap.xml +++ b/apps/web/public/tokens-sitemap.xml @@ -2,4677 +2,4882 @@ https://app.uniswap.org/explore/tokens/ethereum/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xdac17f958d2ee523a2206206994597c13d831ec7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2260fac5e5542a773aa44fbcfedf7c193bc2c599 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6982508145454ce325ddbe47a25d4ec3d2311933 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6b175474e89094c44da98b954eedeac495271d0f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6123b0049f904d730db3c36a31167d9d4121fa6b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xcf0c122c6b73ff809c693db761e7baebe62b6a2e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xfaba6f8e4a5e8ab82f62fe7c39859fa577269be3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x58cb30368ceb2d194740b144eab4c2da8a917dcb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4c9edd5852cd905f086c759e8383e09bff1e68b3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xaaee1a9723aadb7afa2810263653a34ba2c21c7a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x514910771af9ca656af840dff83e8264ecf986ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5b7533812759b45c2b44c19e320ba2cd2681b542 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xae78736cd615f374d3085123a210448e74fc6393 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb9f599ce614feb2e1bbe58f180f370d05b39344e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd5f7838f5c461feff7fe49ea5ebaf7728bb0adfa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd31a59c85ae9d8edefec411d448f90841571b89c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6a7eff1e2c355ad6eb91bebb5ded49257f3fed98 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x576e2bed8f7b46d34016198911cdf9886f78bea7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1258d60b224c0c5cd888d37bbf31aa5fcfb7e870 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x62d0a8458ed7719fdaf978fe5929c6d342b0bfce - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x77e06c9eccf2e797fd462a92b6d7642ef85b0a44 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x24fcfc492c1393274b6bcd568ac9e225bec93584 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x27702a26126e0b3702af63ee09ac4d1a084ef628 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd46ba6d942050d489dbd938a2c909a5d5039a161 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbe9895146f7af43049ca1c1ae358b0541ea49704 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x72f713d11480dcf08b37e1898670e736688d218d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0001a500a6b18995b03f44bb040a5ffc28e45cb0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9e9fbde7c7a83c43913bddc8779158f1368f0413 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5f98805a4e8be255a32880fdec7f6728c6568ba0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2b591e99afe9f32eaa6214f7b7629768c40eeb39 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1ae7e1d0ce06364ced9ad58225a1705b3e5db92b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x046eee2cc3188071c02bfc1745a6b17c656e3f3d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x84018071282d4b2996272659d9c01cb08dd7327f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x12970e6868f88f6557b76120662c1b3e50a646bf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xaea46a60368a7bd060eec7df8cba43b7ef41ad85 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6de037ef9ad2725eb40118bb1702ebb27e4aeb24 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc01154b4ccb518232d6bbfc9b9e6c5068b766f82 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5a98fcbea516cf06857215779fd812ca3bef1b32 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x102c776ddb30c754ded4fdcc77a19230a60d4e4f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x72e4f9f808c49a2a61de9c5896298920dc4eeea9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x467719ad09025fcc6cf6f8311755809d45a5e5f3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf19308f923582a6f7c465e5ce7a9dc1bec6665b1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x710287d1d39dcf62094a83ebb3e736e79400068a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf951e335afb289353dc249e82926178eac7ded78 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf017d3690346eb8234b85f74cee5e15821fee1f4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8c282c35b5e1088bb208991c151182a782637699 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xeaa63125dd63f10874f99cdbbb18410e7fc79dd3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xde342a3e269056fc3305f9e315f4c40d917ba521 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2dff88a56767223a5529ea5960da7a3f5f766406 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x626e8036deb333b408be468f951bdb42433cbf18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xdd66781d0e9a08d4fbb5ec7bac80b691be27f21d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb23d80f5fefcddaa212212f028021b41ded428cf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbaac2b4491727d78d2b78815144570b9f2fe8899 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf8ebf4849f1fa4faf0dff2106a173d3a6cb2eb3a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb90b2a35c65dbc466b04240097ca756ad2005295 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1614f18fc94f47967a3fbe5ffcd46d4e7da3d787 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf1df7305e4bab3885cab5b1e4dfc338452a67891 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x91fbb2503ac69702061f1ac6885759fc853e6eae - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa9e8acf069c58aec8825542845fd754e41a9489a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2c95d751da37a5c1d9c5a7fd465c1d50f3d96160 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe453c3409f8ad2b1fe1ed08e189634d359705a5b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x89d584a1edb3a70b3b07963f9a3ea5399e38b136 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4507cef57c46789ef8d1a19ea45f4216bae2b528 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd1d2eb1b1e90b638588728b4130137d262c87cae - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe92344b4edf545f3209094b192e46600a19e7c2d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8a0a9b663693a22235b896f70a229c4a22597623 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1bbe973bef3a977fc51cbed703e8ffdefe001fed - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa41d2f8ee4f47d3b860a149765a7df8c3287b7f0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x761d38e5ddf6ccf6cf7c55759d5210750b5d60f3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc18360217d8f7ab5e7c516566761ea12ce7f9d72 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe28b3b32b6c345a34ff64674606124dd5aceca30 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x168e209d7b2f58f1f24b8ae7b7d35e662bbf11cc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb131f4a55907b10d1f0a50d8ab8fa09ec342cd74 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3472a5a71965499acd81997a54bba8d852c6e53d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7dd9c5cba05e151c895fde1cf355c9a1d5da6429 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x19efa7d0fc88ffe461d1091f8cbe56dc2708a84f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x14fee680690900ba0cccfc76ad70fd1b95d10e16 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3c3a81e81dc49a522a592e7622a7e711c06bf354 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa1290d69c65a6fe4df752f95823fae25cb99e5a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x92f419fb7a750aed295b0ddf536276bf5a40124f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2c06ba9e7f0daccbc1f6a33ea67e85bb68fbee3a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3d658390460295fb963f54dc0899cfb1c30776df - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8e870d67f660d95d5be530380d0ec0bd388289e1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x853d955acef822db058eb8505911ed77f175b99e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1294f4183763743c7c9519bec51773fb3acd78fd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4e15361fd6b4bb609fa63c81a2be19d873717870 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x695d38eb4e57e0f137e36df7c1f0f2635981246b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x40a7df3df8b56147b781353d379cb960120211d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xaaef88cea01475125522e117bfe45cf32044e238 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x163f8c2467924be0ae7b5347228cabf260318753 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x30672ae2680c319ec1028b69670a4a786baa0f35 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc944e90c64b2c07662a292be6244bdf05cda44a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x15e6e0d4ebeac120f9a97e71faa6a0235b85ed12 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7d225c4cc612e61d26523b099b0718d03152edef - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x82af49447d8a07e3bd95bd0d56f35241523fbab1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xaf88d065e77c8cc2239327c5edb3a432268e5831 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xff970a61a04b1ca14834a43f5de4533ebddb5cc8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x912ce59144191c1204e64559fe8253a0e49e6548 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x5979d7b546e38e414f7e9822514be443a4800529 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x35751007a407ca6feffe80b3cb397736d2cf4dbe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xda10009cbd5d07dd0cecc66161fc93d7c9000da1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xeb466342c4d449bc9f53a865d5cb90586f405215 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xfc5a1a6eb076a2c7ad06ed22c90d7e710e35ad0a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x0c880f6761f1af8d9aa9c466984b80dab9a8c9e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf97f4df75117a78c1a5a0dbb814af92458539fb4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9623063377ad1b27544c965ccd7342f7ea7e88c7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x539bde0d7dbd336b79148aa742883198bbf60342 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3082cc23568ea640225c2467653db90e9250aaa0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x18c11fd286c5ec11c3b683caa813b77f5163a122 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x289ba1701c2f088cf0faf8b3705246331cb8a839 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4cb9a7ae498cedcbb5eae9f25736ae7d428c9d66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x00cbcf7b3d37844e44b888bc747bdd75fcf4e555 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xfa7f8980b0f1e64a2062791cc3b0871572f1f7f0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd79bb960dc8a206806c3a428b31bca49934d18d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3096e7bfd0878cc65be71f8899bc4cfb57187ba3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x13ad51ed4f1b7e9dc168d8a00cb3f4ddd85efa60 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4e352cf164e64adcbad318c3a1e222e9eba4ce42 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x11cdb42b0eb46d95f990bedd4695a6e3fa034978 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xba5ddd1f9d7f570dc94a51479a000e3bce967196 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xc8ccbd97b96834b976c995a67bf46e5754e2c48e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd07d35368e04a839dee335e213302b21ef14bb4a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x323665443cef804a3b5206103304bd4872ea4253 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x83d6c8c06ac276465e4c92e7ac8c23740f435140 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x87aaffdf26c6885f6010219208d5b161ec7609c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1b8d516e2146d7a32aca0fcbf9482db85fd42c3a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xafccb724e3aec1657fc9514e3e53a0e71e80622d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4425742f1ec8d98779690b5a3a6276db85ddc01a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xec70dcb4a1efa46b8f2d97c310c9c4790ba5ffa8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3419875b4d3bca7f3fdda2db7a476a79fd31b4fe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3b60ff35d3f7f62d636b067dd0dc0dfdad670e4e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x58b9cb810a68a7f3e1e4f8cb45d1b9b3c79705e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xfa5ed56a203466cbbc2430a43c66b9d8723528e7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x95146881b86b3ee99e63705ec87afe29fcc044d9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x088cd8f5ef3652623c22d48b1605dcfe860cd704 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xbfd5206962267c7b4b4a8b3d76ac2e1b2a5c4d5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6daf586b7370b14163171544fca24abcc0862ac5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9d2f299715d94d8a7e6f5eaa8e654e8c74a988a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x580e933d90091b9ce380740e3a4a39c67eb85b4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x655a6beebf2361a19549a99486ff65f709bd2646 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9e64d3b9e8ec387a9a58ced80b71ed815f8d82b5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2297aebd383787a160dd0d9f71508148769342e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6694340fc020c5e6b96567843da2df01b2ce1eb6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x772598e9e62155d7fdfe65fdf01eb5a53a8465be - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x431402e8b9de9aa016c743880e04e517074d8cec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd74f5255d557944cf7dd0e45ff521520002d5748 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6fd58f5a2f3468e35feb098b5f59f04157002407 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x561877b6b3dd7651313794e5f2894b2f18be0766 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf9ca0ec182a94f6231df9b14bd147ef7fb9fa17c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd77b108d4f6cefaa0cae9506a934e825becca46e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd56734d7f9979dd94fae3d67c7e928234e71cd4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf1264873436a0771e440e2b28072fafcc5eebd01 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x5575552988a3a80504bbaeb1311674fcfd40ad4b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x0341c0c0ec423328621788d4854119b97f44e391 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x764bfc309090e7f93edce53e5befa374cdcb7b8e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xaaa6c1e32c55a7bfa8066a6fae9b42650f262418 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9e20461bc2c4c980f62f1b279d71734207a6a356 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x7fb7ede54259cb3d4e1eaf230c7e2b1ffc951e9a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3a18dcc9745edcd1ef33ecb93b0b6eba5671e7ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x000000000026839b3f4181f2cf69336af6153b99 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x8b0e6f19ee57089f7649a455d89d7bc6314d04e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x31c91d8fb96bff40955dd2dbc909b36e8b104dde - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x25d887ce7a35172c62febfd67a1856f20faebb00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd4d42f0b6def4ce0383636770ef773390d85c61a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf8388c2b6edf00e2e27eef5200b1befb24ce141d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x619c82392cb6e41778b7d088860fea8447941f4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x94025780a1ab58868d9b2dbbb775f44b32e8e6e5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xad4b9c1fbf4923061814dd9d5732eb703faa53d4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd7a892f28dedc74e6b7b33f93be08abfc394a360 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3269a3c00ab86c753856fd135d97b87facb0d848 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4568ca00299819998501914690d6010ae48a59ba - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x21e60ee73f17ac0a411ae5d690f908c3ed66fe12 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xd3188e0df68559c0b63361f6160c57ad88b239d8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2b41806cbf1ffb3d9e31a9ece6b738bf9d6f645f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf19547f9ed24aa66b03c3a552d181ae334fbb8db - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x35e6a59f786d9266c7961ea28c7b768b33959cbb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x59a729658e9245b0cf1f8cb9fb37945d2b06ea27 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb56c29413af8778977093b9b4947efeea7136c36 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x43ab8f7d2a8dd4102ccea6b438f6d747b1b9f034 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1d987200df3b744cfa9c14f713f5334cb4bc4d5d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3404149e9ee6f17fb41db1ce593ee48fbdcd9506 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x080f6aed32fc474dd5717105dba5ea57268f46eb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb5a628803ee72d82098d4bcaf29a42e63531b441 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1622bf67e6e5747b81866fe0b85178a93c7f86e3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x7dd747d63b094971e6638313a6a2685e80c7fb2e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xa2f9ecf83a48b86265ff5fd36cdbaaa1f349916c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x17a8541b82bf67e10b0874284b4ae66858cb1fd5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xbcd4d5ac29e06e4973a1ddcd782cd035d04bc0b7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x42069d11a2cc72388a2e06210921e839cfbd3280 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xbbea044f9e7c0520195e49ad1e561572e7e1b948 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe85b662fe97e8562f4099d8a1d5a92d4b453bf30 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x3d9907f9a368ad0a51be60f7da3b97cf940982d8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4e51ac49bc5e2d87e0ef713e9e5ab2d71ef4f336 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x4200000000000000000000000000000000000006 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x7f5c764cbc14f9669b88837ca1490cca17c31607 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x4200000000000000000000000000000000000042 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x0b2c639c533813f4aa9d7837caf62653d097ff85 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x1f32b1c2345538c0c6f582fcb022739c4a194ebb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x68f180fcce6836688e9084f035309e29bf0a2095 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x94b008aa00579c1307b0ef2c499ad98a8ce58e58 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xda10009cbd5d07dd0cecc66161fc93d7c9000da1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xdc6ff44d5d932cbd77b52e5612ba0529dc6226f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x8c6f28f2f1a3c87f0f938b96d27520d9751ec8d9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x8700daec35af8ff88c16bdf0418774cb3d7599b4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x920cf626a271321c151d027030d5d08af699456b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x6c84a8f1c29108f47a79964b5fe888d4f4d0de40 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9e1028f5f1d5ede59748ffcee5532509976840e0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xeb466342c4d449bc9f53a865d5cb90586f405215 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x350a791bfc2c21f9ed5d10980dad2e2638ffa7f6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x17aabf6838a6303fc6e9c5a227dc1eb6d95c829a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xf467c7d5a4a9c4687ffc7986ac6ad5a4c81e1404 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x76fb31fb4af56892a25e32cfc43de717950c9278 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xc5b001dc33727f8f26880b184090d3e252470d45 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9560e827af36c94d2ac33a39bce1fe78631088db - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9bcef72be871e61ed4fbbc7630889bee758eb81d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x50c5725949a6f0c72e6c4a641f24049a917db0cb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xf98dcd95217e15e05d8638da4c91125e59590b07 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x4b03afc91295ed778320c2824bad5eb5a1d852dd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xc40f949f8a4e094d1b49a23ea9241d289b7b2819 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x323665443cef804a3b5206103304bd4872ea4253 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x50bce64397c75488465253c0a034b8097fea6578 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x296f55f8fb28e498b858d0bcda06d955b2cb3f97 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x2598c30330d5771ae9f983979209486ae26de875 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x0994206dfe8de6ec6920ff4d779b0d950605fb53 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xc3248a1bd9d72fa3da6e6ba701e58cbf818354eb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x6fd9d7ad17242c41f7131d257212c54a0e816691 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x14778860e937f509e651192a90589de711fb88a9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xdfa46478f9e5ea86d57387849598dbfb2e964b02 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9b88d293b7a791e40d36a39765ffd5a1b9b5c349 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x3eb398fec5f7327c6b15099a9681d9568ded2e82 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x217d47011b23bb961eb6d93ca9945b7501a5bb11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xbfd5206962267c7b4b4a8b3d76ac2e1b2a5c4d5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x1cef2d62af4cd26673c7416957cc4ec619a696a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9fd22a17b4a96da3f83797d122172c450381fb88 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xaddb6a0412de1ba0f936dcaeb8aaa24578dcf3b2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2791bca1f2de4661ed88a30c99a7a9449aa84174 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7ceb23fd6bc0add59e62ac25578270cff1b9f619 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3c499c542cef5e3811e1192ce70d8cc03d5c3359 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc2132d05d31c914a87c6611c10748aeb04b58e8f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x53e0bca35ec356bd5dddfebbd1fc0fd03fabad39 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x61299774020da444af134c82fa83e3810b309991 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xd6df932a45c0f255f85145f286ea0b292b21c90b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2ad2934d5bfb7912304754479dd1f096d5c807da - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc3c7d422809852031b44ab29eec9f1eff2a58756 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8f3cf7ad23cd3cadbd9735aff958023239c6a063 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x750e4c4984a9e0f12978ea6742bc1c5d248f40ed - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x111111517e4929d3dcbdfa7cce55d30d4b6bc4d6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xd0258a3fd00f38aa8090dfee343f10a9d4d30d3f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x430ef9263e76dae63c84292c3409d61c598e9682 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xb33eaad8d922b1083446dc23f610c2567fb5180f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xdc3326e71d45186f113a2f448984ca0e8d201995 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x311434160d7537be358930def317afb606c0d737 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0b3f868e0be5597d5db7feb59e1cadbb0fdda50a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe3f2b1b2229c0333ad17d03f179b87500e7c5e01 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xac0f66379a6d7801d7726d5a943356a172549adb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf88332547c680f755481bf489d890426248bb275 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe5417af564e4bfda1c483642db72007871397896 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe261d618a959afffd53168cd07d12e37b26761db - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe0b52e49357fd4daf2c15e02058dce6bc0057db4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xbbba073c31bf03b8acf7c28ef0738decf3695683 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe238ecb42c424e877652ad82d8a939183a04c35f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3b56a704c01d650147ade2b8cee594066b3f9421 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x5fe2b58c013d7601147dcdd68c143a77499f5531 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x172370d5cd63279efa6d502dab29171933a610af - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x53df32548214f51821cf1fe4368109ac5ddea1ff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xff76c0b48363a7c7307868a81548d340049b0023 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x6f8a06447ff6fcf75d803135a7de15ce88c1d4ec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x50b728d8d964fd00c2d0aad81718b71311fef68a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3a58a54c066fdc0f2d55fc9c89f0415c92ebf3c4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x03b54a6e9a984069379fae1a4fc4dbae93b3bccd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xd93f7e271cb87c23aaa73edc008a79646d1f9912 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x200c234721b5e549c3693ccc93cf191f90dc2af9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x11cd37bb86f65419713f30673a480ea33c826872 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8a16d4bf8a0a716017e8d2262c4ac32927797a2f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa1c57f48f0deb89f569dfbe6e2b7f46d33606fd4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x190eb8a183d22a4bdf278c6791b152228857c033 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2f6f07cdcf3588944bf4c42ac74ff24bf56e7590 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x235737dbb56e8517391473f7c964db31fa6ef280 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0b220b82f3ea3b7f6d9a1d8ab58930c064a2b5bf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8bff1bd27e2789fe390acabc379c380a83b68e84 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xb58458c52b6511dc723d7d6f3be8c36d7383b4a8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x323665443cef804a3b5206103304bd4872ea4253 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2760e46d9bb43dafcbecaad1f64b93207f9f0ed7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x18ec0a6e18e5bc3784fdd3a3634b31245ab704f6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x431d5dff03120afa4bdf332c61a6e1766ef37bdb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x6f7c932e7684666c9fd1d44527765433e01ff61d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xeee3371b89fc43ea970e908536fcddd975135d8a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe5b49820e5a1063f6f4ddf851327b5e8b2301048 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xaa3717090cddc9b227e49d0d84a28ac0a996e6ff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x62a872d9977db171d9e213a5dc2b782e72ca0033 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x381caf412b45dac0f62fbeec89de306d3eabe384 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe0bceef36f3a6efdd5eebfacd591423f8549b9d5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x23d29d30e35c5e8d321e1dc9a8a61bfd846d4c5c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x282d8efce846a88b159800bd4130ad77443fa1a1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x74dd45dd579cad749f9381d6227e7e02277c944b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x714db550b574b3e927af3d93e26127d15721d4c2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xfa68fb4628dff1028cfec22b4162fccd0d45efb6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe631dabef60c37a37d70d3b4f812871df663226f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xdb725f82818de83e99f1dac22a9b5b51d3d04dd4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3c59798620e5fec0ae6df1a19c6454094572ab92 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0d0b8488222f7f83b23e365320a4021b12ead608 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa380c0b01ad15c8cf6b46890bddab5f0868e87f3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8a953cfe442c5e8855cc6c61b1293fa648bae472 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x45c32fa6df82ead1e2ef74d17b76547eddfaff89 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x11cd72f7a4b699c67f225ca8abb20bc9f8db90c7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0c9c7712c83b3c70e7c5e11100d33d9401bdf9dd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x77a6f2e9a9e44fd5d5c3f9be9e52831fc1c3c0a0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xbfc70507384047aa74c29cdc8c5cb88d0f7213ac - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xfcb54da3f4193435184f3f647467e12b50754575 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9a6a40cdf21a0af417f1b815223fd92c85636c58 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe111178a87a3bff0c8d18decba5798827539ae99 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x82617aa52dddf5ed9bb7b370ed777b3182a30fd1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x2ab0e9e4ee70fff1fb9d67031e44f6410170d00e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa486c6bc102f409180ccb8a94ba045d39f8fc7cb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc4a206a306f0db88f98a3591419bc14832536862 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf0059cc2b3e980065a906940fbce5f9db7ae40a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x16eccfdbb4ee1a85a33f3a9b21175cd7ae753db4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x553d3d295e0f695b9228246232edf400ed3560b5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x14af1f2f02dccb1e43402339099a05a5e363b83c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7bdf330f423ea880ff95fc41a280fd5ecfd3d09f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8505b9d2254a7ae468c0e9dd10ccea3a837aef5c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe2aa7db6da1dae97c5f5c6914d285fbfcc32a128 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xb7b31a6bc18e48888545ce79e83e06003be70930 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x1631244689ec1fecbdd22fb5916e920dfc9b8d30 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf6372cdb9c1d3674e83842e3800f2a62ac9f3c66 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x692ac1e363ae34b6b489148152b12e2785a3d8d6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0266f4f08d82372cf0fcbccc0ff74309089c74d1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7fbc10850cae055b27039af31bd258430e714c62 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa3fa99a148fa48d14ed51d610c367c61876997f1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9dbfc1cbf7a1e711503a29b4b5f9130ebeccac96 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x236aa50979d5f3de3bd1eeb40e81137f22ab794b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf86df9b91f002cfeb2aed0e6d05c4c4eaef7cf02 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4200000000000000000000000000000000000006 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x6921b130d297cc43754afba22e5eac0fbf8db75b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x5babfc2f240bc5de90eb7e19d789412db1dec402 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x532f27101965dd16442e59d40670faf5ebb142e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x833589fcd6edb6e08f4c7c32d4f71b54bda02913 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4ed4e862860bed51a9570b96d89af5e1b0efefed - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xac1bd2486aaf3b5c0fc3fd868558b082a531b2b4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x0d97f261b1e88845184f678e2d1e7a98d9fd38de - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8129b94753f22ec4e62e2c4d099ffe6773969ebc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3f14920c99beb920afa163031c4e47a3e03b3e4a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x940181a94a35a4569e4529a3cdfb74e38fd98631 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3419875b4d3bca7f3fdda2db7a476a79fd31b4fe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xa067436db77ab18b1a315095e4b816791609897c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xafb89a09d82fbde58f18ac6437b3fc81724e4df6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x489fe42c267fe0366b16b0c39e7aeef977e841ef - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xdc46c1e93b71ff9209a0f8076a9951569dc35855 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x91f45aa2bde7393e0af1cc674ffe75d746b93567 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x236aa50979d5f3de3bd1eeb40e81137f22ab794b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xf6e932ca12afa26665dc4dde7e27be02a7c02e50 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x524d524b4c9366be706d3a90dcf70076ca037ae3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x5b5dee44552546ecea05edea01dcd7be7aa6144a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x2598c30330d5771ae9f983979209486ae26de875 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfa980ced6895ac314e7de34ef1bfae90a5add21b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x469fda1fb46fcb4befc0d8b994b516bd28c87003 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4e496c0256fb9d4cc7ba2fdf931bc9cbb7731660 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x27d2decb4bfc9c76f0309b8e88dec3a601fe25a8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbfd5206962267c7b4b4a8b3d76ac2e1b2a5c4d5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9e1028f5f1d5ede59748ffcee5532509976840e0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3c3aa127e6ee3d2f2e432d0184dd36f2d2076b52 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xba5e6fa2f33f3955f0cef50c63dcc84861eab663 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x97c806e7665d3afd84a8fe1837921403d59f3dcc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8ee73c484a26e0a5df2ee2a4960b789967dd0415 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x00e57ec29ef2ba7df07ad10573011647b2366f6d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8f019931375454fe4ee353427eb94e2e0c9e0a8c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x93e6407554b2f02640ab806cd57bd83e848ec65d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x55d398326f99059ff775485246999027b3197955 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x2170ed0880ac9a755fd29b2688956bd959f933f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xfdc66a08b0d0dc44c17bbd471b88f49f50cdd20f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x7130d2a12b9bcbfae4f2634d864a1ee1ce3ead9c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1d2f0da169ceb9fc7b3144628db156f3f6c60dbe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xe9e7cea3dedca5984780bafc599bd69add087d56 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xfa54ff1a158b5189ebba6ae130ced6bbd3aea76e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x570a5d26f7765ecb712c0924e4de545b89fd43df - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x47c454ca6be2f6def6f32b638c80f91c9c3c5949 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xad86d0e9764ba90ddd68747d64bffbd79879a238 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf8a0bf9cf54bb92f17374d9e9a321e6a111a51bd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xd691d9a68c887bdf34da8c36f63487333acfd103 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1294f4183763743c7c9519bec51773fb3acd78fd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xb04906e95ab5d797ada81508115611fee694c2b3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x111111111117dc0aa78b770fa6a738034120c302 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xcc42724c6683b7e57334c4e856f4c9965ed682bd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x90c97f71e18723b0cf0dfa30ee176ab653e89f40 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0e09fabb73bd3ade0a17ecc321fd13a19e81ce82 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x2b72867c32cf673f7b02d208b26889fed353b1f8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x031b41e504677879370e9dbcf937283a8691fa7f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1ce0c2827e2ef14d5c4f29a091d735a204794041 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xcf3bb6ac0f6d987a5727e2d15e39c2d6061d5bec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x8ff795a6f4d97e7887c79bea79aba5cc76444adf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x2dff88a56767223a5529ea5960da7a3f5f766406 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x003d87d02a2a01e9e8a20f507c83e15dd83a33d1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x4b0f1812e5df2a09796481ff14017e6005508003 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbf5140a22578168fd562dccf235e5d43a02ce9b1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xca1c644704febf4ab81f85daca488d1623c28e63 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x51e72dd1f2628295cc2ef931cb64fdbdc3a0c599 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbbca42c60b5290f2c48871a596492f93ff0ddc82 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x555296de6a86e72752e5c5dc091fe49713aa145c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0808bf94d57c905f1236212654268ef82e1e594e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x8457ca5040ad67fdebbcc8edce889a335bc0fbfb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xcebef3df1f3c5bfd90fde603e71f31a53b11944d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x90ed8f1dc86388f14b64ba8fb4bbd23099f18240 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x9840652dc04fb9db2c43853633f0f62be6f00f98 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xba2ae424d960c26247dd6c32edc70b295c744c43 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0782b6d8c4551b9760e74c0545a9bcd90bdc41e5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbe2b6c5e31f292009f495ddbda88e28391c9815e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x8f0528ce5ef7b51152a59745befdd91d97091d2f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xffeecbf8d7267757c2dc3d13d730e97e15bfdf7f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0eb3a705fc54725037cc9e008bdede697f62f335 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf21768ccbc73ea5b6fd3c687208a7c2def2d966e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0000028a2eb8346cd5c0267856ab7594b7a55308 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x76a797a59ba2c17726896976b7b3747bfd1d220f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xc79d1fd14f514cd713b5ca43d288a782ae53eab2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xad29abb318791d579433d831ed122afeaf29dcfe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x3203c9e46ca618c8c1ce5dc67e7e9d75f5da2377 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xdb021b1b247fe2f1fa57e0a87c748cc1e321f07f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x7083609fce4d1d8dc0c979aab8c869ea2c873402 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xc5f0f7b66764f6ec8c8dff7ba683102295e16409 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xe29142e14e52bdfbb8108076f66f49661f10ec10 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xb0d502e938ed5f4df2e681fe6e419ff29631d62b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x6730f7a6bbb7b9c8e60843948f7feb4b6a17b7f7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1613957159e9b0ac6c80e824f7eea748a32a0ae2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x471ece3750da237f93b8e339c536989b8978a438 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x765de816845861e75a25fca122bb6898b8b1282a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x66803fb87abd4aac3cbb3fad7c3aa01f6f3fb207 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xd8763cba276a3738e6de85b4b3bf5fded6d6ca73 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x37f750b7cc259a2f741af45294f6a16572cf5cad - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xd71ffd0940c920786ec4dbb5a12306669b5b81ef - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xe8537a3d056da446677b9e9d6c5db704eaab4787 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x4f604735c1cf31399c6e711d5962b2b3e0225ad3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x02de4766c272abc10bc88c220d214a26960a7e92 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xceba9300f2b948710d2653dd7b07f33a8b32118c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0xc16b81af351ba9e64c1a069e3ab18c244a1e3049 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x728f30fa2f100742c7949d1961804fa8e0b1387d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x41ea5d41eeacc2d5c4072260945118a13bb7ebce - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf21661d0d1d76d3ecb8e1b9f1c923dbfffae4097 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb0ecc6ac0073c063dcfc026ccdc9039cae2998e1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x00f932f0fe257456b32deda4758922e56a4f4b42 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xa4af354d466e8a68090dd9eb2cb7caf162f4c8c2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xba50933c268f567bdc86e1ac131be072c6b0b71a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd29da236dd4aac627346e1bba06a619e8c22d7c5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1bfce574deff725a3f483c334b790e25c8fa9779 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9e18d5bab2fa94a6a95f509ecb38f8f68322abd3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbf5495efe5db9ce00f80364c8b423567e58d2110 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x065b4e5dfd50ac12a81722fd0a0de81d78ddf7fb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x57e114b691db790c35207b2e685d4a43181e6061 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0b7f0e51cd1739d6c96982d55ad8fa634dd43a9c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc56c7a0eaa804f854b536a5f3d5f49d2ec4b12b8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x594daad7d77592a2b97b725a7ad59d7e188b5bfa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8355dbe8b0e275abad27eb843f3eaf3fc855e525 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2a961d752eaa791cbff05991e4613290aec0d9ac - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x38e68a37e401f7271568cecaac63c6b1e19130b4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1131d427ecd794714ed00733ac0f851e904c8398 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1495bc9e44af1f8bcb62278d2bec4540cf0c05ea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x808507121b80c02388fad14726482e061b8da827 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x44971abf0251958492fee97da3e5c5ada88b9185 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x320623b8e4ff03373931769a31fc52a4e78b5d70 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6e5970dbd6fc7eb1f29c6d2edf2bc4c36124c0c1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd40c688da9df74e03566eaf0a7c754ed98fbb8cc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8afe4055ebc86bd2afb3940c0095c9aca511d852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9ce84f6a69986a83d92c324df10bc8e64771030f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbe4d9c8c638b5f0864017d7f6a04b66c42953847 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x68bbed6a47194eff1cf514b50ea91895597fc91e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x69420e3a3aa9e17dea102bb3a9b3b73dcddb9528 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7420b4b9a0110cdc71fb720908340c03f9bc03ec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x03aa6298f1370642642415edc0db8b957783e8d6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd533a949740bb3306d119cc777fa900ba034cd52 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf14dd7b286ce197019cba54b189d2b883e70f761 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa35923162c49cf95e6bf26623385eb431ad920d3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8cefbeb2172a9382753de431a493e21ba9694004 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x120a3879da835a5af037bb2d1456bebd6b54d4ba - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x69457a1c9ec492419344da01daf0df0e0369d5d0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf6ce4be313ead51511215f1874c898239a331e37 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x73d7c860998ca3c01ce8c808f5577d94d545d1b4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xeff49b0f56a97c7fd3b51f0ecd2ce999a7861420 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x236501327e701692a281934230af0b6be8df3353 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5026f006b85729a8b14553fae6af249ad16c9aab - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x66761fa41377003622aee3c7675fc7b5c1c2fac5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9f9c8ec3534c3ce16f928381372bfbfbfb9f4d24 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd8c978de79e12728e38aa952a6cb4166f891790f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7122985656e38bdc0302db86685bb972b145bd3c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x582d872a1b094fc48f5de31d3b73f2d9be47def1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x504624040e0642921c2c266a9ac37cafbd8cda4e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc548e90589b166e1364de744e6d35d8748996fe8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4c11249814f11b9346808179cf06e71ac328c1b5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x423f4e6138e475d85cf7ea071ac92097ed631eea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8390a1da07e376ef7add4be859ba74fb83aa02d5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf94e7d0710709388bce3161c32b4eea56d3f91cc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xaa95f26e30001251fb905d264aa7b00ee9df6c18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2416092f143378750bb29b79ed961ab195cceea5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6c84a8f1c29108f47a79964b5fe888d4f4d0de40 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x71eeba415a523f5c952cc2f06361d5443545ad28 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x88a269df8fe7f53e590c561954c52fccc8ec0cfb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x429fed88f10285e61b12bdf00848315fbdfcc341 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb299751b088336e165da313c33e3195b8c6663a6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf0a479c9c3378638ec603b8b6b0d75903902550b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb59c8912c83157a955f9d715e556257f432c35d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xba0dda8762c24da9487f5fa026a9b64b695a07ea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xc24a365a870821eb83fd216c9596edd89479d8d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xa586b3b80d7e3e8d439e25fbc16bc5bcee3e2c85 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xef04804e1e474d3f9b73184d7ef5d786f3fce930 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2e9a6df78e42a30712c10a9dc4b1c8656f8f2879 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x13a7dedb7169a17be92b0e3c7c2315b46f4772b3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1dd6b5f9281c6b4f043c02a83a46c2772024636c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xc5102fe9359fd9a28f877a67e36b0f050d81a3cc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xf525e73bdeb4ac1b0e741af3ed8a8cbb43ab0756 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe4177c1400a8eee1799835dcde2489c6f0d5d616 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xed5740209fcf6974d6f3a5f11e295b5e468ac27c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe10d4a4255d2d35c9e23e2c4790e073046fbaf5c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x10398abc267496e49106b07dd6be13364d10dc71 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x2218a117083f5b482b0bb821d27056ba9c04b1d3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x395ae52bb17aef68c2888d941736a71dc6d4e125 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x9a601c5bb360811d96a23689066af316a30c3027 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xbac3368b5110f3a3dda8b5a0f7b66edb37c47afe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x1d3c629ca5c1d0ab3bdf74600e81b4145615df8e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe9c21de62c5c5d0ceacce2762bf655afdceb7ab3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x658cda444ac43b0a7da13d638700931319b64014 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3d2bd0e15829aa5c362a4144fdf4a1112fa29b5c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3fb83a9a2c4408909c058b0bfe5b4823f54fafe2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x00e5646f60ac6fb446f621d146b6e1886f002905 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x12a4cebf81f8671faf1ab0acea4e3429e42869e7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9ff62d1fc52a907b6dcba8077c2ddca6e6a9d3e1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc61f39418cd27820b5d4e9ba4a7197eefaeb8b05 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x15b7c0c907e4c6b9adaaaabc300c08991d6cea05 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7f67639ffc8c93dd558d452b8920b28815638c44 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x276c9cbaa4bdf57d7109a41e67bd09699536fa3d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x041fdf3f472d2c8a7ecc458fc3b7f543e6c57ef7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3c281a39944a2319aa653d81cfd93ca10983d234 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x96419929d7949d6a801a6909c145c8eef6a40431 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfea9dcdc9e23a9068bf557ad5b186675c61d33ea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xdb6e0e5094a25a052ab6845a9f1e486b9a9b3dde - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xcde172dc5ffc46d228838446c57c1227e0b82049 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xff0c532fdb8cd566ae169c1cb157ff2bdc83e105 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9a26f5433671751c3276a065f57e5a02d2817973 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3636a7734b669ce352e97780df361ce1f809c58c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x50c5725949a6f0c72e6c4a641f24049a917db0cb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xe3086852a4b125803c815a158249ae468a3254ca - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbeb0fd48c2ba0f1aacad2814605f09e08a96b94e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbc45647ea894030a4e9801ec03479739fa2485f0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x768be13e1680b5ebe0024c42c896e3db59ec0149 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x928a6a9fc62b2c94baf2992a6fba4715f5bb0066 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbf4db8b7a679f89ef38125d5f84dd1446af2ea3b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xed899bfdb28c8ad65307fa40f4acab113ae2e14c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1b6a569dd61edce3c383f6d565e2f79ec3a12980 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x76734b57dfe834f102fb61e1ebf844adf8dd931e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4621b7a9c75199271f773ebd9a499dbd165c3191 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xaf07d812d1dcec20bf741075bc18660738d226dd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7f12d13b34f5f4f0a9449c16bcd42f0da47af200 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x55a6f6cb50db03259f6ab17979a4891313be2f45 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x968d6a288d7b024d5012c0b25d67a889e4e3ec19 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7a8a5012022bccbf3ea4b03cd2bb5583d915fb1a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xcde90558fc317c69580deeaf3efc509428df9080 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x0028e1e60167b48a938b785aa5292917e7eaca8b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x76e7447bafa3f0acafc9692629b1d1bc937ca15d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x15ac90165f8b45a80534228bdcb124a011f62fee - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4045b33f339a3027af80013fb5451fdbb01a4492 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xddf98aad8180c3e368467782cd07ae2e3e8d36a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x698dc45e4f10966f6d1d98e3bfd7071d8144c233 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3c8665472ec5af30981b06b4e0143663ebedcc1e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x18a8bd1fe17a1bb9ffb39ecd83e9489cfd17a022 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xba0dda8762c24da9487f5fa026a9b64b695a07ea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x13741c5df9ab03e7aa9fb3bf1f714551dd5a5f8a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xebff2db643cf955247339c8c6bcd8406308ca437 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfadb26be94c1f959f900bf88cd396b3e803481d6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x52c2b317eb0bb61e650683d2f287f56c413e4cf6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x38d513ec43dda20f323f26c7bef74c5cf80b6477 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x33ad778e6c76237d843c52d7cafc972bb7cf8729 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x290814ad0fbd2b935f34d7b40306102313d4c63e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x5e432eecd01c12ee7071ee9219c2477a347da192 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xbdf5bafee1291eec45ae3aadac89be8152d4e673 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xff62ddfa80e513114c3a0bf4d6ffff1c1d17aadf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8c81b4c816d66d36c4bf348bdec01dbcbc70e987 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x6b82297c6f1f9c3b1f501450d2ee7c37667ab70d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x42069babe14fb1802c5cb0f50bb9d2ad6fef55e2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x72499bddb67f4ca150e1f522ca82c87bc9fb18c8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x0578d8a44db98b23bf096a382e016e29a5ce0ffe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8fe815417913a93ea99049fc0718ee1647a2a07c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7d12aeb5d96d221071d176980d23c213d88d9998 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb166e8b140d35d9d8226e40c09f757bac5a4d87d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8853f0c059c27527d33d02378e5e4f6d5afb574a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xf3c052f2baab885c610a748eb01dfbb643ba835b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xcd1cffa8ebc66f1a2cf7675b48ba955ffcb82d8e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xde7a416ac821c77478340eebaa21b68297025ef3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x2da56acb9ea78330f947bd57c54119debda7af71 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8972ab69d499b5537a31576725f0af8f67203d38 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x88faea256f789f8dd50de54f9c807eef24f71b16 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x42069de48741db40aef864f8764432bbccbd0b69 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9a27c6759a6de0f26ac41264f0856617dec6bc3f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfaa4f3bcfc87d791e9305951275e0f62a98bcb10 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfd9fa4f785331ce88b5af8994a047ba087c705d8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x21eceaf3bf88ef0797e3927d855ca5bb569a47fc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7d9ce55d54ff3feddb611fc63ff63ec01f26d15f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4229c271c19ca5f319fb67b4bc8a40761a6d6299 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x80f45eacf6537498ecc660e4e4a2d2f99e195cf4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1a475d06d967aeb686c98de80d079d72097aeacf - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4fb9b20dafe45d91ae287f2e07b2e79709308178 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xd3741ac9b3f280b0819191e4b30be4ecd990771e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x09579452bc3872727a5d105f342645792bb8a82b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8a24d7260cd02d3dfd8eefb66bc17ad4b17d494c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xd88611a629265c9af294ffdd2e7fa4546612273e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9a86980d3625b4a6e69d8a4606d51cbc019e2002 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1c7a460413dd4e964f96d8dfc56e7223ce88cd85 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x776aaef8d8760129a0398cf8674ee28cefc0eab9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x28e29ec91db66733a94ee8e3b86a6199117baf99 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb9898511bd2bad8bfc23eba641ef97a08f27e730 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x76baa16ff15d61d32e6b3576c3a8c83a25c2f180 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x2816a491dd0b7a88d84cbded842a618e59016888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xa7ea9d5d4d4c7cf7dbde5871e6d108603c6942a5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x586e10db93630a4d2da6c6a34ba715305b556f04 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf486ad071f3bee968384d2e39e2d8af0fcf6fd46 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x76d36d44dc4595e8d2eb3ad745f175eda134284f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x1fa4a73a3f0133f0025378af00236f3abdee5d63 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xb3ed0a426155b79b898849803e3b36552f7ed507 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0ef4a107b48163ab4b57fca36e1352151a587be4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x62694d43ccb9b64e76e38385d15e325c7712a735 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xa2b726b1145a4773f68593cf171187d8ebe4d495 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf275e1ac303a4c9d987a2c48b8e555a77fec3f1c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x11a31b833d43853f8869c9eec17f60e3b4d2a753 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbadff0ef41d2a68f22de21eabca8a59aaf495cf0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1fdd61ef9a5c31b9a2abc7d39c139c779e8412af - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4ade2b180f65ed752b6f1296d0418ad21eb578c0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0c5cb676e38d6973837b9496f6524835208145a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb69753c06bb5c366be51e73bfc0cc2e3dc07e371 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8143182a775c54578c8b7b3ef77982498866945d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x76e222b07c53d28b89b0bac18602810fc22b49a8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x18aaa7115705e8be94bffebde57af9bfc265b998 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7d8146cf21e8d7cbe46054e01588207b51198729 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xfe0c30065b384f05761f15d0cc899d4f9f9cc0eb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1ce270557c1f68cfb577b856766310bf8b47fd9c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x793a5d8b30aab326f83d20a9370c827fea8fdc51 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xff836a5821e69066c87e268bc51b849fab94240c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf4d2888d29d722226fafa5d9b24f9164c092421e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8ed97a637a790be1feff5e888d43629dc05408f6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x31c8eacbffdd875c74b94b077895bd78cf1e64a3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc55126051b22ebb829d00368f4b12bde432de5da - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe0f63a424a4439cbe457d80e4f4b51ad25b2c56c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8881562783028f5c1bcb985d2283d5e170d88888 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x67466be17df832165f8c80a5a120ccc652bd7e69 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd939212f16560447ed82ce46ca40a63db62419b5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x88417754ff7062c10f4e3a4ab7e9f9d9cbda6023 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5afe3855358e112b5647b952709e6165e1c1eeee - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x02e7f808990638e9e67e1f00313037ede2362361 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd2bdaaf2b9cc6981fd273dcb7c04023bfbe0a7fe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x112b08621e27e10773ec95d250604a041f36c582 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x32b053f2cba79f80ada5078cb6b305da92bde6e1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5ac34c53a04b9aaa0bf047e7291fb4e8a48f2a18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x26ebb8213fb8d66156f1af8908d43f7e3e367c1d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe3b9cfb8ea8a4f1279fbc28d3e15b4d2d86f18a0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8207c1ffc5b6804f6024322ccf34f29c3541ae26 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x255f1b39172f65dc6406b8bee8b08155c45fe1b6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x092baadb7def4c3981454dd9c0a0d7ff07bcfc86 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x53bcf6698c911b2a7409a740eacddb901fc2a2c6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2ac2b254bc18cd4999f64773a966e4f4869c34ee - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x17fc002b466eec40dae837fc4be5c67993ddbd6f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xc8a4eea31e9b6b61c406df013dd4fec76f21e279 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x498bf2b1e120fed3ad3d42ea2165e9b73f99c1e5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe4dddfe67e7164b0fe14e218d80dc4c08edc01cb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x7c8a1a80fdd00c9cccd6ebd573e9ecb49bfa2a59 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1debd73e752beaf79865fd6446b0c970eae7732f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xaf5db6e1cc585ca312e8c8f7c499033590cf5c98 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x65559aa14915a70190438ef90104769e5e890a00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x7fb688ccf682d58f86d7e38e03f9d22e7705448b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x73cb180bf0521828d8849bc8cf2b920918e23032 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x2e3d870790dc77a83dd1d18184acc7439a53f475 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0xa00e3a3511aac35ca78530c85007afcd31753819 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x528cdc92eab044e1e39fe43b9514bfdab4412b98 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x4f604735c1cf31399c6e711d5962b2b3e0225ad3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x1c954e8fe737f99f68fa1ccda3e51ebdb291948c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xf50d05a1402d0adafa880d36050736f9f6ee7dee - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xab0b2ddb9c7e440fac8e140a89c0dbcbf2d7bbff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x8bc3ec2e7973e64be582a90b08cadd13457160fe - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x64060ab139feaae7f06ca4e63189d86adeb51691 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x5ec03c1f7fa7ff05ec476d19e34a22eddb48acdc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x9627a3d6872be48410fcece9b1ddd344bf08c53e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x1ed02954d60ba14e26c230eec40cbac55fa3aeea - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8d3419b9a18651f3926a205ee0b1acea1e7192de - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb56d0839998fd79efcd15c27cf966250aa58d6d3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x81f91fe59ee415735d59bd5be5cca91a0ea4fa69 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x87c211144b1d9bdaa5a791b8099ea4123dc31d21 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xf4210f93bc68d63df3286c73eba08c6414f40c0d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xece7b98bd817ee5b1f2f536daf34d0b6af8bb542 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4c96a67b0577358894407af7bc3158fc1dffbeb5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x70737489dfdf1a29b7584d40500d3561bd4fe196 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x39353a32eceafe4979a8606512c046c3b6398cc4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x92fb1b7d9730b2f1bd4e2e91368c1eb6fdd2a009 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x174e33ef2effa0a4893d97dda5db4044cc7993a3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfdc944fb59201fb163596ee5e209ebc8fa4dcdc5 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x388e543a5a491e7b42e3fbcd127dd6812ea02d0d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x56a38e7216304108e841579041249feb236c887b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1804e3db872eed4141e482ff74c56862f2791103 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9de16c805a3227b9b92e39a446f9d56cf59fe640 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb8d98a102b0079b69ffbc760c8d857a31653e56e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x5d6812722c3693078e4a0dbe3e9affc27a0b2768 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x255f1b39172f65dc6406b8bee8b08155c45fe1b6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xc2fe011c3885277c7f0e7ffd45ff90cadc8ecd12 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xc1ffaef4e7d553bbaf13926e258a1a555a363a07 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4e73420dcc85702ea134d91a262c8ffc0a72aa70 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xecaf81eb42cd30014eb44130b89bcd6d4ad98b92 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x4eae52907dba9c370e9ee99f0ce810602a4f2c63 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x25d887ce7a35172c62febfd67a1856f20faebb00 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x382ea807a61a418479318efd96f1efbc5c1f2c21 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6468e79a80c0eab0f9a2b574c8d5bc374af59414 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3106a0a076bedae847652f42ef07fd58589e001f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd015422879a1308ba557510345e944b912b9ab73 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5de8ab7e27f6e7a1fff3e5b337584aa43961beef - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xcf078da6e85389de507ceede0e3d217e457b9d49 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1bbf25e71ec48b84d773809b4ba55b6f4be946fb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7039cd6d7966672f194e8139074c3d5c4e6dcf65 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x943af17c37207c9d7a27d12cb5055542a0b7afa8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6d68015171eaa7af9a5a0a103664cf1e506ff699 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6942806d1b2d5886d95ce2f04314ece8eb825833 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x949d48eca67b17269629c7194f4b727d4ef9e5d6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9361adf2b72f413d96f81ff40d794b47ce13b331 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3bb1be077f3f96722ae92ec985ab37fd0a0c4c51 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xdbb7a34bf10169d6d2d0d02a6cbb436cf4381bfa - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x66bff695f3b16a824869a8018a3a6e3685241269 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x85d19fb57ca7da715695fcf347ca2169144523a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x069d89974f4edabde69450f9cf5cf7d8cbd2568d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0fe13ffe64b28a172c58505e24c0c111d149bd47 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x111111111117dc0aa78b770fa6a738034120c302 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xdc7ac5d5d4a9c3b5d8f3183058a92776dc12f4f3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x482702745260ffd69fc19943f70cffe2cacd70e9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xc555d625828c4527d477e595ff1dd5801b4a600e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9eec1a4814323a7396c938bc86aec46b97f1bd82 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x87d73e916d7057945c9bcd8cdd94e42a6f47f776 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x067def80d66fb69c276e53b641f37ff7525162f6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xdd157bd06c1840fa886da18a138c983a7d74c1d7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe80772eaf6e2e18b651f160bc9158b2a5cafca65 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb6093b61544572ab42a0e43af08abafd41bf25a6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x35ca1e5a9b1c09fa542fa18d1ba4d61c8edff852 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x83e60b9f7f4db5cdb0877659b1740e73c662c55b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4d01397994aa636bdcc65c9e8024bc497498c3bb - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xc3abc47863524ced8daf3ef98d74dd881e131c38 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x4d15a3a2286d883af0aa1b3f21367843fac63e07 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xfb7f8a2c0526d01bfb00192781b7a7761841b16c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x3809dcdd5dde24b37abe64a5a339784c3323c44f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x85955046df4668e1dd369d2de9f3aeb98dd2a369 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x554cd6bdd03214b10aafa3e0d4d42de0c5d2937b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x4318cb63a2b8edf2de971e2f17f77097e499459d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xab9cb20a28f97e189ca0b666b8087803ad636b3c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x6a8ec2d9bfbdd20a7f5a4e89d640f7e7ceba4499 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x385eeac5cb85a38a9a07a70c73e0a3271cfb54a7 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0169ec1f8f639b32eec6d923e24c2a2ff45b9dd6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xe161be4a74ab8fa8706a2d03e67c02318d0a0ad6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4d58608eff50b691a3b76189af2a7a123df1e9ba - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x420b0fa3de2efcf2b2fd04152eb1df36a09717cd - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1cd38856ee0fdfd65c757e530e3b1de3061008d3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfad8cb754230dbfd249db0e8eccb5142dd675a0d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xda761a290e01c69325d12d82ac402e5a73d62e81 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xafb5d4d474693e68df500c9c682e6a2841f9661a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xfc5462143a3178cf044e97c491f6bcb5e38f173e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xed1978d01d4a8a9d6a43ac79403d5b8dfbed739b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xba71cb8ef2d59de7399745793657838829e0b147 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x10c1b6f768e13c624a4a23337f1a5ba5c9be0e4b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1b1514c76c54ce8807d7fdedf85c664eee734ece - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x58cd93c4a91c3940109fa27d700f5013b18b5dc2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xea6f7e7e0f46a9e0f4e2048eb129d879f609d632 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x30d19fb77c3ee5cfa97f73d72c6a1e509fa06aef - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xe2dca969624795985f2f083bcd0b674337ba130a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbb7d61d2511fd2e63f02178ca9b663458af9fc63 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x59f4f336bf3d0c49dbfba4a74ebd2a6ace40539a - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x62d0a8458ed7719fdaf978fe5929c6d342b0bfce - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb8fda5aee55120247f16225feff266dfdb381d4c - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xca530408c3e552b020a2300debc7bd18820fb42f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3ffeea07a27fab7ad1df5297fa75e77a43cb5790 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xcfeb09c3c5f0f78ad72166d55f9e6e9a60e96eec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x467bccd9d29f223bce8043b84e8c8b282827790f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2077d81d0c5258230d5a195233941547cb5f0989 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa0bbbe391b0d0957f1d013381b643041d2ca4022 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd1b89856d82f978d049116eba8b7f9df2f342ff3 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x62f03b52c377fea3eb71d451a95ad86c818755d1 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3927fb89f34bbee63351a6340558eebf51a19fb8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xacd2c239012d17beb128b0944d49015104113650 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x86b69f38bea3e02f68ff88534bc61ec60e772b19 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6873c95307e13beb58fb8fcddf9a99667655c9e4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x18084fba666a33d37592fa2633fd49a74dd93a88 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6e79b51959cf968d87826592f46f819f92466615 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x80ee5c641a8ffc607545219a3856562f56427fe9 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0414d8c87b271266a5864329fb4932bbe19c0c49 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xf57e7e7c23978c3caec3c3548e3d615c346e79ff - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb0ffa8000886e57f86dd5264b9582b2ad87b2b91 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1c986661170c1834db49c3830130d4038eeeb866 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9ed7e4b1bff939ad473da5e7a218c771d1569456 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x7f9a7db853ca816b9a138aee3380ef34c437dee0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x371c7ec6d8039ff7933a2aa28eb827ffe1f52f07 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb1bc21f748ae2be95674876710bc6d78235480e0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xadf5dd3e51bf28ab4f07e684ecf5d00691818790 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x1eba7a6a72c894026cd654ac5cdcf83a46445b08 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x38022a157b95c52d43abcac9bd09f028a1079105 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xd2507e7b5794179380673870d88b22f94da6abe0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xc708d6f2153933daa50b2d0758955be0a93a8fec - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x0052074d3eb1429f39e5ea529b54a650c21f5aa4 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x4e78011ce80ee02d2c3e649fb657e45898257815 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x7583feddbcefa813dc18259940f76a02710a8905 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xe78aee6ccb05471a69677fb74da80f5d251c042b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x04f177fcacf6fb4d2f95d41d7d3fee8e565ca1d0 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xa6da8c8999c094432c77e7d318951d34019af24b - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x6d3b8c76c5396642960243febf736c6be8b60562 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7cf7132ede0ca592a236b6198a681bb7b42dd5ae - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3afeae00a594fbf2e4049f924e3c6ac93296b6e8 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x0a93a7be7e7e426fc046e204c44d6b03a302b631 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xc9b6ef062fab19d3f1eabc36b1f2e852af1acd18 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1754e5aadce9567a95f545b146a616ce34eead53 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xdb173587d459ddb1b9b0f2d6d88febef039304a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x10a7a84c91988138f8dbbc82a23b02c8639e2552 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x92af6f53febd6b4c6f5293840b6076a1b82c4bc2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xeb9e49fb4c33d9f6aefb1b03f9133435e24c0ec6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x1b2c141479757b8643a519be4692904088d860b2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4d25e94291fe8dcfbfa572cbb2aaa7b755087c91 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8e0e798966382e53bfb145d474254cbe065c17dc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4b6f82a4ed0b9e3767f53309b87819a78d041a7f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x004aa1586011f3454f487eac8d0d5c647d646c69 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0x741777f6b6d8145041f73a0bddd35ae81f55a40f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xc6c58f600917de512cd02d2b6ed595ab54b4c30f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x03aa6298f1370642642415edc0db8b957783e8d6 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x3ee2200efb3400fabb9aacf31297cbdd1d435d47 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x0d8ce2a99bb6e3b7db580ed848240e4a0f9ae153 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xa697e272a73744b343528c3bc4702f2565b2f422 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x301af3eff0c904dc5ddd06faa808f653474f7fcc - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x776f9987d9deed90eed791cbd824d971fd5ccf09 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xf7de7e8a6bd59ed41a4b5fe50278b3b7f31384df - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x19e6bfc1a6e4b042fb20531244d47e252445df01 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x4338665cbb7b2485a8855a139b75d5e34ab0db94 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x2940566eb50f15129238f4dc599adc4f742d7d8e - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xbb73bb2505ac4643d5c0a99c2a1f34b3dfd09d11 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x4ea98c1999575aaadfb38237dd015c5e773f75a2 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/celo/0x1d18d0386f51ab03e7e84e71bda1681eba865f1f - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x57b96d4af698605563a4653d882635da59bf11af - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd33526068d116ce69f19a9ee46f0bd304f21a51f - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x2a5fa016ffb20c70e2ef36058c08547f344677aa - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbe0ed4138121ecfc5c0e56b40517da27e6c5226b - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x9fd9278f04f01c6a39a9d1c1cd79f7782c6ade08 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x054c9d4c6f4ea4e14391addd1812106c97d05690 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7613c48e0cd50e42dd9bf0f6c235063145f6f8dc - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x614da3b37b6f66f7ce69b4bbbcf9a55ce6168707 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x069e4aa272d17d9625aa3b6f863c7ef6cfb96713 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x24da31e7bb182cb2cabfef1d88db19c2ae1f5572 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x7d4a23832fad83258b32ce4fd3109ceef4332af4 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb58e61c3098d85632df34eecfb899a1ed80921cb - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x67c4d14861f9c975d004cfb3ac305bee673e996e - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x69babe9811cc86dcfc3b8f9a14de6470dd18eda4 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x32f0d04b48427a14fb3cbc73db869e691a9fec6f - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x4cff49d0a19ed6ff845a9122fa912abcfb1f68a6 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x51cb253744189f11241becb29bedd3f1b5384fdb - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xcf4c91ecafc43c9f382db723ba20b82efa852821 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6968676661ac9851c38907bdfcc22d5dd77b564d - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0d438f3b5175bebc262bf23753c1e53d03432bde - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb98d4c97425d9908e66e53a6fdf673acca0be986 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x68a47fe1cf42eba4a030a10cd4d6a1031ca3ca0a - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x8a370c951f34e295b2655b47bb0985dd08d8f718 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x525574c899a7c877a11865339e57376092168258 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xd9a442856c234a39a81a089c06451ebaa4306a72 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x1c43d05be7e5b54d506e3ddb6f0305e8a66cd04e - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xb766039cc6db368759c1e56b79affe831d0cc507 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x18c14c2d707b2212e17d1579789fc06010cfca23 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0xe0ee18eacafddaeb38f8907c74347c44385578ab - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x56659245931cb6920e39c189d2a0e7dd0da2d57b - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0xb6a5ae40e79891e4deadad06c8a7ca47396df21c - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x04565fe9aa3ae571ada8e1bebf8282c4e5247b2a - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xf8a99f2bf2ce5bb6ce4aafcf070d8723bc904aa2 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x3b9728bd65ca2c11a817ce39a6e91808cceef6fd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x6797b6244fa75f2e78cdffc3a4eb169332b730cc - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xe2c86869216ac578bd62a4b8313770d9ee359a05 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x47b464edb8dc9bc67b5cd4c9310bb87b773845bd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x28a730de97dc62a8c88363e0b1049056f1274a70 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xba5ede8d98ab88cea9f0d69918dde28dc23c2553 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8319767a7b602f88e376368dca1b92d38869b9b4 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x461ee40928677644b8195662ab91bcdaae6ef105 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x24569d33653c404f90af10a2b98d6e0030d3d267 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x22222bd682745cf032006394750739684e45a5f8 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x9124577428c5bd73ad7636cbc5014081384f29d6 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xaa6cccdce193698d33deb9ffd4be74eaa74c4898 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xe095780ba2a64a4efa7a74830f0b71656f0b0ad4 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb59c8912c83157a955f9d715e556257f432c35d7 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7771450ece9c61430953d2646f995e33a06c91f5 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xc48823ec67720a04a9dfd8c7d109b2c3d6622094 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x9ec02756a559700d8d9e79ece56809f7bcc5dc27 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x3593d125a4f7849a1b059e64f4517a86dd60c95d - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb0ffa8000886e57f86dd5264b9582b2ad87b2b91 - 2024-06-14T20:41:03.599Z + 2024-06-28T19:37:25.115Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xa045fe936e26e1e1e1fb27c1f2ae3643acde0171 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xbeef698bd78139829e540622d5863e723e8715f1 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x426a688ee72811773eb64f5717a32981b56f10c1 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x873259322be8e50d80a4b868d186cc5ab148543a - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x661c70333aa1850ccdbae82776bb436a0fcfeefb - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x0a2c375553e6965b42c135bb8b15a8914b08de0c - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x6fba952443be1de22232c824eb8d976b426b3c38 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1abaea1f7c830bd89acc67ec4af516284b1bc33c - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb62132e35a6c13ee1ee0f84dc5d40bad8d815206 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xb60fdf036f2ad584f79525b5da76c5c531283a1b - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x5a3e6a77ba2f983ec0d371ea3b475f8bc0811ad5 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x55296f69f40ea6d20e478533c15a6b08b654e758 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x1a7e4e63778b4f12a199c062f3efdd288afcbce8 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0x45804880de22913dafe09f4980848ece6ecbaf78 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/ethereum/0xe5018913f2fdf33971864804ddb5fca25c539032 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x2c650dab03a59332e2e0c0c4a7f726913e5028c1 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x9aee3c99934c88832399d6c6e08ad802112ebeab - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/arbitrum/0x439c0cf1038f8002a4cad489b427e217ba4b42ad - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/optimism/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/polygon/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xb79dd08ea68a908a97220c76d19a6aa9cbde4376 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x4b61e2f1bbdee6d746209a693156952936f1702c - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7480527815ccae421400da01e052b120cc4255e9 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x7466de7bb8b5e41ee572f4167de6be782a7fa75d - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x298d411511a05dc1b559ed8f79c56bee06687b14 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x8e16d46cb2da01cdd49601ec73d7b0344969ae33 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x18dd5b087bca9920562aff7a0199b96b9230438b - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x37f0c2915cecc7e977183b8543fc0864d03e064c - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0x37f24b26bcefbfac7f261b97f8036da98f81a299 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/base/0xacb5b33ce55ba7729e38b2b59677e71c0112f0d9 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0x6985884c4392d348587b19cb9eaaf157f13271cd - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z 0.8 https://app.uniswap.org/explore/tokens/bnb/0xc71b5f631354be6853efe9c3ab6b9590f8302e81 - 2024-06-21T20:00:53.479Z + 2024-06-28T20:19:23.439Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x7e744bbb1a49a44dfcc795014a4ba618e418fbbe + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x0c04ff41b11065eed8c9eda4d461ba6611591395 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x636bd98fc13908e475f56d8a38a6e03616ec5563 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x590246bfbf89b113d8ac36faeea12b7589f7fe5b + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x80034f803afb1c6864e3ca481ef1362c54d094b9 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x73fbd93bfda83b111ddc092aa3a4ca77fd30d380 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xff33a6b3dc0127862eedd3978609404b22298a54 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xc770eefad204b5180df6a14ee197d99d808ee52d + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xa0385e7283c83e2871e9af49eec0966088421ddd + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xb2617246d0c6c0087f18703d576831899ca94f01 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xba386a4ca26b85fd057ab1ef86e3dc7bdeb5ce70 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x9ebb0895bd9c7c9dfab0d8d877c66ba613ac98ea + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xd12a99dbc40036cec6f1b776dccd2d36f5953b94 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x8ab2ff0116a279a99950c66a12298962d152b83c + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x420698cfdeddea6bc78d59bc17798113ad278f9d + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xa8c8cfb141a3bb59fea1e2ea6b79b5ecbcd7b6ca + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xd8e8438cf7beed13cfabc82f300fb6573962c9e3 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0xb1c9d42fa4ba691efe21656a7e6953d999b990c4 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/arbitrum/0xdadeca1167fe47499e53eb50f261103630974905 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/arbitrum/0xa05245ade25cc1063ee50cf7c083b4524c1c4302 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/polygon/0x4fafad147c8cd0e52f83830484d164e960bdc6c3 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x4dd9077269dd08899f2a9e73507125962b5bc87f + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x8931ee05ec111325c1700b68e5ef7b887e00661d + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x26f1bb40ea88b46ceb21557dc0ffac7b7c0ad40f + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x642e993fa91ffe9fb24d39a8eb0e0663145f8e92 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x0c41f1fc9022feb69af6dc666abfe73c9ffda7ce + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0xf7ccb8a6e3400eb8eb0c47619134f7516e025215 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x2416092f143378750bb29b79ed961ab195cceea5 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0xf0268c5f9aa95baf5c25d646aabb900ac12f0800 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x0c067fc190cde145b0c537765a78d4e19873a5cc + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0xbe5614875952b1683cb0a2c20e6509be46d353a4 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x87a0233a8cb4392ec3eb8fa467817fc0b6a326dd + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0xdfbea88c4842d30c26669602888d746d30f9d60d + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0xb6fe221fe9eef5aba221c348ba20a1bf5e73624c + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x80b3455e1db60b4cba46aba12e8b1e256dd64979 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/bnb/0x747747e47a48c669be384e0dfb248eee6ba04039 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/celo/0x50e85c754929840b58614f48e29c64bc78c58345 + 2024-06-28T19:37:25.115Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x02f92800f57bcd74066f5709f1daa1a4302df875 + 2024-06-28T20:19:23.439Z + 0.8 + + + https://app.uniswap.org/explore/tokens/ethereum/0x967da4048cd07ab37855c090aaf366e4ce1b9f48 + 2024-06-28T20:19:23.439Z + 0.8 + + + https://app.uniswap.org/explore/tokens/base/0x729031b3995538ddf6b6bce6e68d5d6fdeb3ccb5 + 2024-06-28T20:19:23.439Z 0.8 \ No newline at end of file diff --git a/apps/web/src/assets/images/walletAnnouncementBannerQR.png b/apps/web/src/assets/images/walletAnnouncementBannerQR.png deleted file mode 100644 index d02d9767c2f..00000000000 Binary files a/apps/web/src/assets/images/walletAnnouncementBannerQR.png and /dev/null differ diff --git a/apps/web/src/components/AccountDrawer/AnalyticsToggle.tsx b/apps/web/src/components/AccountDrawer/AnalyticsToggle.tsx index eb558645c31..41af1e9630a 100644 --- a/apps/web/src/components/AccountDrawer/AnalyticsToggle.tsx +++ b/apps/web/src/components/AccountDrawer/AnalyticsToggle.tsx @@ -1,7 +1,7 @@ import { SettingsToggle } from 'components/AccountDrawer/SettingsToggle' import { t } from 'i18n' import { useState } from 'react' -// eslint-disable-next-line @typescript-eslint/no-restricted-imports +// eslint-disable-next-line no-restricted-imports import { analytics, getAnalyticsAtomDirect } from 'utilities/src/telemetry/analytics/analytics' export function AnalyticsToggle() { diff --git a/apps/web/src/components/AccountDrawer/AuthenticatedHeader.tsx b/apps/web/src/components/AccountDrawer/AuthenticatedHeader.tsx index 44f432efafa..0e5846fcb8a 100644 --- a/apps/web/src/components/AccountDrawer/AuthenticatedHeader.tsx +++ b/apps/web/src/components/AccountDrawer/AuthenticatedHeader.tsx @@ -28,6 +28,9 @@ import { ApplicationModal } from 'state/application/reducer' import { useUserHasAvailableClaim, useUserUnclaimedAmount } from 'state/claim/hooks' import styled from 'styled-components' import { ThemedText } from 'theme/components' +import { ArrowDownCircleFilled } from 'ui/src/components/icons' +import { FeatureFlags } from 'uniswap/src/features/gating/flags' +import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import Trace from 'uniswap/src/features/telemetry/Trace' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks' @@ -104,6 +107,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account const shouldShowBuyFiatButton = !isPathBlocked('/buy') const { formatNumber, formatDelta } = useFormatter() + const forAggregatorEnabled = useFeatureFlag(FeatureFlags.ForAggregatorWeb) const shouldDisableNFTRoutes = useDisableNFTRoutes() const unclaimedAmount: CurrencyAmount | undefined = useUserUnclaimedAmount(account) @@ -137,14 +141,27 @@ export default function AuthenticatedHeader({ account, openSettings }: { account } = useFiatOnrampAvailability(shouldCheck, openFoRModalWithAnalytics) const handleBuyCryptoClick = useCallback(() => { + if (forAggregatorEnabled) { + accountDrawer.close() + navigate(`/buy`, { replace: true }) + return + } + if (!fiatOnrampAvailabilityChecked) { setShouldCheck(true) } else if (fiatOnrampAvailable) { openFoRModalWithAnalytics() } - }, [fiatOnrampAvailabilityChecked, fiatOnrampAvailable, openFoRModalWithAnalytics]) + }, [ + accountDrawer, + fiatOnrampAvailabilityChecked, + fiatOnrampAvailable, + forAggregatorEnabled, + navigate, + openFoRModalWithAnalytics, + ]) const disableBuyCryptoButton = Boolean( - error || (!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) || fiatOnrampAvailabilityLoading + error || (!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) || fiatOnrampAvailabilityLoading, ) const { data: portfolioBalances } = useTokenBalancesQuery({ cacheOnly: !accountDrawer.isOpen }) @@ -223,7 +240,7 @@ export default function AuthenticatedHeader({ account, openSettings }: { account errorTooltip={t('moonpay.restricted.region')} /> )} - {!shouldDisableNFTRoutes && ( + {!shouldDisableNFTRoutes && !forAggregatorEnabled && ( } @@ -231,6 +248,14 @@ export default function AuthenticatedHeader({ account, openSettings }: { account onClick={navigateToProfile} /> )} + {forAggregatorEnabled && ( + } + name={t('common.receive')} + onClick={() => undefined} // TODO: implement when recieve modal is implemented + /> + )} {isUnclaimed && ( diff --git a/apps/web/src/components/AccountDrawer/IconButton.tsx b/apps/web/src/components/AccountDrawer/IconButton.tsx index e9aef490b94..6fce4ae3e9b 100644 --- a/apps/web/src/components/AccountDrawer/IconButton.tsx +++ b/apps/web/src/components/AccountDrawer/IconButton.tsx @@ -46,7 +46,9 @@ const IconStyles = css<{ hideHorizontal?: boolean }>` } :active { background-color: ${({ theme }) => theme.surface1}; - transition: background-color ${({ theme }) => theme.transition.duration.fast} linear, ${getWidthTransition}; + transition: + background-color ${({ theme }) => theme.transition.duration.fast} linear, + ${getWidthTransition}; } ` @@ -114,7 +116,8 @@ const TextWrapper = styled.div` const TextHide = styled.div` overflow: hidden; - transition: width ${({ theme }) => theme.transition.timing.inOut} ${({ theme }) => theme.transition.duration.fast}, + transition: + width ${({ theme }) => theme.transition.timing.inOut} ${({ theme }) => theme.transition.duration.fast}, max-width ${({ theme }) => theme.transition.timing.inOut} ${({ theme }) => theme.transition.duration.fast}; ` @@ -143,7 +146,7 @@ export const IconWithConfirmTextButton = ({ setShowTextWithoutCallback(val) onShowConfirm?.(val) }, - [onShowConfirm] + [onShowConfirm], ) const dimensionsRef = useRef({ diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityRow.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityRow.tsx index 22d3daac8df..70528d71e31 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityRow.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/ActivityRow.tsx @@ -26,7 +26,10 @@ const ActivityRowDescriptor = styled(ThemedText.BodySmall)` const StyledTimestamp = styled(ThemedText.BodySmall)` color: ${({ theme }) => theme.neutral2}; font-variant: small; - font-feature-settings: 'tnum' on, 'lnum' on, 'ss02' on; + font-feature-settings: + 'tnum' on, + 'lnum' on, + 'ss02' on; ` function StatusIndicator({ activity: { status, timestamp, offchainOrderDetails } }: { activity: Activity }) { diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.test.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.test.tsx index ec4cf3f93d1..5f991b7ee97 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.test.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.test.tsx @@ -52,15 +52,15 @@ describe('CancelOrdersDialog', () => { isVisible={true} orders={[mockOrderDetails]} cancelState={CancellationState.REVIEWING_CANCELLATION} - /> + />, ) expect(document.body).toMatchSnapshot() expect(screen.getByText('Cancel order')).toBeInTheDocument() expect( screen.getByText( - 'Your swap could execute before cancellation is processed. Your network costs cannot be refunded. Do you wish to proceed?' - ) + 'Your swap could execute before cancellation is processed. Your network costs cannot be refunded. Do you wish to proceed?', + ), ).toBeInTheDocument() }) it('should render limit order text', async () => { @@ -73,15 +73,15 @@ describe('CancelOrdersDialog', () => { isVisible={true} orders={[{ ...mockOrderDetails, type: SignatureType.SIGN_LIMIT }]} cancelState={CancellationState.REVIEWING_CANCELLATION} - /> + />, ) expect(document.body).toMatchSnapshot() expect(screen.getByText('Cancel limit')).toBeInTheDocument() expect( screen.getByText( - 'Your swap could execute before cancellation is processed. Your network costs cannot be refunded. Do you wish to proceed?' - ) + 'Your swap could execute before cancellation is processed. Your network costs cannot be refunded. Do you wish to proceed?', + ), ).toBeInTheDocument() }) }) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.tsx index 68f56891815..55e255b6fb5 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/CancelOrdersDialog.tsx @@ -38,7 +38,7 @@ type CancelOrdersDialogProps = Partial void - } + }, ) { const { orders, cancelState, cancelTxHash, onConfirm, onCancel } = props @@ -95,7 +95,7 @@ export function CancelOrdersDialog( const gasEstimate = useCancelOrdersGasEstimate(orders) if ( [CancellationState.PENDING_SIGNATURE, CancellationState.PENDING_CONFIRMATION, CancellationState.CANCELLED].includes( - cancelState + cancelState, ) ) { const cancelSubmitted = diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.test.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.test.tsx index f826ba5e07c..2d9799a9f51 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.test.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.test.tsx @@ -44,7 +44,7 @@ describe('OrderContent', () => { settledOutputCurrencyAmountRaw: '106841079134757921', }, }} - /> + />, ) expect(container).toMatchSnapshot() expect(container).toHaveTextContent('Order executed') @@ -73,7 +73,7 @@ describe('OrderContent', () => { settledOutputCurrencyAmountRaw: '106841079134757921', }, }} - /> + />, ) expect(container).toMatchSnapshot() expect(container).toHaveTextContent('Order pending') @@ -104,7 +104,7 @@ describe('OrderContent', () => { settledOutputCurrencyAmountRaw: '106841079134757921', }, }} - /> + />, ) expect(container).toMatchSnapshot() expect(container).toHaveTextContent('Limit pending') diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx index e1f15365645..34fc4a6c4b9 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx @@ -61,7 +61,7 @@ export function useOpenOffchainActivityModal() { }) setSelectedOrder({ order, logos, modalOpen: true }) }, - [setSelectedOrder] + [setSelectedOrder], ) } @@ -136,7 +136,7 @@ export function useOrderAmounts(order?: UniswapXOrderDetails): inputAmount: CurrencyAmount.fromRawAmount(inputCurrency, swapInfo.inputCurrencyAmountRaw), outputAmount: CurrencyAmount.fromRawAmount( outputCurrency, - swapInfo.settledOutputCurrencyAmountRaw ?? swapInfo.expectedOutputCurrencyAmountRaw + swapInfo.settledOutputCurrencyAmountRaw ?? swapInfo.expectedOutputCurrencyAmountRaw, ), } } else { @@ -214,7 +214,7 @@ export function OrderContent({ const currencies = useMemo( () => [amounts?.inputAmount.currency, amounts?.outputAmount.currency], - [amounts?.inputAmount.currency, amounts?.outputAmount.currency] + [amounts?.inputAmount.currency, amounts?.outputAmount.currency], ) if (!amounts?.inputAmount || !amounts?.outputAmount) { @@ -343,7 +343,7 @@ export function OffchainActivityModal() { }, [setSelectedOrder]) const cancelOrder = useCancelMultipleOrdersCallback( - useMemo(() => [syncedSelectedOrder].filter(Boolean) as Array, [syncedSelectedOrder]) + useMemo(() => [syncedSelectedOrder].filter(Boolean) as Array, [syncedSelectedOrder]), ) return ( diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainOrderLineItem.test.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainOrderLineItem.test.tsx index 21f379de4ad..a6df97b6d6d 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainOrderLineItem.test.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainOrderLineItem.test.tsx @@ -18,7 +18,7 @@ describe('OffchainOrderLineItem', () => { inputAmount: CurrencyAmount.fromRawAmount(DAI, 1), outputAmount: CurrencyAmount.fromRawAmount(USDC_MAINNET, 1), }} - /> + />, ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText('Rate')).toBeInTheDocument() @@ -50,7 +50,7 @@ describe('OffchainOrderLineItem', () => { addedTime: 1, expiry: 2, }} - /> + />, ) expect(screen.getByText('Expiry')).toBeInTheDocument() }) @@ -88,7 +88,7 @@ describe('OffchainOrderLineItem', () => { addedTime: 1, expiry: 2, }} - /> + />, ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText('Transaction ID')).toBeInTheDocument() diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainOrderLineItem.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainOrderLineItem.tsx index 66ca61524c0..c01849fe635 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainOrderLineItem.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainOrderLineItem.tsx @@ -49,7 +49,7 @@ function useLineItem(details: OffchainOrderLineItemProps): LineItemData | undefi details.amounts?.inputAmount.currency, details.amounts?.outputAmount.currency, details.amounts?.inputAmount.quotient, - details.amounts?.outputAmount.quotient + details.amounts?.outputAmount.quotient, ) } /> diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/getCurrency.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/getCurrency.ts index df3674fc9bd..0e5fbe98754 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/getCurrency.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/getCurrency.ts @@ -1,5 +1,6 @@ import { Currency } from '@uniswap/sdk-core' import { SupportedInterfaceChainId, chainIdToBackendChain } from 'constants/chains' +import { COMMON_BASES } from 'constants/routing' import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens' import { apolloClient } from 'graphql/data/apollo/client' import { gqlTokenToCurrencyInfo } from 'graphql/data/types' @@ -8,16 +9,23 @@ import { SimpleTokenQuery, Token, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { isSameAddress } from 'utilities/src/addresses' export async function getCurrency( currencyId: string, - chainId: SupportedInterfaceChainId + chainId: SupportedInterfaceChainId, ): Promise { const isNative = currencyId === NATIVE_CHAIN_ID || currencyId?.toLowerCase() === 'native' || currencyId?.toLowerCase() === 'eth' if (isNative) { return nativeOnChain(chainId) } + const commonBase = chainId + ? COMMON_BASES[chainId]?.find((base) => base.currency.isToken && isSameAddress(base.currency.address, currencyId)) + : undefined + if (commonBase) { + return commonBase.currency + } const { data } = await apolloClient.query({ query: SimpleTokenDocument, variables: { diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/hooks.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/hooks.ts index 9d43256d4b9..21eca429e46 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/hooks.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/hooks.ts @@ -69,7 +69,7 @@ export function useAllActivities(account: string) { const localMap = useLocalActivities(account) const remoteMap = useMemo( () => parseRemoteActivities(activities, account, formatNumberOrString), - [account, activities, formatNumberOrString] + [account, activities, formatNumberOrString], ) const updateCancelledTx = useTransactionCanceller() @@ -103,7 +103,7 @@ export function useOpenLimitOrders(account: string) { activities?.filter( (activity) => activity.offchainOrderDetails?.type === SignatureType.SIGN_LIMIT && - activity.status === TransactionStatus.Pending + activity.status === TransactionStatus.Pending, ) ?? [] return { openLimitOrders, @@ -137,7 +137,7 @@ export function useCancelOrdersGasEstimate(orders?: UniswapXOrderDetails[]): Gas chainId: orders[0].chainId, } : undefined, - [orders] + [orders], ) const cancelTransaction = useCreateCancelTransactionRequest(cancelTransactionParams) const gasEstimate = useTransactionGasFee(cancelTransaction, GasSpeed.Fast) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/index.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/index.tsx index 08cca223e59..cdedd6901fb 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/index.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/index.tsx @@ -63,7 +63,7 @@ export function ActivityTab({ account }: { account: string }) { {activityGroup.transactions.map( (activity) => - !(hideSpam && activity.isSpam) && + !(hideSpam && activity.isSpam) && , )} diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseLocal.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseLocal.ts index 29cb6503dc6..3033e42c30e 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseLocal.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseLocal.ts @@ -45,7 +45,7 @@ function buildCurrencyDescriptor( currencyB: Currency | undefined, amtB: string, formatNumber: FormatNumberFunctionType, - delimiter = t('for') + delimiter = t('for'), ) { const formattedA = currencyA ? formatNumber({ @@ -67,7 +67,7 @@ function buildCurrencyDescriptor( async function parseSwap( swap: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo, chainId: SupportedInterfaceChainId, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ): Promise> { const [tokenIn, tokenOut] = await Promise.all([ getCurrency(swap.inputCurrencyId, chainId), @@ -89,7 +89,7 @@ function parseWrap( wrap: WrapTransactionInfo, chainId: InterfaceChainId, status: TransactionStatus, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ): Partial { const native = nativeOnChain(chainId) const wrapped = native.wrapped @@ -100,7 +100,7 @@ function parseWrap( wrap.currencyAmountRaw, output, wrap.currencyAmountRaw, - formatNumber + formatNumber, ) const title = getActivityTitle(TransactionType.WRAP, status, wrap.unwrapped) const currencies = wrap.unwrapped ? [wrapped, native] : [native, wrapped] @@ -111,7 +111,7 @@ function parseWrap( async function parseApproval( approval: ApproveTransactionInfo, chainId: SupportedInterfaceChainId, - status: TransactionStatus + status: TransactionStatus, ): Promise> { const currency = await getCurrency(approval.tokenAddress, chainId) const descriptor = currency?.symbol ?? currency?.name ?? t('common.unknown') @@ -119,7 +119,7 @@ async function parseApproval( title: getActivityTitle( TransactionType.APPROVAL, status, - BigNumber.from(approval.amount).eq(0) /* use alternate if it's a revoke */ + BigNumber.from(approval.amount).eq(0) /* use alternate if it's a revoke */, ), descriptor, currencies: [currency], @@ -133,7 +133,7 @@ type GenericLPInfo = Omit< async function parseLP( lp: GenericLPInfo, chainId: SupportedInterfaceChainId, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ): Promise> { const [baseCurrency, quoteCurrency] = await Promise.all([ getCurrency(lp.baseCurrencyId, chainId), @@ -146,7 +146,7 @@ async function parseLP( quoteCurrency, quoteRaw, formatNumber, - t('common.and') + t('common.endAdornment'), ) return { descriptor, currencies: [baseCurrency, quoteCurrency] } @@ -155,7 +155,7 @@ async function parseLP( async function parseCollectFees( collect: CollectFeesTransactionInfo, chainId: SupportedInterfaceChainId, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ): Promise> { // Adapts CollectFeesTransactionInfo to generic LP type const { @@ -167,13 +167,13 @@ async function parseCollectFees( return parseLP( { baseCurrencyId, quoteCurrencyId, expectedAmountBaseRaw, expectedAmountQuoteRaw }, chainId, - formatNumber + formatNumber, ) } async function parseMigrateCreateV3( lp: MigrateV2LiquidityToV3TransactionInfo | CreateV3PoolTransactionInfo, - chainId: SupportedInterfaceChainId + chainId: SupportedInterfaceChainId, ): Promise> { const [baseCurrency, quoteCurrency] = await Promise.all([ getCurrency(lp.baseCurrencyId, chainId), @@ -189,7 +189,7 @@ async function parseMigrateCreateV3( async function parseSend( send: SendTransactionInfo, chainId: SupportedInterfaceChainId, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ): Promise> { const { currencyId, amount, recipient } = send const currency = await getCurrency(currencyId, chainId) @@ -210,7 +210,7 @@ async function parseSend( export async function transactionToActivity( details: TransactionDetails | undefined, chainId: SupportedInterfaceChainId, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ): Promise { if (!details) { return undefined @@ -266,7 +266,7 @@ export async function transactionToActivity( export function getTransactionToActivityQueryOptions( transaction: TransactionDetails | undefined, chainId: SupportedInterfaceChainId, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ) { return queryOptions({ queryKey: ['transactionToActivity', transaction, chainId], @@ -276,7 +276,7 @@ export function getTransactionToActivityQueryOptions( export function getSignatureToActivityQueryOptions( signature: SignatureDetails | undefined, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ) { return queryOptions({ queryKey: ['signatureToActivity', signature], @@ -296,7 +296,7 @@ function convertToSecTimestamp(timestamp: number) { export async function signatureToActivity( signature: SignatureDetails | undefined, - formatNumber: FormatNumberFunctionType + formatNumber: FormatNumberFunctionType, ): Promise { if (!signature) { return undefined diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.test.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.test.tsx index 91ecd7991f2..129bf90e388 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.test.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.test.tsx @@ -181,7 +181,7 @@ describe('parseRemote', () => { ...swapOrderTokenChanges, TokenTransfer: [mockTokenTransferOutPartsFragment], }, - jest.fn().mockReturnValue('100') + jest.fn().mockReturnValue('100'), ) expect(result).toEqual(undefined) }) @@ -195,7 +195,7 @@ describe('parseRemote', () => { ...swapOrderTokenChanges, TokenTransfer: [], }, // no token changes - jest.fn().mockReturnValue('100') + jest.fn().mockReturnValue('100'), ) expect(result).toEqual(undefined) }) @@ -204,7 +204,7 @@ describe('parseRemote', () => { const result = offchainOrderDetailsFromGraphQLTransactionActivity( { ...MockSwapOrder, details: { ...mockTransactionDetailsPartsFragment, __typename: 'TransactionDetails' } }, swapOrderTokenChanges, - jest.fn().mockReturnValue('100') + jest.fn().mockReturnValue('100'), ) expect(result).toEqual({ chainId: 1, diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.tsx index ee7a82408e3..37baf7f18d8 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.tsx @@ -90,7 +90,7 @@ const SPAMMABLE_ACTIVITY_TYPES = [TransactionType.Receive, TransactionType.Mint, function isSpam( { NftTransfer, TokenTransfer }: TransactionChanges, details: TransactionDetailsPartsFragment, - account: string + account: string, ): boolean { if (!SPAMMABLE_ACTIVITY_TYPES.includes(details.type) || details.from === account) { return false @@ -108,13 +108,16 @@ function callsPositionManagerContract(assetActivity: TransactionActivity) { // Gets counts for number of NFTs in each collection present function getCollectionCounts(nftTransfers: NftTransferPartsFragment[]): { [key: string]: number | undefined } { - return nftTransfers.reduce((acc, NFTChange) => { - const key = NFTChange.asset.collection?.name ?? NFTChange.asset.name - if (key) { - acc[key] = (acc?.[key] ?? 0) + 1 - } - return acc - }, {} as { [key: string]: number | undefined }) + return nftTransfers.reduce( + (acc, NFTChange) => { + const key = NFTChange.asset.collection?.name ?? NFTChange.asset.name + if (key) { + acc[key] = (acc?.[key] ?? 0) + 1 + } + return acc + }, + {} as { [key: string]: number | undefined }, + ) } function getSwapTitle(sent: TokenTransferPartsFragment, received: TokenTransferPartsFragment): string | undefined { @@ -187,12 +190,12 @@ type SwapAmounts = { // eslint-disable-next-line import/no-unused-modules export function parseSwapAmounts( changes: TransactionChanges, - formatNumberOrString: FormatNumberOrStringFunctionType + formatNumberOrString: FormatNumberOrStringFunctionType, ): SwapAmounts | undefined { const sent = changes.TokenTransfer.find((t) => t.direction === 'OUT') // Any leftover native token is refunded on exact_out swaps where the input token is native const refund = changes.TokenTransfer.find( - (t) => t.direction === 'IN' && t.asset.id === sent?.asset.id && t.asset.standard === NATIVE_CHAIN_ID + (t) => t.direction === 'IN' && t.asset.id === sent?.asset.id && t.asset.standard === NATIVE_CHAIN_ID, ) const received = changes.TokenTransfer.find((t) => t.direction === 'IN' && t !== refund) if (!sent || !received) { @@ -271,12 +274,12 @@ function parseLend(changes: TransactionChanges, formatNumberOrString: FormatNumb function parseSwapOrder( changes: TransactionChanges, formatNumberOrString: FormatNumberOrStringFunctionType, - assetActivity: TransactionActivity + assetActivity: TransactionActivity, ) { const offchainOrderDetails = offchainOrderDetailsFromGraphQLTransactionActivity( assetActivity, changes, - formatNumberOrString + formatNumberOrString, ) return { ...parseSwap(changes, formatNumberOrString), @@ -288,7 +291,7 @@ function parseSwapOrder( export function offchainOrderDetailsFromGraphQLTransactionActivity( activity: AssetActivityPartsFragment & { details: TransactionDetailsPartsFragment }, changes: TransactionChanges, - formatNumberOrString: FormatNumberOrStringFunctionType + formatNumberOrString: FormatNumberOrStringFunctionType, ): UniswapXOrderDetails | undefined { const chainId = supportedChainIdFromGQLChain(activity.chain) if (!activity || !activity.details || !chainId) { @@ -357,7 +360,7 @@ type TransactionActivity = AssetActivityPartsFragment & { details: TransactionDe function parseSendReceive( changes: TransactionChanges, formatNumberOrString: FormatNumberOrStringFunctionType, - assetActivity: TransactionActivity + assetActivity: TransactionActivity, ) { // TODO(cartcrom): remove edge cases after backend implements // Edge case: Receiving two token transfers in interaction w/ V3 manager === removing liquidity. These edge cases should potentially be moved to backend @@ -415,7 +418,7 @@ function parseSendReceive( function parseMint( changes: TransactionChanges, formatNumberOrString: FormatNumberOrStringFunctionType, - assetActivity: TransactionActivity + assetActivity: TransactionActivity, ) { const collectionMap = getCollectionCounts(changes.NftTransfer) if (Object.keys(collectionMap).length === 1) { @@ -433,7 +436,7 @@ function parseMint( function parseUnknown( _changes: TransactionChanges, _formatNumberOrString: FormatNumberOrStringFunctionType, - assetActivity: TransactionActivity + assetActivity: TransactionActivity, ) { return { title: t('common.contractInteraction'), ...COMMON_CONTRACTS[assetActivity.details.to.toLowerCase()] } } @@ -441,7 +444,7 @@ function parseUnknown( type TransactionTypeParser = ( changes: TransactionChanges, formatNumberOrString: FormatNumberOrStringFunctionType, - assetActivity: TransactionActivity + assetActivity: TransactionActivity, ) => Partial const ActivityParserByType: { [key: string]: TransactionTypeParser | undefined } = { [TransactionType.Swap]: parseSwap, @@ -503,7 +506,7 @@ function parseUniswapXOrder(activity: OrderActivity): Activity | undefined { function parseRemoteActivity( assetActivity: AssetActivityPartsFragment | undefined, account: string, - formatNumberOrString: FormatNumberOrStringFunctionType + formatNumberOrString: FormatNumberOrStringFunctionType, ): Activity | undefined { try { if (!assetActivity) { @@ -535,7 +538,7 @@ function parseRemoteActivity( return acc }, - { NftTransfer: [], TokenTransfer: [], TokenApproval: [], NftApproval: [], NftApproveForAll: [] } + { NftTransfer: [], TokenTransfer: [], TokenApproval: [], NftApproval: [], NftApproveForAll: [] }, ) const supportedChain = supportedChainIdFromGQLChain(assetActivity.chain) @@ -566,7 +569,7 @@ function parseRemoteActivity( const parsedFields = ActivityParserByType[assetActivity.details.type]?.( changes, formatNumberOrString, - assetActivity as TransactionActivity + assetActivity as TransactionActivity, ) return { ...defaultFields, ...parsedFields } } catch (e) { @@ -581,7 +584,7 @@ function parseRemoteActivity( export function parseRemoteActivities( assetActivities: (AssetActivityPartsFragment | undefined)[] | undefined, account: string, - formatNumberOrString: FormatNumberOrStringFunctionType + formatNumberOrString: FormatNumberOrStringFunctionType, ) { return assetActivities?.reduce((acc: { [hash: string]: Activity }, assetActivity) => { const activity = parseRemoteActivity(assetActivity, account, formatNumberOrString) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/utils.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/utils.ts index 25df5acf9d2..2fd81d01218 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/utils.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/utils.ts @@ -94,20 +94,20 @@ export const createGroups = (activities: Array = [], hideSpam = false) function getCancelMultipleUniswapXOrdersParams( orders: Array<{ encodedOrder: string; type: SignatureType }>, - chainId: InterfaceChainId + chainId: InterfaceChainId, ) { const nonces = orders .map(({ encodedOrder, type }) => type === SignatureType.SIGN_UNISWAPX_V2_ORDER ? CosignedV2DutchOrder.parse(encodedOrder, chainId) - : DutchOrder.parse(encodedOrder, chainId) + : DutchOrder.parse(encodedOrder, chainId), ) .map((order) => order.info.nonce) return getCancelMultipleParams(nonces) } export function useCancelMultipleOrdersCallback( - orders?: Array + orders?: Array, ): () => Promise { const provider = useEthersWeb3Provider() const permit2 = useContract(permit2Address(orders?.[0]?.chainId), PERMIT2_ABI, true) @@ -173,7 +173,7 @@ async function cancelMultipleUniswapXOrders({ async function getCancelMultipleUniswapXOrdersTransaction( orders: Array<{ encodedOrder: string; type: SignatureType }>, chainId: InterfaceChainId, - permit2: Permit2 + permit2: Permit2, ): Promise { const cancelParams = getCancelMultipleUniswapXOrdersParams(orders, chainId) if (!permit2 || cancelParams.length === 0) { @@ -201,7 +201,7 @@ export function useCreateCancelTransactionRequest( orders: Array<{ encodedOrder: string; type: SignatureType }> chainId: InterfaceChainId } - | undefined + | undefined, ): TransactionRequest | undefined { const permit2 = useContract(permit2Address(params?.chainId), PERMIT2_ABI, true) const transactionFetcher = useCallback(() => { diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitDetailActivityRow.test.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitDetailActivityRow.test.tsx index eb02d439866..f3e93e9ed1c 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitDetailActivityRow.test.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitDetailActivityRow.test.tsx @@ -58,7 +58,7 @@ describe('LimitDetailActivityRow', () => { order={{ ...mockOrder, offchainOrderDetails: undefined }} onToggleSelect={jest.fn()} selected={false} - /> + />, ) expect(container.firstChild?.firstChild?.firstChild).toBeNull() }) @@ -69,14 +69,14 @@ describe('LimitDetailActivityRow', () => { onToggleSelect={jest.fn()} selected={false} order={{ ...mockOrder, offchainOrderDetails: { ...mockOrderDetails, swapInfo: undefined as any } }} - /> + />, ) expect(container.firstChild?.firstChild?.firstChild).toBeNull() }) it('should render with valid details', () => { const { container } = render( - + , ) expect(container.firstChild).toMatchSnapshot() expect(screen.getByText('when 0.00042 WETH/DAI')).toBeInTheDocument() diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitDetailActivityRow.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitDetailActivityRow.tsx index 6d837625863..39e30ed4093 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitDetailActivityRow.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitDetailActivityRow.tsx @@ -70,8 +70,8 @@ export function LimitDetailActivityRow({ order, onToggleSelect, selected }: Limi return tradePrice.quote( CurrencyAmount.fromRawAmount( amounts.inputAmount.currency, - parseUnits('1', amounts.inputAmount.currency.decimals).toString() - ) + parseUnits('1', amounts.inputAmount.currency.decimals).toString(), + ), ) }, [amounts?.inputAmount, amounts?.outputAmount, amountsDefined]) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/OpenLimitOrdersButton.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/OpenLimitOrdersButton.tsx index 0602ba2d482..bb0ee3ae8ef 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/OpenLimitOrdersButton.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/OpenLimitOrdersButton.tsx @@ -1,20 +1,8 @@ import { useOpenLimitOrders } from 'components/AccountDrawer/MiniPortfolio/Activity/hooks' -import Column from 'components/Column' -import { TimeForwardIcon } from 'components/Icons/TimeForward' -import Row from 'components/Row' +import { TabButton } from 'components/AccountDrawer/MiniPortfolio/shared' import { Plural, Trans, t } from 'i18n' -import { ChevronRight } from 'react-feather' -import styled, { useTheme } from 'styled-components' -import { ClickableStyle, ThemedText } from 'theme/components' - -const Container = styled.button` - border-radius: 16px; - border: none; - background: ${({ theme }) => theme.surface2}; - padding: 12px 16px; - margin-top: 12px; - ${ClickableStyle} -` +import { Clock } from 'react-feather' +import { useTheme } from 'styled-components' function getExtraWarning(openLimitOrders: any[]) { if (openLimitOrders.length >= 100) { @@ -40,27 +28,25 @@ export function OpenLimitOrdersButton({ const { openLimitOrders } = useOpenLimitOrders(account) const theme = useTheme() const extraWarning = getExtraWarning(openLimitOrders) + if (!openLimitOrders || openLimitOrders.length < 1) { return null } + return ( - - - - - - - - - {extraWarning && {extraWarning}} - - - - - + + } + icon={} + extraWarning={extraWarning} + onClick={openLimitsMenu} + disabled={disabled} + className={className} + /> ) } diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/__snapshots__/OpenLimitOrdersButton.test.tsx.snap b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/__snapshots__/OpenLimitOrdersButton.test.tsx.snap index 2980208014c..da72cda05b3 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/__snapshots__/OpenLimitOrdersButton.test.tsx.snap +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/__snapshots__/OpenLimitOrdersButton.test.tsx.snap @@ -56,14 +56,6 @@ exports[`OpenLimitOrdersButton should render if there are open limit orders 1`] gap: 12px; } -.c5 { - color: #222222; - -webkit-letter-spacing: -0.01em; - -moz-letter-spacing: -0.01em; - -ms-letter-spacing: -0.01em; - letter-spacing: -0.01em; -} - .c0 { border-radius: 16px; border: none; @@ -102,40 +94,55 @@ exports[`OpenLimitOrdersButton should render if there are open limit orders 1`] class="c1 c3" > - +

-
1 open limit -
+
+ diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/NFTs/NFTItem.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/NFTs/NFTItem.tsx index a078d560417..3e5f5e6bfaf 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/NFTs/NFTItem.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/NFTs/NFTItem.tsx @@ -2,6 +2,8 @@ import { InterfaceElementName, SharedEventName } from '@uniswap/analytics-events import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' import Column from 'components/Column' import Row from 'components/Row' +import { MouseFollowTooltip, TooltipSize } from 'components/Tooltip' +import { t } from 'i18next' import { Box } from 'nft/components/Box' import { NftCard } from 'nft/components/card' import { detailsHref } from 'nft/components/card/utils' @@ -10,6 +12,8 @@ import { WalletAsset } from 'nft/types' import { useNavigate } from 'react-router-dom' import styled from 'styled-components' import { ThemedText } from 'theme/components' +import { capitalize } from 'tsafe' +import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { useTrace } from 'utilities/src/telemetry/trace/TraceContext' import { NumberType, useFormatter } from 'utils/formatNumbers' @@ -52,32 +56,44 @@ export function NFT({ const trace = useTrace() const navigateToNFTDetails = () => { - accountDrawer.close() - navigate(detailsHref(asset)) + if (asset.chain === Chain.Ethereum) { + accountDrawer.close() + navigate(detailsHref(asset)) + } } return ( - - sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, { - element: InterfaceElementName.MINI_PORTFOLIO_NFT_ITEM, - collection_name: asset.collection?.name, - collection_address: asset.collection?.address, - token_id: asset.tokenId, - ...trace, - }) - } - mediaShouldBePlaying={mediaShouldBePlaying} - setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} - testId="mini-portfolio-nft" - /> + + + sendAnalyticsEvent(SharedEventName.ELEMENT_CLICKED, { + element: InterfaceElementName.MINI_PORTFOLIO_NFT_ITEM, + collection_name: asset.collection?.name, + collection_address: asset.collection?.address, + token_id: asset.tokenId, + ...trace, + }) + } + mediaShouldBePlaying={mediaShouldBePlaying} + setCurrentTokenPlayingMedia={setCurrentTokenPlayingMedia} + testId="mini-portfolio-nft" + /> + ) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/NFTs/index.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/NFTs/index.tsx index ebf0dd383e3..deb977c21a9 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/NFTs/index.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/NFTs/index.tsx @@ -1,28 +1,53 @@ import { NFT } from 'components/AccountDrawer/MiniPortfolio/NFTs/NFTItem' import { DEFAULT_NFT_QUERY_AMOUNT } from 'components/AccountDrawer/MiniPortfolio/constants' import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' +import { TabButton } from 'components/AccountDrawer/MiniPortfolio/shared' import { useNftBalance } from 'graphql/data/nft/NftBalance' +import { t } from 'i18n' import { LoadingAssets } from 'nft/components/collection/CollectionAssetLoading' import { EmptyWalletModule } from 'nft/components/profile/view/EmptyWalletContent' -import { useState } from 'react' +import { useProfilePageState, useSellAsset, useWalletCollections } from 'nft/hooks' +import { ProfilePageStateType } from 'nft/types' +import { useCallback, useState } from 'react' import InfiniteScroll from 'react-infinite-scroll-component' +import { useNavigate } from 'react-router-dom' import styled from 'styled-components' +import { Gallery } from 'ui/src/components/icons' +import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { FeatureFlags } from 'uniswap/src/features/gating/flags' +import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' + +const StyledTabButton = styled(TabButton)` + width: calc(100% - 32px); + margin: 0 16px -4px; +` export default function NFTs({ account }: { account: string }) { + const forAggregatorEnabled = useFeatureFlag(FeatureFlags.ForAggregatorWeb) const accountDrawer = useAccountDrawer() - const { walletAssets, loading, hasNext, loadMore } = useNftBalance( - account, - [], - [], - DEFAULT_NFT_QUERY_AMOUNT, - undefined, - undefined, - undefined, - !accountDrawer.isOpen - ) + const navigate = useNavigate() + const setSellPageState = useProfilePageState((state) => state.setProfilePageState) + const resetSellAssets = useSellAsset((state) => state.reset) + const clearCollectionFilters = useWalletCollections((state) => state.clearCollectionFilters) + + const isL2NFTsEnabled = useFeatureFlag(FeatureFlags.L2NFTs) + const { walletAssets, loading, hasNext, loadMore } = useNftBalance({ + ownerAddress: account, + first: DEFAULT_NFT_QUERY_AMOUNT, + skip: !accountDrawer.isOpen, + chains: isL2NFTsEnabled ? [Chain.Ethereum, Chain.Zora] : undefined, + }) const [currentTokenPlayingMedia, setCurrentTokenPlayingMedia] = useState() + const navigateToProfile = useCallback(() => { + accountDrawer.close() + resetSellAssets() + setSellPageState(ProfilePageStateType.VIEWING) + clearCollectionFilters() + navigate('/nfts/profile') + }, [clearCollectionFilters, navigate, resetSellAssets, setSellPageState, accountDrawer]) + if (loading && !walletAssets) { return ( @@ -36,35 +61,44 @@ export default function NFTs({ account }: { account: string }) { } return ( - - - - ) - } - dataLength={walletAssets?.length ?? 0} - style={{ overflow: 'unset' }} - scrollableTarget="wallet-dropdown-scroll-wrapper" - > - - {walletAssets?.length - ? walletAssets.map((asset, index) => { - return ( - - ) - }) - : null} - - + <> + {forAggregatorEnabled && ( + } + onClick={navigateToProfile} + /> + )} + + + + ) + } + dataLength={walletAssets?.length ?? 0} + style={{ overflow: 'unset' }} + scrollableTarget="wallet-dropdown-scroll-wrapper" + > + + {walletAssets?.length + ? walletAssets.map((asset, index) => { + return ( + + ) + }) + : null} + + + ) } diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/cache.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/cache.ts index a24cb418210..f339e578793 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/cache.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/cache.ts @@ -49,10 +49,10 @@ export function useCachedPositions(account: string): UseCachedPositionsReturnTyp return cache } }), - POSITION_CACHE_EXPIRY + POSITION_CACHE_EXPIRY, ) }, - [account, setCachedPositions] + [account, setCachedPositions], ) return [cachedPositions[account], setPositionsAndStaleTimeout] } @@ -70,12 +70,12 @@ export function usePoolAddressCache() { const [cache, updateCache] = useAtom(poolAddressCacheAtom) const get = useCallback( (details: PositionDetails, chainId: InterfaceChainId) => cache[poolAddressKey(details, chainId)], - [cache] + [cache], ) const set = useCallback( (details: PositionDetails, chainId: InterfaceChainId, address: string) => updateCache((c) => ({ ...c, [poolAddressKey(details, chainId)]: address })), - [updateCache] + [updateCache], ) return { get, set } } @@ -89,7 +89,7 @@ function useTokenCache() { const entry = cache[buildCurrencyKey(chainId, address)] return entry ? deserializeToken(entry) : undefined }, - [cache] + [cache], ) const set = useCallback( (token?: Token) => { @@ -97,7 +97,7 @@ function useTokenCache() { setCache((cache) => ({ ...cache, [currencyKey(token)]: serializeToken(token) })) } }, - [setCache] + [setCache], ) return { get, set } } @@ -114,7 +114,7 @@ export function useGetCachedTokens(chains: InterfaceChainId[]): TokenGetterFn { Object.values(fetched).forEach(tokenCache.set) return fetched }, - [multicallContracts, tokenCache] + [multicallContracts, tokenCache], ) // Uses tokens from local state if available, otherwise fetches them @@ -130,7 +130,7 @@ export function useGetCachedTokens(chains: InterfaceChainId[]): TokenGetterFn { const fetched = await fetchRemoteTokens([...missing], chainId) return { ...local, ...fetched } }, - [fetchRemoteTokens, tokenCache] + [fetchRemoteTokens, tokenCache], ) return getTokens diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/getTokensAsync.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/getTokensAsync.ts index a531adb61ca..ab807df7b58 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/getTokensAsync.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/getTokensAsync.ts @@ -46,13 +46,13 @@ function tryParseToken(address: string, chainId: InterfaceChainId, data: CallRes const name = nameData.success ? (Erc20.decodeFunctionResult('name', nameData.returnData)[0] as string) : nameDataBytes32.success - ? (Erc20Bytes32.decodeFunctionResult('name', nameDataBytes32.returnData)[0] as string) - : undefined + ? (Erc20Bytes32.decodeFunctionResult('name', nameDataBytes32.returnData)[0] as string) + : undefined const symbol = symbolData.success ? (Erc20.decodeFunctionResult('symbol', symbolData.returnData)[0] as string) : symbolDataBytes32.success - ? (Erc20Bytes32.decodeFunctionResult('symbol', symbolDataBytes32.returnData)[0] as string) - : undefined + ? (Erc20Bytes32.decodeFunctionResult('symbol', symbolDataBytes32.returnData)[0] as string) + : undefined const decimals = decimalsData.success ? parseInt(decimalsData.returnData) : DEFAULT_ERC20_DECIMALS return new Token(chainId, address, decimals, symbol, name) @@ -94,7 +94,7 @@ const TokenPromiseCache: { [key: CurrencyKey]: Promise | unde export async function getTokensAsync( addresses: string[], chainId: InterfaceChainId, - multicall: UniswapInterfaceMulticall + multicall: UniswapInterfaceMulticall, ): Promise { if (addresses.length === 0) { return {} @@ -123,7 +123,7 @@ export async function getTokensAsync( // Caches tokens currently being fetched for further calls to use formattedAddresses.forEach( (address) => - (TokenPromiseCache[buildCurrencyKey(chainId, address)] = calledTokens.then((tokenMap) => tokenMap[address])) + (TokenPromiseCache[buildCurrencyKey(chainId, address)] = calledTokens.then((tokenMap) => tokenMap[address])), ) const tokenMap = await calledTokens diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts index 39082df73ee..1ad6803f1db 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts @@ -30,7 +30,7 @@ type ContractMap = { [key: number]: T } export function useContractMultichain( addressMap: AddressMap, ABI: any, - chainIds?: InterfaceChainId[] + chainIds?: InterfaceChainId[], ): ContractMap { const account = useAccount() const { provider: walletProvider } = useWeb3React() @@ -48,8 +48,8 @@ export function useContractMultichain( walletProvider && account.chainId === chainId ? walletProvider : isSupportedChain(chainId) - ? RPC_PROVIDERS[chainId] - : undefined + ? RPC_PROVIDERS[chainId] + : undefined if (provider) { acc[chainId] = getContract(addressMap[chainId] ?? '', ABI, provider) as T } @@ -91,7 +91,7 @@ export function usePoolPriceMap(positions: PositionInfo[] | undefined) { } return acc }, {}) ?? {}, - [data?.tokens] + [data?.tokens], ) return { priceMap, pricesLoading: loading && !data } diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx index ddf01b2ceaf..5edea6ccc1a 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/index.tsx @@ -37,14 +37,14 @@ function useFilterPossiblyMaliciousPositionInfo(positions: PositionInfo[] | unde positions ? positions.reduce((acc, position) => ({ ...acc, [position.details.tokenId.toString()]: position }), {}) : {}, - [positions] + [positions], ) const positionDetails = useMemo(() => positions?.map((position) => position.details) ?? [], [positions]) const filteredPositionDetails = useFilterPossiblyMaliciousPositions(positionDetails) return useMemo( () => filteredPositionDetails.map((positionDetails) => tokenIdsToPositionInfo[positionDetails.tokenId.toString()]), - [filteredPositionDetails, tokenIdsToPositionInfo] + [filteredPositionDetails, tokenIdsToPositionInfo], ) } @@ -149,7 +149,7 @@ function PositionListItem({ positionInfo }: { positionInfo: PositionInfo }) { pool_token_0_address: pool.token0.address, pool_token_1_address: pool.token1.address, }), - [chainId, pool.token0.address, pool.token0.symbol, pool.token1.address, pool.token1.symbol] + [chainId, pool.token0.address, pool.token0.symbol, pool.token1.address, pool.token1.symbol], ) return ( diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions.tsx index 4e5749a2b65..38314513aeb 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions.tsx @@ -32,7 +32,7 @@ function createPositionInfo( details: PositionDetails, slot0: any, tokenA: Token, - tokenB: Token + tokenB: Token, ): PositionInfo { /* Instantiates a Pool with a hardcoded 0 liqudity value since the sdk only uses this value for swap state and this avoids an RPC fetch */ const pool = new Pool(tokenA, tokenB, details.fee, slot0.sqrtPriceX96.toString(), 0, slot0.tick) @@ -87,27 +87,30 @@ export default function useMultiChainPositions(account: string, chains = DEFAULT const callData = positionIds.map((id) => pm.interface.encodeFunctionData('collect', [ { tokenId: id, recipient: account, amount0Max: MAX_UINT128, amount1Max: MAX_UINT128 }, - ]) + ]), + ) + const fees = (await pm.callStatic.multicall(callData)).reduce( + (acc, feeBytes, index) => { + const key = chainId.toString() + positionIds[index] + acc[key] = pm.interface.decodeFunctionResult('collect', feeBytes) as FeeAmounts + return acc + }, + {} as { [key: string]: FeeAmounts }, ) - const fees = (await pm.callStatic.multicall(callData)).reduce((acc, feeBytes, index) => { - const key = chainId.toString() + positionIds[index] - acc[key] = pm.interface.decodeFunctionResult('collect', feeBytes) as FeeAmounts - return acc - }, {} as { [key: string]: FeeAmounts }) setFeeMap((prev) => ({ ...prev, ...fees })) }, - [account] + [account], ) const fetchPositionIds = useCallback( async (pm: NonfungiblePositionManager, balance: BigNumber) => { const callData = Array.from({ length: balance.toNumber() }, (_, i) => - pm.interface.encodeFunctionData('tokenOfOwnerByIndex', [account, i]) + pm.interface.encodeFunctionData('tokenOfOwnerByIndex', [account, i]), ) return (await pm.callStatic.multicall(callData)).map((idByte) => BigNumber.from(idByte)) }, - [account] + [account], ) const fetchPositionDetails = useCallback(async (pm: NonfungiblePositionManager, positionIds: BigNumber[]) => { @@ -117,7 +120,7 @@ export default function useMultiChainPositions(account: string, chains = DEFAULT ({ ...pm.interface.decodeFunctionResult('positions', positionBytes), tokenId: positionIds[index], - } as unknown as PositionDetails) + }) as unknown as PositionDetails, ) }, []) @@ -127,7 +130,7 @@ export default function useMultiChainPositions(account: string, chains = DEFAULT const poolInterface = new Interface(IUniswapV3PoolStateJSON.abi) as UniswapV3PoolInterface const tokens = await getTokens( positionDetails.flatMap((details) => [details.token0, details.token1]), - chainId + chainId, ) const calls: Call[] = [] @@ -166,7 +169,7 @@ export default function useMultiChainPositions(account: string, chains = DEFAULT return acc }, []) }, - [account, poolAddressCache, getTokens] + [account, poolAddressCache, getTokens], ) const fetchPositionsForChain = useCallback( @@ -197,7 +200,7 @@ export default function useMultiChainPositions(account: string, chains = DEFAULT return [] } }, - [account, fetchPositionDetails, fetchPositionFees, fetchPositionIds, fetchPositionInfo, pms, multicalls] + [account, fetchPositionDetails, fetchPositionFees, fetchPositionIds, fetchPositionInfo, pms, multicalls], ) const fetchAllPositions = useCallback(async () => { @@ -241,7 +244,7 @@ export default function useMultiChainPositions(account: string, chains = DEFAULT const prices = [priceMap[currencyKey(position.pool.token0)], priceMap[currencyKey(position.pool.token1)]] return { ...position, fees, prices } as PositionInfo }), - [feeMap, positions, priceMap] + [feeMap, positions, priceMap], ) return { positions: positionsWithFeesAndPrices, loading: pricesLoading || positionsLoading } diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/PortfolioLogo.test.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/PortfolioLogo.test.tsx index 4f0f9818198..08933d9bf7e 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/PortfolioLogo.test.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/PortfolioLogo.test.tsx @@ -14,7 +14,7 @@ describe('PortfolioLogo', () => { it('renders with L2 icon', () => { const { container } = render( - + , ) expect(container).toMatchSnapshot() }) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx index fec861d1c81..73467be9f9e 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Tokens/index.tsx @@ -37,7 +37,7 @@ export default function Tokens() { const { visibleTokens, hiddenTokens } = useMemo( () => splitHiddenTokens(tokenBalances ?? [], { hideSmallBalances, hideSpam }), - [hideSmallBalances, tokenBalances, hideSpam] + [hideSmallBalances, tokenBalances, hideSpam], ) if (!data) { @@ -55,12 +55,12 @@ export default function Tokens() { {visibleTokens.map( (tokenBalance) => - tokenBalance.token && + tokenBalance.token && , )} {hiddenTokens.map( (tokenBalance) => - tokenBalance.token && + tokenBalance.token && , )} diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/shared.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/shared.tsx new file mode 100644 index 00000000000..bd8aaff7e91 --- /dev/null +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/shared.tsx @@ -0,0 +1,46 @@ +import Column from 'components/Column' +import Row from 'components/Row' +import { ReactNode } from 'react' +import { ArrowRight } from 'react-feather' +import styled, { useTheme } from 'styled-components' +import { ClickableStyle, ThemedText } from 'theme/components' +import { Text } from 'ui/src' + +const Container = styled.button` + border-radius: 16px; + border: none; + background: ${({ theme }) => theme.surface2}; + padding: 12px 16px; + margin-top: 12px; + ${ClickableStyle} +` + +interface TabButtonProps { + text: ReactNode + icon: ReactNode + extraWarning?: ReactNode + onClick: () => void + disabled?: boolean + className?: string +} + +export function TabButton({ text, icon, extraWarning, onClick, disabled, className }: TabButtonProps) { + const theme = useTheme() + + return ( + + + + {icon} + + + {text} + + {extraWarning && {extraWarning}} + + + + + + ) +} diff --git a/apps/web/src/components/AccountDrawer/SettingsToggle.test.tsx b/apps/web/src/components/AccountDrawer/SettingsToggle.test.tsx index 8e7ac8f8f2f..67f292247f0 100644 --- a/apps/web/src/components/AccountDrawer/SettingsToggle.test.tsx +++ b/apps/web/src/components/AccountDrawer/SettingsToggle.test.tsx @@ -12,7 +12,7 @@ describe('SettingsToggle', () => { description="Test description" isActive={mockActive} toggle={mockToggle} - /> + />, ) expect(mockActive).toBeFalsy() diff --git a/apps/web/src/components/AccountDrawer/UniwalletModal.tsx b/apps/web/src/components/AccountDrawer/UniwalletModal.tsx index 960b07d1a11..1cb6c337a0d 100644 --- a/apps/web/src/components/AccountDrawer/UniwalletModal.tsx +++ b/apps/web/src/components/AccountDrawer/UniwalletModal.tsx @@ -69,6 +69,8 @@ export default function UniwalletModal() { useEffect(() => { if (open) { sendAnalyticsEvent(InterfaceEventName.UNIWALLET_CONNECT_MODAL_OPENED) + } else { + setUri(undefined) } }, [open]) diff --git a/apps/web/src/components/AddressInputPanel/index.tsx b/apps/web/src/components/AddressInputPanel/index.tsx index 0ff2071584b..74f7c724031 100644 --- a/apps/web/src/components/AddressInputPanel/index.tsx +++ b/apps/web/src/components/AddressInputPanel/index.tsx @@ -24,7 +24,8 @@ const ContainerRow = styled.div<{ error: boolean }>` align-items: center; border-radius: 1.25rem; border: 1px solid ${({ error, theme }) => (error ? theme.critical : theme.surface3)}; - transition: border-color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')}, + transition: + border-color 300ms ${({ error }) => (error ? 'step-end' : 'step-start')}, color 500ms ${({ error }) => (error ? 'step-end' : 'step-start')}; background-color: ${({ theme }) => theme.surface1}; ` @@ -95,7 +96,7 @@ export default function AddressInputPanel({ const withoutSpaces = input.replace(/\s+/g, '') onChange(withoutSpaces) }, - [onChange] + [onChange], ) const error = Boolean(value.length > 0 && !loading && !address) diff --git a/apps/web/src/components/AnimatedSlider/index.tsx b/apps/web/src/components/AnimatedSlider/index.tsx new file mode 100644 index 00000000000..7a5ba73215f --- /dev/null +++ b/apps/web/src/components/AnimatedSlider/index.tsx @@ -0,0 +1,47 @@ +import { Children, ReactNode } from 'react' +import { AnimatePresence, Flex, styled } from 'ui/src' + +type SlideDirection = 'forward' | 'backward' + +const AnimatedItem = styled(Flex, { + zIndex: 100, + x: 0, + opacity: 1, + width: '100%', + + variants: { + going: { + ':string': (going: string) => ({ + enterStyle: { + x: going === 'forward' ? 10 : -10, + opacity: 0, + }, + exitStyle: { + zIndex: 0, + x: going === 'forward' ? -10 : 10, + opacity: 0, + }, + }), + }, + } as const, +}) + +export function AnimatedSlider({ + currentIndex, + slideDirection = 'forward', + children, +}: { + currentIndex: number + children: ReactNode + slideDirection?: SlideDirection +}) { + const arrayChildren = Children.toArray(children) + + return ( + + + {arrayChildren[currentIndex]} + + + ) +} diff --git a/apps/web/src/components/BreadcrumbNav/index.test.tsx b/apps/web/src/components/BreadcrumbNav/index.test.tsx index a33299e783d..9758a3f0aad 100644 --- a/apps/web/src/components/BreadcrumbNav/index.test.tsx +++ b/apps/web/src/components/BreadcrumbNav/index.test.tsx @@ -15,7 +15,7 @@ describe('BreadcrumbNav', () => { symbol: 'WBTC', }) const { asFragment } = render( - + , ) expect(asFragment()).toMatchSnapshot() diff --git a/apps/web/src/components/Button/GetHelp.tsx b/apps/web/src/components/Button/GetHelp.tsx index 14e3a37b832..e09be814654 100644 --- a/apps/web/src/components/Button/GetHelp.tsx +++ b/apps/web/src/components/Button/GetHelp.tsx @@ -24,9 +24,9 @@ const StyledExternalLink = styled(ExternalLink)` } stroke: none; ` -export default function GetHelp() { +export default function GetHelp({ url }: { url?: string }) { return ( - + diff --git a/apps/web/src/components/Button/index.tsx b/apps/web/src/components/Button/index.tsx index 8d751d77aa1..67b6207650c 100644 --- a/apps/web/src/components/Button/index.tsx +++ b/apps/web/src/components/Button/index.tsx @@ -530,7 +530,7 @@ type ThemeButtonRef = HTMLButtonElement export const ThemeButton = forwardRef(function ThemeButton( { children, ...rest }, - ref + ref, ) { return ( diff --git a/apps/web/src/components/Charts/ChartModel.tsx b/apps/web/src/components/Charts/ChartModel.tsx index 9a1dff5f76b..9a34b582f53 100644 --- a/apps/web/src/components/Charts/ChartModel.tsx +++ b/apps/web/src/components/Charts/ChartModel.tsx @@ -146,7 +146,7 @@ export abstract class ChartModel { /** Updates the chart without re-creating it or resetting pan/zoom. */ public updateOptions( { locale, theme, format, isLargeScreen, onCrosshairMove }: ChartModelParams, - nonDefaultChartOptions?: DeepPartial + nonDefaultChartOptions?: DeepPartial, ) { this.onCrosshairMove = onCrosshairMove @@ -253,7 +253,7 @@ export function Chart, TDataType e const { md: isLargeScreen } = useScreenSize() const modelParams = useMemo( () => ({ ...params, format, theme, locale, isLargeScreen, onCrosshairMove: setCrosshairData }), - [format, isLargeScreen, locale, params, theme] + [format, isLargeScreen, locale, params, theme], ) // Chart model state should not affect React render cycles since the chart canvas is drawn outside of React, so we store via ref diff --git a/apps/web/src/components/Charts/LiquidityChart/index.tsx b/apps/web/src/components/Charts/LiquidityChart/index.tsx index 08fe61c4f18..9e7369b18ba 100644 --- a/apps/web/src/components/Charts/LiquidityChart/index.tsx +++ b/apps/web/src/components/Charts/LiquidityChart/index.tsx @@ -128,7 +128,7 @@ async function calculateActiveRangeTokensLocked( sqrtPriceX96?: JSBI currentTick?: number liquidity?: JSBI - } + }, ): Promise<{ amount0Locked: number; amount1Locked: number } | undefined> { if (!poolData.currentTick || !poolData.sqrtPriceX96 || !poolData.liquidity) { return undefined @@ -159,7 +159,7 @@ async function calculateActiveRangeTokensLocked( poolData.sqrtPriceX96, tick.liquidityActive, poolData.currentTick, - mockTicks + mockTicks, ) // Calculate amount of token0 that would need to be swapped to reach the bottom of the range const bottomOfRangePrice = TickMath.getSqrtRatioAtTick(mockTicks[0].index) @@ -182,7 +182,7 @@ async function calculateTokensLocked( token0: Token, token1: Token, feeTier: FeeAmount, - tick: TickProcessed + tick: TickProcessed, ): Promise<{ amount0Locked: number; amount1Locked: number }> { try { const tickSpacing = TICK_SPACINGS[feeTier] @@ -300,7 +300,7 @@ export function useLiquidityBarData({ tokenB, feeTier, ticksProcessed[activeRangeIndex], - activePoolData + activePoolData, ) barData[activeRangeIndex] = { ...activeRangeData, ...activeTickTvl } } diff --git a/apps/web/src/components/Charts/LiquidityChart/renderer.tsx b/apps/web/src/components/Charts/LiquidityChart/renderer.tsx index 2b130f0e186..77dfece4b0a 100644 --- a/apps/web/src/components/Charts/LiquidityChart/renderer.tsx +++ b/apps/web/src/components/Charts/LiquidityChart/renderer.tsx @@ -79,7 +79,7 @@ export class LiquidityBarSeriesRenderer implemen this._data.barSpacing, renderingScope.horizontalPixelRatio, this._data.visibleRange.from, - this._data.visibleRange.to + this._data.visibleRange.to, ) const zeroY = priceToCoordinate(0) ?? 0 ctx.fillStyle = this._options.tokenAColor @@ -95,7 +95,7 @@ export class LiquidityBarSeriesRenderer implemen } const width = Math.min( Math.max(renderingScope.horizontalPixelRatio, column.right - column.left), - this._data.barSpacing * renderingScope.horizontalPixelRatio + this._data.barSpacing * renderingScope.horizontalPixelRatio, ) // Create margin to make visual bars thin diff --git a/apps/web/src/components/Charts/PriceChart/RoundedCandlestickSeries/renderer.ts b/apps/web/src/components/Charts/PriceChart/RoundedCandlestickSeries/renderer.ts index bd8dd76df47..c273c65e547 100644 --- a/apps/web/src/components/Charts/PriceChart/RoundedCandlestickSeries/renderer.ts +++ b/apps/web/src/components/Charts/PriceChart/RoundedCandlestickSeries/renderer.ts @@ -75,7 +75,7 @@ export class RoundedCandleSeriesRenderer + visibleRange: Range, ): void { if (this._data === null || this._options === null) { return @@ -99,7 +99,7 @@ export class RoundedCandleSeriesRenderer, - radius: number + radius: number, ): void { if (this._data === null || this._options === null) { return @@ -117,7 +117,7 @@ export class RoundedCandleSeriesRenderer implements this._data.visibleRange, renderingScope, zeroY * renderingScope.verticalPixelRatio, - options.hoveredLogicalIndex + options.hoveredLogicalIndex, ) const areaPaths = this._createAreas(linesMeshed) @@ -151,7 +151,7 @@ export class StackedAreaSeriesRenderer implements visibleRange: Range, renderingScope: BitmapCoordinatesRenderingScope, zeroY: number, - hoveredIndex?: number | null + hoveredIndex?: number | null, ) { const { horizontalPixelRatio, verticalPixelRatio } = renderingScope const oddLines: LinePathData[] = [] diff --git a/apps/web/src/components/Charts/VolumeChart/CrosshairHighlightPrimitive.tsx b/apps/web/src/components/Charts/VolumeChart/CrosshairHighlightPrimitive.tsx index 580d00f5952..bdac192755e 100644 --- a/apps/web/src/components/Charts/VolumeChart/CrosshairHighlightPrimitive.tsx +++ b/apps/web/src/components/Charts/VolumeChart/CrosshairHighlightPrimitive.tsx @@ -36,7 +36,7 @@ export function positionsLine( positionMedia: number, pixelRatio: number, desiredWidthMedia = 1, - widthIsBitmap?: boolean + widthIsBitmap?: boolean, ): BitmapPositionLength { const scaledPosition = Math.round(pixelRatio * positionMedia) const lineBitmapWidth = widthIsBitmap ? desiredWidthMedia : Math.round(desiredWidthMedia * pixelRatio) @@ -77,7 +77,7 @@ class CrosshairHighlightPaneRenderer implements ISeriesPrimitivePaneRenderer { const margin = Math.min( Math.max(scope.horizontalPixelRatio, crosshairPos.length), - this._data.barSpacing * scope.horizontalPixelRatio + this._data.barSpacing * scope.horizontalPixelRatio, ) * 0.035 const crosshairXPosition = crosshairPos.position + margin @@ -88,7 +88,7 @@ class CrosshairHighlightPaneRenderer implements ISeriesPrimitivePaneRenderer { crosshairYPosition, crosshairPos.length, scope.bitmapSize.height - crosshairYPosition, - 9 + 9, ) ctx.fill() @@ -104,7 +104,7 @@ class CrosshairHighlightPaneRenderer implements ISeriesPrimitivePaneRenderer { crosshairXPosition + crosshairPos.length, crosshairYPosition, scope.bitmapSize.width - (crosshairXPosition + crosshairPos.length), - scope.bitmapSize.height - crosshairYPosition + scope.bitmapSize.height - crosshairYPosition, ) // reset global settings ctx.globalAlpha = 1 diff --git a/apps/web/src/components/Charts/VolumeChart/index.tsx b/apps/web/src/components/Charts/VolumeChart/index.tsx index 8311ed0fbb0..49391813d2e 100644 --- a/apps/web/src/components/Charts/VolumeChart/index.tsx +++ b/apps/web/src/components/Charts/VolumeChart/index.tsx @@ -140,7 +140,7 @@ export function VolumeChart({ height, data, feeTier, timePeriod, stale }: Volume const params = useMemo( () => ({ data, colors: [theme.accent1], headerHeight: 75, stale }), - [data, stale, theme.accent1] + [data, stale, theme.accent1], ) return ( diff --git a/apps/web/src/components/Charts/VolumeChart/renderer.tsx b/apps/web/src/components/Charts/VolumeChart/renderer.tsx index 4a73913a229..8338c073133 100644 --- a/apps/web/src/components/Charts/VolumeChart/renderer.tsx +++ b/apps/web/src/components/Charts/VolumeChart/renderer.tsx @@ -99,7 +99,7 @@ export class CustomHistogramSeriesRenderer im this._data.barSpacing, renderingScope.horizontalPixelRatio, this._data.visibleRange.from, - this._data.visibleRange.to + this._data.visibleRange.to, ) const zeroY = priceToCoordinate(0) ?? 0 for (let i = this._data.visibleRange.from; i < this._data.visibleRange.to; i++) { @@ -111,7 +111,7 @@ export class CustomHistogramSeriesRenderer im let previousY = zeroY const width = Math.min( Math.max(renderingScope.horizontalPixelRatio, column.right - column.left), - this._data.barSpacing * renderingScope.horizontalPixelRatio + this._data.barSpacing * renderingScope.horizontalPixelRatio, ) // Modification: increase space between bars diff --git a/apps/web/src/components/Charts/VolumeChart/utils.ts b/apps/web/src/components/Charts/VolumeChart/utils.ts index 99b8876c730..30be9b505f2 100644 --- a/apps/web/src/components/Charts/VolumeChart/utils.ts +++ b/apps/web/src/components/Charts/VolumeChart/utils.ts @@ -18,7 +18,7 @@ export function getCumulativeVolume(data: CustomHistogramData[]) { export function getVolumeProtocolInfo( data: StackedHistogramData | undefined, - sources: PriceSource[] + sources: PriceSource[], ): ChartHeaderProtocolInfo[] { const info = new Array() for (const source of sources) { @@ -131,7 +131,7 @@ export interface ColumnPosition { function calculateColumnPosition( xMedia: number, columnData: ColumnCommon, - previousPosition: ColumnPosition | undefined + previousPosition: ColumnPosition | undefined, ): ColumnPosition { const xBitmapUnRounded = xMedia * columnData.horizontalPixelRatio const xBitmap = Math.round(xBitmapUnRounded) @@ -173,7 +173,7 @@ export function calculateColumnPositionsInPlace( barSpacingMedia: number, horizontalPixelRatio: number, startIndex: number, - endIndex: number + endIndex: number, ): void { const common = columnCommon(barSpacingMedia, horizontalPixelRatio) let previous: ColumnPositionItem | undefined = undefined @@ -196,7 +196,7 @@ export function calculateColumnPositionsInPlace( const width = item.column.right - item.column.left + 1 return Math.min(smallest, width) }, - Math.ceil(barSpacingMedia * horizontalPixelRatio) + Math.ceil(barSpacingMedia * horizontalPixelRatio), ) if (common.spacing > 0 && minColumnWidth < alignToMinimalWidthLimit) { ;(items as ColumnPositionItem[]).forEach((item: ColumnPositionItem, index: number) => { diff --git a/apps/web/src/components/Charts/hooks.ts b/apps/web/src/components/Charts/hooks.ts index 096e84f6ecb..ac8d67af507 100644 --- a/apps/web/src/components/Charts/hooks.ts +++ b/apps/web/src/components/Charts/hooks.ts @@ -18,6 +18,6 @@ export function useHeaderDateFormatter() { } return new Date(time * 1000).toLocaleString(locale, headerTimeFormatOptions) }, - [locale] + [locale], ) } diff --git a/apps/web/src/components/ConfirmSwapModal/Error.tsx b/apps/web/src/components/ConfirmSwapModal/Error.tsx index 3368a2c6864..5f2aa5d6449 100644 --- a/apps/web/src/components/ConfirmSwapModal/Error.tsx +++ b/apps/web/src/components/ConfirmSwapModal/Error.tsx @@ -101,7 +101,7 @@ export default function Error({ errorType, trade, showTrade, swapResult, onRetry href={getExplorerLink( swapResult.response.chainId, swapResult.response.hash, - ExplorerDataType.TRANSACTION + ExplorerDataType.TRANSACTION, )} color="neutral2" > diff --git a/apps/web/src/components/ConfirmSwapModal/Head.test.tsx b/apps/web/src/components/ConfirmSwapModal/Head.test.tsx index ac8f080596e..d70799edc2e 100644 --- a/apps/web/src/components/ConfirmSwapModal/Head.test.tsx +++ b/apps/web/src/components/ConfirmSwapModal/Head.test.tsx @@ -5,7 +5,7 @@ import { fireEvent, render, screen } from 'test-utils/render' describe('ConfirmSwapModal/Head', () => { it('should render correctly for a classic swap', () => { const { asFragment } = render( - + , ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText('Review swap')).toBeInTheDocument() @@ -13,7 +13,7 @@ describe('ConfirmSwapModal/Head', () => { it('should render correctly for a Limit order', () => { const { asFragment } = render( - + , ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText('Review limit')).toBeInTheDocument() @@ -22,7 +22,7 @@ describe('ConfirmSwapModal/Head', () => { it('should call the close callback', () => { const callback = jest.fn() const component = render( - + , ) const button = component.getByTestId('confirmation-close-icon') diff --git a/apps/web/src/components/ConfirmSwapModal/Pending.test.tsx b/apps/web/src/components/ConfirmSwapModal/Pending.test.tsx index e7b5ad08ffd..3a86fa4384b 100644 --- a/apps/web/src/components/ConfirmSwapModal/Pending.test.tsx +++ b/apps/web/src/components/ConfirmSwapModal/Pending.test.tsx @@ -93,11 +93,11 @@ describe('Pending - classic trade titles', () => { revocationPending={revocationPending} wrapTxHash={wrapTxHash} swapResult={swapResult} - /> + />, ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText(expectedTitle)).toBeInTheDocument() - } + }, ) }) @@ -117,10 +117,10 @@ describe('Pending - uniswapX trade titles', () => { revocationPending={revocationPending} wrapTxHash={wrapTxHash} swapResult={swapResult} - /> + />, ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText(expectedTitle)).toBeInTheDocument() - } + }, ) }) diff --git a/apps/web/src/components/ConfirmSwapModal/ProgressIndicator.tsx b/apps/web/src/components/ConfirmSwapModal/ProgressIndicator.tsx index 49ad86b5b7a..2e1cb248d25 100644 --- a/apps/web/src/components/ConfirmSwapModal/ProgressIndicator.tsx +++ b/apps/web/src/components/ConfirmSwapModal/ProgressIndicator.tsx @@ -163,7 +163,7 @@ export default function ProgressIndicator({ : uniswapUrls.helpArticleUrls.howToSwapTokens, }, }), - [inputTokenColor, nativeCurrency.symbol, trade, theme.accent1] + [inputTokenColor, nativeCurrency.symbol, trade, theme.accent1], ) if (steps.length === 0) { diff --git a/apps/web/src/components/ConfirmSwapModal/index.tsx b/apps/web/src/components/ConfirmSwapModal/index.tsx index 798aff20e45..5563cb36dc3 100644 --- a/apps/web/src/components/ConfirmSwapModal/index.tsx +++ b/apps/web/src/components/ConfirmSwapModal/index.tsx @@ -179,7 +179,7 @@ export function ConfirmSwapModal({ // If the user dismissed the modal while showing the price update, log the event as rejected. sendAnalyticsEvent( SwapEventName.SWAP_PRICE_UPDATE_ACKNOWLEDGED, - formatSwapPriceUpdatedEventProperties(trade, priceUpdate, SwapPriceUpdateUserResponse.REJECTED) + formatSwapPriceUpdatedEventProperties(trade, priceUpdate, SwapPriceUpdateUserResponse.REJECTED), ) } onDismiss() diff --git a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceButton.test.tsx b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceButton.test.tsx index 7c129a4e313..7ce6ab93a76 100644 --- a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceButton.test.tsx +++ b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceButton.test.tsx @@ -21,7 +21,7 @@ describe('LimitPresetPriceButton', () => { onSelect={onSelect} disabled={disabled} selected={selected} - /> + />, ) await act(() => userEvent.click(screen.getByText(num > 0 ? `+${num}%` : 'Market'))) expect(container.firstChild).toMatchSnapshot() diff --git a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.test.tsx b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.test.tsx index 80dc02426dd..2354915fd01 100644 --- a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.test.tsx +++ b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.test.tsx @@ -16,6 +16,7 @@ const mockSwapAndLimitContextValue = { setSelectedChainId: jest.fn(), currentTab: SwapTab.Limit, setCurrentTab: jest.fn(), + isSwapAndLimitContext: true, } const mockLimitContextValue = { @@ -53,7 +54,7 @@ describe('LimitPriceInputPanel', () => { const { container } = render( - + , ) expect(screen.getByText('Limit price')).toBeVisible() expect(screen.getByPlaceholderText('0')).toBeVisible() @@ -67,7 +68,7 @@ describe('LimitPriceInputPanel', () => { - + , ) expect(screen.getByText('DAI')).toBeVisible() expect(screen.getByPlaceholderText('0')).toBeVisible() @@ -88,7 +89,7 @@ describe('LimitPriceInputPanel', () => { - + , ) expect(screen.getByText('DAI')).toBeVisible() expect(container.querySelector('.token-symbol-container')).toHaveTextContent('USDC') diff --git a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.tsx b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.tsx index 698b84b2f22..38a41d573d3 100644 --- a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.tsx +++ b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.tsx @@ -112,7 +112,7 @@ export function LimitPriceInputPanel({ onCurrencySelect }: LimitPriceInputPanelP } const oneUnitOfBaseCurrency = CurrencyAmount.fromRawAmount( baseCurrency, - JSBI.BigInt(parseUnits('1', baseCurrency?.decimals)) + JSBI.BigInt(parseUnits('1', baseCurrency?.decimals)), ) const getAdjustedPrice = (priceAdjustmentPercentage: number) => { return new Price({ @@ -121,7 +121,10 @@ export function LimitPriceInputPanel({ onCurrencySelect }: LimitPriceInputPanelP // (100 + adjustmentPercentage) times the market quote amount for 1 input token quoteAmount: CurrencyAmount.fromRawAmount( quoteCurrency, - JSBI.multiply(JSBI.BigInt(100 + priceAdjustmentPercentage), marketPrice.quote(oneUnitOfBaseCurrency).quotient) + JSBI.multiply( + JSBI.BigInt(100 + priceAdjustmentPercentage), + marketPrice.quote(oneUnitOfBaseCurrency).quotient, + ), ), }) } @@ -145,7 +148,7 @@ export function LimitPriceInputPanel({ onCurrencySelect }: LimitPriceInputPanelP } const oneUnitOfBaseCurrency = CurrencyAmount.fromRawAmount( baseCurrency, - JSBI.BigInt(parseUnits('1', baseCurrency?.decimals)) + JSBI.BigInt(parseUnits('1', baseCurrency?.decimals)), ) const marketOutputAmount = adjustedPrice?.quote(oneUnitOfBaseCurrency) setLimitPrice( @@ -154,12 +157,12 @@ export function LimitPriceInputPanel({ onCurrencySelect }: LimitPriceInputPanelP type: NumberType.SwapTradeAmount, placeholder: limitPrice, locale: 'en-US', - }) + }), ) setLimitState((prev) => ({ ...prev, limitPriceEdited: true })) sendAnalyticsEvent(InterfaceEventNameLocal.LimitPresetRateSelected, { value: adjustmentPercentage }) }, - [baseCurrency, limitPrice, setLimitPrice, setLimitState] + [baseCurrency, limitPrice, setLimitPrice, setLimitState], ) const { currentPriceAdjustment } = useCurrentPriceAdjustment({ @@ -188,12 +191,15 @@ export function LimitPriceInputPanel({ onCurrencySelect }: LimitPriceInputPanelP amount: marketPrice .invert() .quote( - CurrencyAmount.fromRawAmount(quoteCurrency, JSBI.BigInt(parseUnits('1', quoteCurrency?.decimals))) + CurrencyAmount.fromRawAmount( + quoteCurrency, + JSBI.BigInt(parseUnits('1', quoteCurrency?.decimals)), + ), ), type: NumberType.SwapTradeAmount, placeholder: '', locale: 'en-US', - }) + }), ) } setLimitState((prev) => ({ ...prev, limitPriceInverted: !prev.limitPriceInverted, limitPriceEdited: true })) @@ -264,7 +270,7 @@ export function LimitPriceInputPanel({ onCurrencySelect }: LimitPriceInputPanelP } onCurrencySelect( limitPriceInverted ? invertCurrencyField(currencySelectModalField) : currencySelectModalField, - currency + currency, ) }} selectedCurrency={quoteCurrency} diff --git a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/useCurrentPriceAdjustment.ts b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/useCurrentPriceAdjustment.ts index 39795c4d163..fe36769aa9c 100644 --- a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/useCurrentPriceAdjustment.ts +++ b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/useCurrentPriceAdjustment.ts @@ -37,7 +37,7 @@ export function useCurrentPriceAdjustment({ } const oneUnitOfBaseCurrency = CurrencyAmount.fromRawAmount( baseCurrency, - JSBI.BigInt(parseUnits('1', baseCurrency?.decimals)) + JSBI.BigInt(parseUnits('1', baseCurrency?.decimals)), ) const marketQuote = marketPrice.quote(oneUnitOfBaseCurrency) diff --git a/apps/web/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx b/apps/web/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx index ef0558e9c16..7668f7317a0 100644 --- a/apps/web/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx +++ b/apps/web/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx @@ -265,13 +265,13 @@ const SwapCurrencyInputPanel = forwardRef { const [modalOpen, setModalOpen] = useState(false) const account = useAccount() const { chainId } = useSwapAndLimitContext() const chainAllowed = useIsSupportedChainId(chainId) - const selectedCurrencyBalance = useCurrencyBalance(account.address, currency ?? undefined, chainId) + const selectedCurrencyBalance = useCurrencyBalance(account.address, currency ?? undefined) const theme = useTheme() const { formatCurrencyAmount } = useFormatter() @@ -435,7 +435,7 @@ const SwapCurrencyInputPanel = forwardRef ) - } + }, ) SwapCurrencyInputPanel.displayName = 'SwapCurrencyInputPanel' diff --git a/apps/web/src/components/Dialog/Dialog.test.tsx b/apps/web/src/components/Dialog/Dialog.test.tsx index dfbcb1b8cc5..20caa693a01 100644 --- a/apps/web/src/components/Dialog/Dialog.test.tsx +++ b/apps/web/src/components/Dialog/Dialog.test.tsx @@ -35,7 +35,7 @@ describe('', () => { body={mockBody} onCancel={mockOnCancel} buttonsConfig={mockButtonsConfig} - /> + />, ) expect(document.body).toMatchSnapshot() @@ -58,7 +58,7 @@ describe('', () => { body={mockBody} onCancel={mockOnCancel} buttonsConfig={mockButtonsConfig} - /> + />, ) fireEvent.click(screen.getByTestId('Dialog-closeButton')) @@ -75,7 +75,7 @@ describe('', () => { body={mockBody} onCancel={mockOnCancel} buttonsConfig={mockButtonsConfig} - /> + />, ) fireEvent.click(screen.getByText('Left Button')) @@ -92,7 +92,7 @@ describe('', () => { body={mockBody} onCancel={mockOnCancel} buttonsConfig={mockButtonsConfig} - /> + />, ) fireEvent.click(screen.getByText('Right Button')) @@ -109,7 +109,7 @@ describe('', () => { description={mockDescription} body={mockBody} onCancel={mockOnCancel} - /> + />, ) expect(screen.queryByText('Left Button')).not.toBeInTheDocument() expect(screen.queryByText('Right Button')).not.toBeInTheDocument() @@ -134,7 +134,7 @@ describe('', () => { description={mockDescription} body={mockBody} onCancel={mockOnCancel} - /> + />, ) expect(document.body).toMatchSnapshot() diff --git a/apps/web/src/components/ErrorBoundary/index.tsx b/apps/web/src/components/ErrorBoundary/index.tsx index d368e0ce4d9..c7d6095af2e 100644 --- a/apps/web/src/components/ErrorBoundary/index.tsx +++ b/apps/web/src/components/ErrorBoundary/index.tsx @@ -9,7 +9,7 @@ import { PropsWithChildren, useState } from 'react' import { Copy } from 'react-feather' import styled from 'styled-components' import { CopyToClipboard, ExternalLink, ThemedText } from 'theme/components' -import { isSentryEnabled } from 'utils/env' +import { isRemoteReportingEnabled } from 'utils/env' const FallbackWrapper = styled.div` display: flex; @@ -60,7 +60,10 @@ const CodeBlockWrapper = styled.div` display: flex; flex-direction: column; background: ${({ theme }) => theme.surface2}; - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), + box-shadow: + 0px 0px 1px rgba(0, 0, 0, 0.01), + 0px 4px 8px rgba(0, 0, 0, 0.04), + 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 24px 32px rgba(0, 0, 0, 0.01); border-radius: 24px; padding: 24px; @@ -99,7 +102,7 @@ const Fallback = ({ error, eventId }: { error: Error; eventId: string | null }) ? [ThemedText.HeadlineSmall, ThemedText.BodySmall] : [ThemedText.HeadlineLarge, ThemedText.BodySecondary] - const showErrorId = isSentryEnabled() && eventId + const showErrorId = isRemoteReportingEnabled() && eventId const showMoreButton = ( setExpanded((s) => !s)}> diff --git a/apps/web/src/components/Expand/index.test.tsx b/apps/web/src/components/Expand/index.test.tsx index c56dc3769db..2c82fe93bc4 100644 --- a/apps/web/src/components/Expand/index.test.tsx +++ b/apps/web/src/components/Expand/index.test.tsx @@ -7,7 +7,7 @@ describe('Expand', () => { render( Header} isOpen={false} onToggle={noop} button={Button}> Body - + , ) expect(screen.queryByText('Body')).not.toBeVisible() }) @@ -16,7 +16,7 @@ describe('Expand', () => { render( Header} isOpen={true} onToggle={noop} button={Button}> Body - + , ) expect(screen.queryByText('Body')).toBeVisible() }) @@ -26,7 +26,7 @@ describe('Expand', () => { render( Header} isOpen={false} onToggle={onToggle} button={Button}> Body - + , ) const button = screen.getByText('Button') diff --git a/apps/web/src/components/FeatureFlagModal/FeatureFlagModal.tsx b/apps/web/src/components/FeatureFlagModal/FeatureFlagModal.tsx index 15cd515c531..9f13bb75b90 100644 --- a/apps/web/src/components/FeatureFlagModal/FeatureFlagModal.tsx +++ b/apps/web/src/components/FeatureFlagModal/FeatureFlagModal.tsx @@ -225,6 +225,7 @@ export default function FeatureFlagModal() { + @@ -244,11 +245,7 @@ export default function FeatureFlagModal() { - - + diff --git a/apps/web/src/components/FeeSelector/index.tsx b/apps/web/src/components/FeeSelector/index.tsx index 169785f0c7b..8f865af6d79 100644 --- a/apps/web/src/components/FeeSelector/index.tsx +++ b/apps/web/src/components/FeeSelector/index.tsx @@ -92,9 +92,9 @@ export default function FeeSelector({ [FeeAmount.LOW]: PoolState.NOT_EXISTS, [FeeAmount.MEDIUM]: PoolState.NOT_EXISTS, [FeeAmount.HIGH]: PoolState.NOT_EXISTS, - } + }, ), - [pools] + [pools], ) const [showOptions, setShowOptions] = useState(false) @@ -112,7 +112,7 @@ export default function FeeSelector({ }) handleFeePoolSelect(fee) }, - [handleFeePoolSelect, trace] + [handleFeePoolSelect, trace], ) useEffect(() => { diff --git a/apps/web/src/components/FiatOnrampModal/index.tsx b/apps/web/src/components/FiatOnrampModal/index.tsx index abc5ca477a0..705892d9c92 100644 --- a/apps/web/src/components/FiatOnrampModal/index.tsx +++ b/apps/web/src/components/FiatOnrampModal/index.tsx @@ -108,7 +108,7 @@ export default function FiatOnrampModal() { colorCode: theme.accent1, defaultCurrencyCode: getDefaultCurrencyCode( tokenAddress, - chain?.backendChain.chain ?? queryChain?.backendChain.chain ?? accountChainInfo?.backendChain.chain + chain?.backendChain.chain ?? queryChain?.backendChain.chain ?? accountChainInfo?.backendChain.chain, ), redirectUrl: swapUrl, walletAddresses: JSON.stringify( @@ -117,8 +117,8 @@ export default function FiatOnrampModal() { ...acc, [currencyCode]: account.address, }), - {} - ) + {}, + ), ), }), }) diff --git a/apps/web/src/components/FormattedCurrencyAmount/index.tsx b/apps/web/src/components/FormattedCurrencyAmount/index.tsx index 31f2be5b15e..d96f63d0a3e 100644 --- a/apps/web/src/components/FormattedCurrencyAmount/index.tsx +++ b/apps/web/src/components/FormattedCurrencyAmount/index.tsx @@ -15,8 +15,8 @@ export default function FormattedCurrencyAmount({ {currencyAmount.equalTo(JSBI.BigInt(0)) ? '0' : currencyAmount.greaterThan(CURRENCY_AMOUNT_MIN) - ? currencyAmount.toSignificant(significantDigits) - : `<${CURRENCY_AMOUNT_MIN.toSignificant(1)}`} + ? currencyAmount.toSignificant(significantDigits) + : `<${CURRENCY_AMOUNT_MIN.toSignificant(1)}`} ) } diff --git a/apps/web/src/components/Icons/BraveBrowserLogo.tsx b/apps/web/src/components/Icons/BraveBrowserLogo.tsx new file mode 100644 index 00000000000..1ef92dadfbf --- /dev/null +++ b/apps/web/src/components/Icons/BraveBrowserLogo.tsx @@ -0,0 +1,52 @@ +import { ComponentProps } from 'react' + +export function BraveBrowserLogo(props: ComponentProps<'svg'>) { + return ( + + + + + + + + + + + + + + + + + + ) +} diff --git a/apps/web/src/components/Icons/Images.tsx b/apps/web/src/components/Icons/Images.tsx index 8fdc1f5a442..4859768cfe3 100644 --- a/apps/web/src/components/Icons/Images.tsx +++ b/apps/web/src/components/Icons/Images.tsx @@ -1,9 +1,9 @@ -export function ImagesIcon() { +export function ImagesIcon(props: React.SVGProps) { return ( - + ) diff --git a/apps/web/src/components/Icons/TimeForward.tsx b/apps/web/src/components/Icons/TimeForward.tsx deleted file mode 100644 index d07c8679f9f..00000000000 --- a/apps/web/src/components/Icons/TimeForward.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { useTheme } from 'styled-components' - -export const TimeForwardIcon = () => { - const theme = useTheme() - return ( - - - - ) -} diff --git a/apps/web/src/components/LiquidityChartRangeInput/Area.tsx b/apps/web/src/components/LiquidityChartRangeInput/Area.tsx index d7ffcef632d..46667b9034f 100644 --- a/apps/web/src/components/LiquidityChartRangeInput/Area.tsx +++ b/apps/web/src/components/LiquidityChartRangeInput/Area.tsx @@ -37,10 +37,10 @@ export const Area = ({ series.filter((d) => { const value = xScale(xValue(d)) return value > 0 && value <= window.innerWidth - }) as Iterable<[number, number]> + }) as Iterable<[number, number]>, ) ?? undefined } /> ), - [fill, series, xScale, xValue, yScale, yValue] + [fill, series, xScale, xValue, yScale, yValue], ) diff --git a/apps/web/src/components/LiquidityChartRangeInput/AxisBottom.tsx b/apps/web/src/components/LiquidityChartRangeInput/AxisBottom.tsx index dd27a9a9d30..7584d400e7c 100644 --- a/apps/web/src/components/LiquidityChartRangeInput/AxisBottom.tsx +++ b/apps/web/src/components/LiquidityChartRangeInput/AxisBottom.tsx @@ -39,5 +39,5 @@ export const AxisBottom = ({ ), - [innerHeight, offset, xScale] + [innerHeight, offset, xScale], ) diff --git a/apps/web/src/components/LiquidityChartRangeInput/Brush.tsx b/apps/web/src/components/LiquidityChartRangeInput/Brush.tsx index df21b707c7f..a70c425d11e 100644 --- a/apps/web/src/components/LiquidityChartRangeInput/Brush.tsx +++ b/apps/web/src/components/LiquidityChartRangeInput/Brush.tsx @@ -105,7 +105,7 @@ export const Brush = ({ setLocalBrushExtent(scaled) }, - [xScale, brushExtent, setBrushExtent] + [xScale, brushExtent, setBrushExtent], ) // keep local and external brush extent in sync @@ -272,6 +272,6 @@ export const Brush = ({ westHandleColor, westHandleInView, xScale, - ] + ], ) } diff --git a/apps/web/src/components/LiquidityChartRangeInput/Chart.tsx b/apps/web/src/components/LiquidityChartRangeInput/Chart.tsx index 05547c2bc8c..19282eb48d1 100644 --- a/apps/web/src/components/LiquidityChartRangeInput/Chart.tsx +++ b/apps/web/src/components/LiquidityChartRangeInput/Chart.tsx @@ -30,7 +30,7 @@ export function Chart({ const [innerHeight, innerWidth] = useMemo( () => [height - margins.top - margins.bottom, width - margins.left - margins.right], - [width, height, margins] + [width, height, margins], ) const { xScale, yScale } = useMemo(() => { @@ -76,7 +76,7 @@ export function Chart({ resetBrush={() => { onBrushDomainChange( [current * zoomLevels.initialMin, current * zoomLevels.initialMax] as [number, number], - 'reset' + 'reset', ) }} showResetButton={Boolean(ticksAtLimit[Bound.LOWER] || ticksAtLimit[Bound.UPPER])} diff --git a/apps/web/src/components/LiquidityChartRangeInput/Line.tsx b/apps/web/src/components/LiquidityChartRangeInput/Line.tsx index e7a9264b3c2..3543f412cb7 100644 --- a/apps/web/src/components/LiquidityChartRangeInput/Line.tsx +++ b/apps/web/src/components/LiquidityChartRangeInput/Line.tsx @@ -20,5 +20,5 @@ export const Line = ({ }) => useMemo( () => , - [value, xScale, innerHeight] + [value, xScale, innerHeight], ) diff --git a/apps/web/src/components/LiquidityChartRangeInput/Zoom.tsx b/apps/web/src/components/LiquidityChartRangeInput/Zoom.tsx index 6db2931f1eb..3d70d29a1e5 100644 --- a/apps/web/src/components/LiquidityChartRangeInput/Zoom.tsx +++ b/apps/web/src/components/LiquidityChartRangeInput/Zoom.tsx @@ -84,7 +84,7 @@ export default function Zoom({ .transition() .call(zoomBehavior.current.scaleTo, 0.5), ], - [svg] + [svg], ) useEffect(() => { diff --git a/apps/web/src/components/LiquidityChartRangeInput/index.tsx b/apps/web/src/components/LiquidityChartRangeInput/index.tsx index 0f95cdf82ae..6fb0c9fab64 100644 --- a/apps/web/src/components/LiquidityChartRangeInput/index.tsx +++ b/apps/web/src/components/LiquidityChartRangeInput/index.tsx @@ -127,7 +127,7 @@ export default function LiquidityChartRangeInput({ } }) }, - [isSorted, onLeftRangeInput, onRightRangeInput, ticksAtLimit] + [isSorted, onLeftRangeInput, onRightRangeInput, ticksAtLimit], ) interactive = interactive && Boolean(formattedData?.length) @@ -159,7 +159,7 @@ export default function LiquidityChartRangeInput({ return price ? `${(Math.sign(percent) < 0 ? '-' : '') + formatDelta(percent)}` : '' }, - [formatDelta, isSorted, price, ticksAtLimit] + [formatDelta, isSorted, price, ticksAtLimit], ) const isUninitialized = !currencyA || !currencyB || (formattedData === undefined && !isLoading) diff --git a/apps/web/src/components/Logo/CurrencyLogo.tsx b/apps/web/src/components/Logo/CurrencyLogo.tsx index 69b5fc32133..a47730d3aa3 100644 --- a/apps/web/src/components/Logo/CurrencyLogo.tsx +++ b/apps/web/src/components/Logo/CurrencyLogo.tsx @@ -5,7 +5,7 @@ import AssetLogo, { AssetLogoBaseProps } from 'components/Logo/AssetLogo' export default function CurrencyLogo( props: AssetLogoBaseProps & { currency?: Currency | null - } + }, ) { return ( ` max-height: 350px; overflow: auto; background-color: ${({ theme }) => theme.surface1}; - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), + box-shadow: + 0px 0px 1px rgba(0, 0, 0, 0.01), + 0px 4px 8px rgba(0, 0, 0, 0.04), + 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 24px 32px rgba(0, 0, 0, 0.01); border: 1px solid ${({ theme }) => theme.surface3}; border-radius: 12px; @@ -122,7 +125,7 @@ export const Menu = ({ modal, flyoutAlignment = FlyoutAlignment.RIGHT, ToggleUI, {content} - ) + ), )} )} diff --git a/apps/web/src/components/Modal/index.tsx b/apps/web/src/components/Modal/index.tsx index 21b0358b3d4..cf531338c79 100644 --- a/apps/web/src/components/Modal/index.tsx +++ b/apps/web/src/components/Modal/index.tsx @@ -119,7 +119,7 @@ export default function Modal({ onDismiss() } }, - [isOpen, onDismiss] + [isOpen, onDismiss], ) const fadeTransition = useTransition(isOpen, { @@ -171,8 +171,8 @@ export default function Modal({ style: { transform: y.interpolate((y) => `translateY(${(y as number) > 0 ? y : 0}px)`) }, } : slideIn - ? { style: styles } - : {})} + ? { style: styles } + : {})} aria-label="dialog" $height={height} $minHeight={minHeight} @@ -185,10 +185,10 @@ export default function Modal({ {!initialFocusRef && isMobile ?
: null} {children} - ) + ), )} - ) + ), )}
) diff --git a/apps/web/src/components/NavBar/ChainSelector/ChainSelectorRow.test.tsx b/apps/web/src/components/NavBar/ChainSelector/ChainSelectorRow.test.tsx index ac55bd8f334..0c33647d288 100644 --- a/apps/web/src/components/NavBar/ChainSelector/ChainSelectorRow.test.tsx +++ b/apps/web/src/components/NavBar/ChainSelector/ChainSelectorRow.test.tsx @@ -6,7 +6,7 @@ describe('ChainSelectorRow', () => { WEB_SUPPORTED_CHAIN_IDS.forEach((chainId) => { it(`should match snapshot for chainId ${chainId}`, () => { const { container } = render( - + , ) expect(container).toMatchSnapshot() }) @@ -20,7 +20,7 @@ describe('ChainSelectorRow', () => { onSelectChain={onSelectChain} isPending={false} disabled={false} - /> + />, ) const button = getByTestId('Optimism-selector') button.click() @@ -35,7 +35,7 @@ describe('ChainSelectorRow', () => { onSelectChain={onSelectChain} isPending={false} disabled={true} - /> + />, ) const button = getByTestId('Optimism-selector') button.click() diff --git a/apps/web/src/components/NavBar/ChainSelector/ChainSelectorRow.tsx b/apps/web/src/components/NavBar/ChainSelector/ChainSelectorRow.tsx index af1c92dbf68..0686f1da3e0 100644 --- a/apps/web/src/components/NavBar/ChainSelector/ChainSelectorRow.tsx +++ b/apps/web/src/components/NavBar/ChainSelector/ChainSelectorRow.tsx @@ -1,7 +1,6 @@ import Loader from 'components/Icons/LoadingSpinner' import { ChainLogo } from 'components/Logo/ChainLogo' import { getChain, useSupportedChainId } from 'constants/chains' -import { useAccount } from 'hooks/useAccount' import { Trans } from 'i18n' import { Check } from 'react-feather' import { useSwapAndLimitContext } from 'state/swap/hooks' @@ -63,9 +62,7 @@ interface ChainSelectorRowProps { } export default function ChainSelectorRow({ disabled, targetChain, onSelectChain, isPending }: ChainSelectorRowProps) { const theme = useTheme() - const account = useAccount() - const { chainId: swapChainId, multichainUXEnabled } = useSwapAndLimitContext() - const chainId = multichainUXEnabled ? swapChainId : account.chainId + const { chainId } = useSwapAndLimitContext() const supportedChain = useSupportedChainId(targetChain) const active = chainId === targetChain diff --git a/apps/web/src/components/NavBar/ChainSelector/index.tsx b/apps/web/src/components/NavBar/ChainSelector/index.tsx index 8fc9375c04f..0e97c272a22 100644 --- a/apps/web/src/components/NavBar/ChainSelector/index.tsx +++ b/apps/web/src/components/NavBar/ChainSelector/index.tsx @@ -77,9 +77,7 @@ function useWalletSupportedChains(): InterfaceChainId[] { } export const ChainSelector = ({ leftAlign }: { leftAlign?: boolean }) => { - const account = useAccount() - const { chainId: swapChainId, setSelectedChainId, multichainUXEnabled } = useSwapAndLimitContext() - const chainId = multichainUXEnabled ? swapChainId : account.chainId + const { chainId, setSelectedChainId, multichainUXEnabled } = useSwapAndLimitContext() // multichainFlagEnabled is different from multichainUXEnabled, multichainUXEnabled applies to swap // flag can be true but multichainUXEnabled can be false (TDP page) const multichainFlagEnabled = useFeatureFlag(FeatureFlags.MultichainUX) @@ -109,7 +107,7 @@ export const ChainSelector = ({ leftAlign }: { leftAlign?: boolean }) => { } return acc }, - { supported: [], unsupported: [] } as Record + { supported: [], unsupported: [] } as Record, ) return [supported, unsupported] }, [isSupportedChain, showTestnets, walletSupportsChain]) @@ -128,7 +126,7 @@ export const ChainSelector = ({ leftAlign }: { leftAlign?: boolean }) => { setIsOpen(false) popoverRef.current?.close() }, - [popoverRef, selectChain, setIsOpen, setSelectedChainId, multichainUXEnabled] + [popoverRef, selectChain, setIsOpen, setSelectedChainId, multichainUXEnabled], ) const styledMenuCss = css` @@ -152,11 +150,11 @@ export const ChainSelector = ({ leftAlign }: { leftAlign?: boolean }) => { if (navRefreshEnabled) { return ( - + {menuLabel} - + {supportedChains.map((selectorChain) => ( theme.accent2}; + color: ${({ theme }) => theme.accent1} !important; +` +const DownloadCTA = styled(Row)<{ isMobile: boolean }>` cursor: pointer; padding: 12px ${DOWNLOAD_PADDING_X}px; border-radius: 20px; transition: all 0.2s; box-sizing: content-box; transform: translateX(-${DOWNLOAD_PADDING_X}px); + ${({ isMobile }) => isMobile && HoverStyles} &:hover { - background: ${({ theme }) => theme.accent2}; - color: ${({ theme }) => theme.accent1} !important; + ${HoverStyles} } @media screen and (max-width: ${NAV_BREAKPOINT.isMobileDrawer}px) { transform: none; @@ -31,10 +36,13 @@ const DownloadCTA = styled(Row)` ` export function DownloadApp({ onClick }: { onClick?: () => void }) { const openGetTheAppModal = useOpenModal(ApplicationModal.GET_THE_APP) + const isTouchDevice = useIsTouchDevice() + const isMobileDrawer = useIsMobileDrawer() return ( { if (onClick) { diff --git a/apps/web/src/components/NavBar/CompanyMenu/MenuDropdown.tsx b/apps/web/src/components/NavBar/CompanyMenu/MenuDropdown.tsx index f6c6f5b76bd..61422661853 100644 --- a/apps/web/src/components/NavBar/CompanyMenu/MenuDropdown.tsx +++ b/apps/web/src/components/NavBar/CompanyMenu/MenuDropdown.tsx @@ -83,7 +83,7 @@ export function MenuDropdown({ close }: { close?: () => void }) { }, [tabs]) return ( - + {!areTabsVisible &&
} diff --git a/apps/web/src/components/NavBar/CompanyMenu/MobileMenuDrawer.tsx b/apps/web/src/components/NavBar/CompanyMenu/MobileMenuDrawer.tsx index 2049436097a..2884875f850 100644 --- a/apps/web/src/components/NavBar/CompanyMenu/MobileMenuDrawer.tsx +++ b/apps/web/src/components/NavBar/CompanyMenu/MobileMenuDrawer.tsx @@ -1,8 +1,10 @@ +import { AnimatedSlider } from 'components/AnimatedSlider' import Column from 'components/Column' import { useMenuContent } from 'components/NavBar/CompanyMenu/Content' import { DownloadApp } from 'components/NavBar/CompanyMenu/DownloadAppCTA' import { MenuLink } from 'components/NavBar/CompanyMenu/MenuDropdown' import { NavDropdown } from 'components/NavBar/NavDropdown' +import { getSettingsViewIndex } from 'components/NavBar/PreferencesMenu' import { CurrencySettings } from 'components/NavBar/PreferencesMenu/Currency' import { LanguageSettings } from 'components/NavBar/PreferencesMenu/Language' import { PreferenceSettings } from 'components/NavBar/PreferencesMenu/Preferences' @@ -74,7 +76,7 @@ export function MobileMenuDrawer({ isOpen, closeMenu }: { isOpen: boolean; close }) } }, - [setSettingsView, dropdownRef] + [setSettingsView, dropdownRef], ) const onExitPreferencesMenu = useCallback(() => changeView(PreferencesView.SETTINGS), [changeView]) const { t } = useTranslation() @@ -89,9 +91,12 @@ export function MobileMenuDrawer({ isOpen, closeMenu }: { isOpen: boolean; close }, [isOpen]) return ( - + - {settingsView === PreferencesView.SETTINGS && ( + - )} - {settingsView === PreferencesView.LANGUAGE && } - {settingsView === PreferencesView.CURRENCY && } + + + + ) diff --git a/apps/web/src/components/NavBar/CompanyMenu/index.tsx b/apps/web/src/components/NavBar/CompanyMenu/index.tsx index 20c837266ca..e31523d0fb1 100644 --- a/apps/web/src/components/NavBar/CompanyMenu/index.tsx +++ b/apps/web/src/components/NavBar/CompanyMenu/index.tsx @@ -11,11 +11,11 @@ import styled from 'styled-components' import { Popover, Text } from 'ui/src' import { Hamburger } from 'ui/src/components/icons' -const ArrowDown = styled(ArrowChangeDown)` +const ArrowDown = styled(ArrowChangeDown)<{ $isActive: boolean }>` height: 100%; - color: ${({ theme }) => theme.neutral2}; + color: ${({ $isActive, theme }) => ($isActive ? theme.neutral1 : theme.neutral2)}; ` -const UniIconContainer = styled.div` +const Trigger = styled.div` position: relative; display: flex; align-items: center; @@ -28,6 +28,11 @@ const UniIconContainer = styled.div` } } ` +const UniIcon = styled.div` + display: flex; + align-items: center; + gap: 4px; +` export function CompanyMenu() { const popoverRef = useRef(null) @@ -54,16 +59,18 @@ export function CompanyMenu() { return ( - - - {isLargeScreen && ( - - Uniswap - - )} + + + + {isLargeScreen && ( + + Uniswap + + )} + {(isSmallScreen || isTouchDevice) && } - - + + {isMobileDrawer ? : } diff --git a/apps/web/src/components/NavBar/DownloadApp/Modal/GetStarted.tsx b/apps/web/src/components/NavBar/DownloadApp/Modal/GetStarted.tsx index b8dc4251824..4ef5bdb6f57 100644 --- a/apps/web/src/components/NavBar/DownloadApp/Modal/GetStarted.tsx +++ b/apps/web/src/components/NavBar/DownloadApp/Modal/GetStarted.tsx @@ -2,6 +2,7 @@ import ExtensionIllustration from 'assets/images/extensionIllustration.jpg' import WalletIllustration from 'assets/images/walletIllustration.jpg' import Column from 'components/Column' import { AppleLogo } from 'components/Icons/AppleLogo' +import { BraveBrowserLogo } from 'components/Icons/BraveBrowserLogo' import { GoogleChromeLogo } from 'components/Icons/GoogleChromeLogo' import { GooglePlayStoreLogo } from 'components/Icons/GooglePlayStoreLogo' import { WiggleIcon } from 'components/NavBar/DownloadApp/GetTheAppButton' @@ -78,9 +79,14 @@ export function GetStarted({ toAppDownload }: { toAppDownload: () => void }) { - - - + + + + + + + + diff --git a/apps/web/src/components/NavBar/DownloadApp/Modal/GetTheApp.tsx b/apps/web/src/components/NavBar/DownloadApp/Modal/GetTheApp.tsx index 56d64bd8e56..d500a7712a6 100644 --- a/apps/web/src/components/NavBar/DownloadApp/Modal/GetTheApp.tsx +++ b/apps/web/src/components/NavBar/DownloadApp/Modal/GetTheApp.tsx @@ -1,18 +1,12 @@ -import walletAppPromoBannerQR from 'assets/images/walletAnnouncementBannerQR.png' import { ReactComponent as AppStoreBadge } from 'assets/svg/app-store-badge.svg' import { ReactComponent as PlayStoreBadge } from 'assets/svg/play-store-badge.svg' import { ModalContent } from 'components/NavBar/DownloadApp/Modal/Content' import Row from 'components/Row' +import { WalletOneLinkQR } from 'components/WalletOneLinkQR' import { useTranslation } from 'react-i18next' import styled from 'styled-components' import { ExternalLink } from 'theme/components' -const StyledQRCode = styled.img` - width: 150px; - height: 150px; - border: 1px solid ${({ theme }) => theme.surface3}; - border-radius: 12px; -` const BadgeLink = styled(ExternalLink)` stroke: none; :hover { @@ -24,7 +18,7 @@ export function GetTheApp() { return ( - + diff --git a/apps/web/src/components/NavBar/DownloadApp/Modal/index.tsx b/apps/web/src/components/NavBar/DownloadApp/Modal/index.tsx index 88899fd35e0..f184d07bd22 100644 --- a/apps/web/src/components/NavBar/DownloadApp/Modal/index.tsx +++ b/apps/web/src/components/NavBar/DownloadApp/Modal/index.tsx @@ -1,3 +1,4 @@ +import { AnimatedSlider } from 'components/AnimatedSlider' import Modal from 'components/Modal' import { GetStarted } from 'components/NavBar/DownloadApp/Modal/GetStarted' import { GetTheApp } from 'components/NavBar/DownloadApp/Modal/GetTheApp' @@ -81,10 +82,14 @@ export function GetTheAppModal() { {isLegacyNav ? ( - ) : page === Page.GetStarted ? ( - setPage(Page.GetApp)} /> ) : ( - + + setPage(Page.GetApp)} /> + + )} diff --git a/apps/web/src/components/NavBar/LEGACY/SearchBar/RecentlySearchedAssets.ts b/apps/web/src/components/NavBar/LEGACY/SearchBar/RecentlySearchedAssets.ts index 165ed7d766c..0873a248dca 100644 --- a/apps/web/src/components/NavBar/LEGACY/SearchBar/RecentlySearchedAssets.ts +++ b/apps/web/src/components/NavBar/LEGACY/SearchBar/RecentlySearchedAssets.ts @@ -34,12 +34,12 @@ export function useAddRecentlySearchedAsset() { (asset: RecentlySearchedAsset) => { // Removes the new asset if it was already in the array const newHistory = searchHistory.filter( - (oldAsset) => !(oldAsset.address === asset.address && oldAsset.chain === asset.chain) + (oldAsset) => !(oldAsset.address === asset.address && oldAsset.chain === asset.chain), ) newHistory.unshift(asset) updateSearchHistory(newHistory) }, - [searchHistory, updateSearchHistory] + [searchHistory, updateSearchHistory], ) } @@ -82,7 +82,7 @@ export function useRecentlySearchedAssets() { imageUrl: queryCollection?.image?.url ?? '', } }, - [queryCollections] + [queryCollections], ) collections?.forEach((collection) => (resultsMap[collection.address] = collection)) queryData.tokens?.filter(Boolean).forEach((token) => { diff --git a/apps/web/src/components/NavBar/LEGACY/SearchBar/SearchBar.tsx b/apps/web/src/components/NavBar/LEGACY/SearchBar/SearchBar.tsx index 03fc6bc8f62..6bd28cea433 100644 --- a/apps/web/src/components/NavBar/LEGACY/SearchBar/SearchBar.tsx +++ b/apps/web/src/components/NavBar/LEGACY/SearchBar/SearchBar.tsx @@ -36,7 +36,7 @@ const KeyShortcut = styled.div` border-radius: 4px; font-size: 12px; font-weight: 535; - line-height: 16px; + line-height: 18px; display: flex; align-items: center; opacity: 0.6; @@ -110,8 +110,8 @@ export function SearchBar() { const placeholderText = isMobileOrTablet ? t('common.search.label') : shouldDisableNFTRoutes - ? t('common.searchTokens') - : t('common.searchTokensNFT') + ? t('common.searchTokens') + : t('common.searchTokensNFT') const handleKeyPress = useCallback( (event: KeyboardEvent) => { @@ -121,7 +121,7 @@ export function SearchBar() { !isOpen && toggleOpen() } }, - [isOpen] + [isOpen], ) useEffect(() => { @@ -161,7 +161,7 @@ export function SearchBar() { className={clsx( styles.nftSearchBar, !isOpen && !isMobile && magicalGradientOnHover, - isMobileOrTablet && (isOpen ? styles.visible : styles.hidden) + isMobileOrTablet && (isOpen ? styles.visible : styles.hidden), )} borderRadius={isOpen || isMobileOrTablet ? undefined : '16'} borderTopRightRadius={isOpen && !isMobile ? '16' : undefined} diff --git a/apps/web/src/components/NavBar/LEGACY/SearchBar/SearchBarDropdown.tsx b/apps/web/src/components/NavBar/LEGACY/SearchBar/SearchBarDropdown.tsx index e790b04bda4..dfb78621702 100644 --- a/apps/web/src/components/NavBar/LEGACY/SearchBar/SearchBarDropdown.tsx +++ b/apps/web/src/components/NavBar/LEGACY/SearchBar/SearchBarDropdown.tsx @@ -77,7 +77,7 @@ const SearchBarDropdownSection = ({ selected_search_result_address: suggestion.address, }} /> - ) + ), )} @@ -155,7 +155,7 @@ function SearchBarDropdownContents({ const { data: trendingCollections, loading: trendingCollectionsAreLoading } = useTrendingCollections( 3, - HistoryDuration.Day + HistoryDuration.Day, ) const formattedTrendingCollections = useMemo(() => { @@ -181,7 +181,7 @@ function SearchBarDropdownContents({ const trendingTokensLength = !isNFTPage ? 3 : 2 const trendingTokens = useMemo( () => trendingTokenData?.slice(0, trendingTokensLength) ?? [...Array(trendingTokensLength)], - [trendingTokenData, trendingTokensLength] + [trendingTokenData, trendingTokensLength], ) const totalSuggestions = hasInput diff --git a/apps/web/src/components/NavBar/LEGACY/SearchBar/SuggestionRow.tsx b/apps/web/src/components/NavBar/LEGACY/SearchBar/SuggestionRow.tsx index 2c790550f16..89837cc055b 100644 --- a/apps/web/src/components/NavBar/LEGACY/SearchBar/SuggestionRow.tsx +++ b/apps/web/src/components/NavBar/LEGACY/SearchBar/SuggestionRow.tsx @@ -104,7 +104,7 @@ export const SuggestionRow = ({ const [brokenCollectionImage, setBrokenCollectionImage] = useState(false) const warning = useTokenWarning( isToken ? suggestion.address : undefined, - isToken ? supportedChainIdFromGQLChain(suggestion.chain) : UniverseChainId.Mainnet + isToken ? supportedChainIdFromGQLChain(suggestion.chain) : UniverseChainId.Mainnet, ) const handleClick = useCallback(() => { diff --git a/apps/web/src/components/NavBar/MobileBottomBar/MobileBottomBar.tsx b/apps/web/src/components/NavBar/MobileBottomBar/MobileBottomBar.tsx index 581d2207d17..12b49d4cb6a 100644 --- a/apps/web/src/components/NavBar/MobileBottomBar/MobileBottomBar.tsx +++ b/apps/web/src/components/NavBar/MobileBottomBar/MobileBottomBar.tsx @@ -1,6 +1,5 @@ import { NAV_BREAKPOINT } from 'components/NavBar/ScreenSizes' import styled, { css } from 'styled-components' -import { opacify } from 'theme/utils' import { Z_INDEX } from 'theme/zIndex' const MOBILE_BAR_MAX_HEIGHT = 100 // ensure that it's translated out of view on scroll @@ -34,8 +33,8 @@ export const MobileBottomBar = styled.div<{ $hide: boolean }>` ${MobileBottomBarBase} width: 100%; max-height: ${MOBILE_BAR_MAX_HEIGHT}px; - background-color: ${({ theme }) => opacify(20, theme.surface1)}}; backdrop-filter: blur(4px); + mask: linear-gradient(to bottom, transparent, ${({ theme }) => theme.surface1} 15%); padding: 12px 16px; transition: bottom ${({ theme }) => theme.transition.duration.slow}; ${({ $hide }) => $hide && `bottom: -${MOBILE_BAR_MAX_HEIGHT}px !important`}; diff --git a/apps/web/src/components/NavBar/MobileBottomBar/TDPActionTabs.tsx b/apps/web/src/components/NavBar/MobileBottomBar/TDPActionTabs.tsx index 8aef098b841..8048d466e71 100644 --- a/apps/web/src/components/NavBar/MobileBottomBar/TDPActionTabs.tsx +++ b/apps/web/src/components/NavBar/MobileBottomBar/TDPActionTabs.tsx @@ -22,6 +22,8 @@ const TDPActionPill = styled.button<{ $color?: string }>` background-color: ${({ $color, theme }) => $color || theme.neutral2}; color: ${({ theme }) => theme.neutralContrast}; padding: 12px 20px; + font-size: 18px; + font-weight: 535; ${ClickableStyle} > svg { stroke: transparent; @@ -49,7 +51,7 @@ export function TDPActionTabs() { } navigate(href) }, - [account, currencyChainId, switchChain, navigate] + [account, currencyChainId, switchChain, navigate], ) const tabs: TabItem[] = [ @@ -70,7 +72,7 @@ export function TDPActionTabs() { }, ] return ( - + {tabs.map((tab) => ( toActionLink(tab.href)} $color={tokenColor}> {tab.icon} diff --git a/apps/web/src/components/NavBar/NavBar.tsx b/apps/web/src/components/NavBar/NavBar.tsx index 11c4ef0e73c..bd200182597 100644 --- a/apps/web/src/components/NavBar/NavBar.tsx +++ b/apps/web/src/components/NavBar/NavBar.tsx @@ -17,7 +17,7 @@ import { useIsSendPage } from 'hooks/useIsSendPage' import { useIsSwapPage } from 'hooks/useIsSwapPage' import { useProfilePageState } from 'nft/hooks' import { ProfilePageStateType } from 'nft/types' -import styled from 'styled-components' +import styled, { css } from 'styled-components' import { BREAKPOINTS } from 'theme' import { Z_INDEX } from 'theme/zIndex' import { FeatureFlags } from 'uniswap/src/features/gating/flags' @@ -40,14 +40,23 @@ const NavContents = styled.div` justify-content: center; flex: 1 auto 1; ` -const Left = styled(Row)` - display: flex; - align-items: center; +const NavItems = css` gap: 12px; @media screen and (max-width: ${BREAKPOINTS.sm}px) { - gap: 8px; + gap: 4px; } ` +const Left = styled(Row)` + display: flex; + align-items: center; + wrap: nowrap; + ${NavItems} +` +const Right = styled(Row)` + justify-content: flex-end; + align-self: flex-end; + ${NavItems} +` const SearchContainer = styled.div` display: flex; flex: 1; @@ -78,7 +87,7 @@ export const RefreshedNavbar = () => { return ( ) diff --git a/apps/web/src/components/NavBar/NavDropdown/NavDropdown.tsx b/apps/web/src/components/NavBar/NavDropdown/NavDropdown.tsx index d84d06fe8b5..4a5e2d34035 100644 --- a/apps/web/src/components/NavBar/NavDropdown/NavDropdown.tsx +++ b/apps/web/src/components/NavBar/NavDropdown/NavDropdown.tsx @@ -1,5 +1,6 @@ +import { RemoveScroll } from '@tamagui/remove-scroll' import { ScrollBarStyles } from 'components/Common' -import { NAV_BREAKPOINT } from 'components/NavBar/ScreenSizes' +import { NAV_BREAKPOINT, useIsMobileDrawer } from 'components/NavBar/ScreenSizes' import Row from 'components/Row' import { ReactNode, RefObject } from 'react' import styled from 'styled-components' @@ -11,7 +12,7 @@ const NavDropdownContent = styled.div<{ $width?: number }>` background: ${({ theme }) => theme.surface1}; ${({ theme }) => !theme.darkMode && `box-shadow: 3px 3px 10px ${theme.surface3}`}; ${({ $width }) => $width && `width: ${$width}px;`} - max-height: calc(100vh - ${({ theme }) => theme.navHeight}px); + max-height: calc(100dvh - ${({ theme }) => theme.navHeight}px); overflow: auto; ${ScrollBarStyles} @@ -21,7 +22,7 @@ const NavDropdownContent = styled.div<{ $width?: number }>` border: none; box-shadow: none; overflowx: auto; - max-height: calc(100vh - ${({ theme }) => theme.navHeight + 12}px); + max-height: calc(100dvh - ${({ theme }) => theme.navHeight + 12}px); ${ScrollBarStyles} ::-webkit-scrollbar-track { margin-top: 40px; @@ -31,11 +32,14 @@ const NavDropdownContent = styled.div<{ $width?: number }>` interface NavDropdownProps { children: ReactNode + isOpen: boolean width?: number dropdownRef?: RefObject } -export function NavDropdown({ children, width, dropdownRef }: NavDropdownProps) { +export function NavDropdown({ children, width, dropdownRef, isOpen }: NavDropdownProps) { + const isMobileDrawer = useIsMobileDrawer() + return ( <> - - - - - - - - - - - + + + + + + + + + + + + + ) diff --git a/apps/web/src/components/NavBar/NavIcon.tsx b/apps/web/src/components/NavBar/NavIcon.tsx index 2521c8c115c..15c92e219e2 100644 --- a/apps/web/src/components/NavBar/NavIcon.tsx +++ b/apps/web/src/components/NavBar/NavIcon.tsx @@ -1,11 +1,12 @@ import { t } from 'i18n' import { ReactNode } from 'react' -import styled from 'styled-components' +import styled, { css } from 'styled-components' +import { FeatureFlags } from 'uniswap/src/features/gating/flags' +import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' -const Container = styled.div<{ $size: number; $isActive: boolean }>` +const ContainerBase = css<{ $size: number }>` width: ${({ $size }) => $size}px; height: ${({ $size }) => $size}px; - color: ${({ $isActive, theme }) => ($isActive ? theme.neutral1 : theme.neutral2)}; position: relative; display: flex; flex-direction: column; @@ -15,18 +16,31 @@ const Container = styled.div<{ $size: number; $isActive: boolean }>` background-color: transparent; border: none; cursor: pointer; - border-radius: 16px; - transition: 250; + transition: all 250ms; z-index: 1; +` +const LegacyContainer = styled.div<{ $size: number; $isActive: boolean }>` + ${ContainerBase} + color: ${({ $isActive, theme }) => ($isActive ? theme.neutral1 : theme.neutral2)}; + border-radius: 16px; transform: translateY(-1px) translateX(4px); ` +const Container = styled.div<{ $size: number; $isActive: boolean }>` + ${ContainerBase} + background-color: ${({ $isActive, theme }) => ($isActive ? theme.surface1Hovered : 'transparent')}; + color: ${({ theme }) => theme.neutral2}; + border-radius: 50%; + &:hover { + background-color: ${({ theme }) => theme.surface1Hovered}; + } +` interface NavIconProps { children: ReactNode size?: number isActive?: boolean label?: string - onClick: () => void + onClick?: () => void } export const NavIcon = ({ @@ -36,9 +50,14 @@ export const NavIcon = ({ label = t('common.navigationButton'), onClick, }: NavIconProps) => { - return ( + const isNavRefresh = useFeatureFlag(FeatureFlags.NavRefresh) + return isNavRefresh ? ( {children} + ) : ( + + {children} + ) } diff --git a/apps/web/src/components/NavBar/PreferencesMenu/Preferences.tsx b/apps/web/src/components/NavBar/PreferencesMenu/Preferences.tsx index fae7787bc30..175d929830c 100644 --- a/apps/web/src/components/NavBar/PreferencesMenu/Preferences.tsx +++ b/apps/web/src/components/NavBar/PreferencesMenu/Preferences.tsx @@ -82,7 +82,7 @@ export function PreferenceSettings({ <> {showHeader && ( - + )} diff --git a/apps/web/src/components/NavBar/PreferencesMenu/index.tsx b/apps/web/src/components/NavBar/PreferencesMenu/index.tsx index dd8e4f6e4df..94f9addc829 100644 --- a/apps/web/src/components/NavBar/PreferencesMenu/index.tsx +++ b/apps/web/src/components/NavBar/PreferencesMenu/index.tsx @@ -1,4 +1,6 @@ +import { AnimatedSlider } from 'components/AnimatedSlider' import { NavDropdown, NavDropdownDefaultWrapper } from 'components/NavBar/NavDropdown/index' +import { NavIcon } from 'components/NavBar/NavIcon' import { CurrencySettings } from 'components/NavBar/PreferencesMenu/Currency' import { LanguageSettings } from 'components/NavBar/PreferencesMenu/Language' import { PreferenceSettings } from 'components/NavBar/PreferencesMenu/Preferences' @@ -7,22 +9,47 @@ import { useCallback, useState } from 'react' import { Popover } from 'ui/src' import { Global } from 'ui/src/components/icons' +export function getSettingsViewIndex(view: PreferencesView) { + if (view === PreferencesView.SETTINGS) { + return 0 + } else if (view === PreferencesView.LANGUAGE) { + return 1 + } else { + return 2 + } +} + export function PreferenceMenu() { const [settingsView, setSettingsView] = useState(PreferencesView.SETTINGS) - const onExitMenu = useCallback(() => setSettingsView(PreferencesView.SETTINGS), [setSettingsView]) + const [isOpen, setIsOpen] = useState(false) + const handleExitMenu = useCallback(() => setSettingsView(PreferencesView.SETTINGS), [setSettingsView]) + const onOpenChange = useCallback( + (open: boolean) => { + setIsOpen(open) + if (!open) { + handleExitMenu() + } + }, + [handleExitMenu, setIsOpen], + ) return ( - - - + + + + + - + - {settingsView === PreferencesView.SETTINGS && ( + setSettingsView(view)} /> - )} - {settingsView === PreferencesView.LANGUAGE && } - {settingsView === PreferencesView.CURRENCY && } + + + diff --git a/apps/web/src/components/NavBar/SearchBar/RecentlySearchedAssets.ts b/apps/web/src/components/NavBar/SearchBar/RecentlySearchedAssets.ts index 2407f67c9c6..4c3bd36a564 100644 --- a/apps/web/src/components/NavBar/SearchBar/RecentlySearchedAssets.ts +++ b/apps/web/src/components/NavBar/SearchBar/RecentlySearchedAssets.ts @@ -37,12 +37,12 @@ export function useAddRecentlySearchedAsset() { (asset: RecentlySearchedAsset) => { // Removes the new asset if it was already in the array const newHistory = searchHistory.filter( - (oldAsset) => !(oldAsset.address === asset.address && oldAsset.chain === asset.chain) + (oldAsset) => !(oldAsset.address === asset.address && oldAsset.chain === asset.chain), ) newHistory.unshift(asset) updateSearchHistory(newHistory) }, - [searchHistory, updateSearchHistory] + [searchHistory, updateSearchHistory], ) } @@ -85,7 +85,7 @@ export function useRecentlySearchedAssets() { imageUrl: queryCollection?.image?.url ?? '', } }, - [queryCollections] + [queryCollections], ) collections?.forEach((collection) => (resultsMap[collection.address] = collection)) queryData.tokens?.filter(Boolean).forEach((token) => { diff --git a/apps/web/src/components/NavBar/SearchBar/SearchBarDropdown.tsx b/apps/web/src/components/NavBar/SearchBar/SearchBarDropdown.tsx index 54af2ab6193..b8e67220cee 100644 --- a/apps/web/src/components/NavBar/SearchBar/SearchBarDropdown.tsx +++ b/apps/web/src/components/NavBar/SearchBar/SearchBarDropdown.tsx @@ -96,7 +96,7 @@ function SearchBarDropdownSection({ selected_search_result_address: suggestion.address, }} /> - ) + ), )} @@ -173,7 +173,7 @@ function SearchBarDropdownContents({ const { data: trendingCollections, loading: trendingCollectionsAreLoading } = useTrendingCollections( 3, - HistoryDuration.Day + HistoryDuration.Day, ) const formattedTrendingCollections = useMemo(() => { @@ -198,7 +198,7 @@ function SearchBarDropdownContents({ const trendingTokensLength = !isNFTPage ? 3 : 2 const trendingTokens = useMemo( () => trendingTokenData?.slice(0, trendingTokensLength) ?? [...Array(trendingTokensLength)], - [trendingTokenData, trendingTokensLength] + [trendingTokenData, trendingTokensLength], ) const totalSuggestions = hasInput diff --git a/apps/web/src/components/NavBar/SearchBar/SuggestionRow.tsx b/apps/web/src/components/NavBar/SearchBar/SuggestionRow.tsx index 177eb6a6389..1405d5a9870 100644 --- a/apps/web/src/components/NavBar/SearchBar/SuggestionRow.tsx +++ b/apps/web/src/components/NavBar/SearchBar/SuggestionRow.tsx @@ -106,7 +106,7 @@ export function SuggestionRow({ const [brokenCollectionImage, setBrokenCollectionImage] = useState(false) const warning = useTokenWarning( isToken ? suggestion.address : undefined, - isToken ? supportedChainIdFromGQLChain(suggestion.chain) : UniverseChainId.Mainnet + isToken ? supportedChainIdFromGQLChain(suggestion.chain) : UniverseChainId.Mainnet, ) const handleClick = useCallback(() => { diff --git a/apps/web/src/components/NavBar/SearchBar/__snapshots__/SearchBar.test.tsx.snap b/apps/web/src/components/NavBar/SearchBar/__snapshots__/SearchBar.test.tsx.snap index 74c332d89f9..ac65aadc55a 100644 --- a/apps/web/src/components/NavBar/SearchBar/__snapshots__/SearchBar.test.tsx.snap +++ b/apps/web/src/components/NavBar/SearchBar/__snapshots__/SearchBar.test.tsx.snap @@ -2,26 +2,12 @@ exports[`disable nft on searchbar should render text with nfts 1`] = ` .c2 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.c4 { box-sizing: border-box; margin: 0; min-width: 0; } -.c5 { +.c3 { width: 100%; display: -webkit-box; display: -webkit-flex; @@ -51,7 +37,7 @@ exports[`disable nft on searchbar should render text with nfts 1`] = ` z-index: 1000; } -.c7 { +.c6 { background-color: #22222212; color: #7D7D7D; padding: 0px 8px; @@ -78,7 +64,7 @@ exports[`disable nft on searchbar should render text with nfts 1`] = ` backdrop-filter: blur(60px); } -.c3 { +.c4 { grid-area: input; background-color: #F9F9F9; border: 1px solid #22222212; @@ -92,19 +78,24 @@ exports[`disable nft on searchbar should render text with nfts 1`] = ` -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + gap: 4px; } -.c3:hover { +.c4:hover { background-color: #F5F5F5; } -.c6 { +.c5 { width: 20px; height: 20px; } @media screen and (max-width:396px) { - .c3 { + .c4 { border: none; } } @@ -123,60 +114,56 @@ exports[`disable nft on searchbar should render text with nfts 1`] = ` class="c1" >
-
- - - - -
- - - -
- / -
+ + +
+ + + +
+ /
@@ -188,26 +175,12 @@ exports[`disable nft on searchbar should render text with nfts 1`] = ` exports[`disable nft on searchbar should render text without nfts 1`] = ` .c2 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; -} - -.c4 { box-sizing: border-box; margin: 0; min-width: 0; } -.c5 { +.c3 { width: 100%; display: -webkit-box; display: -webkit-flex; @@ -237,7 +210,7 @@ exports[`disable nft on searchbar should render text without nfts 1`] = ` z-index: 1000; } -.c7 { +.c6 { background-color: #22222212; color: #7D7D7D; padding: 0px 8px; @@ -264,7 +237,7 @@ exports[`disable nft on searchbar should render text without nfts 1`] = ` backdrop-filter: blur(60px); } -.c3 { +.c4 { grid-area: input; background-color: #F9F9F9; border: 1px solid #22222212; @@ -278,19 +251,24 @@ exports[`disable nft on searchbar should render text without nfts 1`] = ` -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + gap: 4px; } -.c3:hover { +.c4:hover { background-color: #F5F5F5; } -.c6 { +.c5 { width: 20px; height: 20px; } @media screen and (max-width:396px) { - .c3 { + .c4 { border: none; } } @@ -309,60 +287,56 @@ exports[`disable nft on searchbar should render text without nfts 1`] = ` class="c1" >
-
- - - - -
- - - -
- / -
+ + +
+ + + +
+ /
diff --git a/apps/web/src/components/NavBar/SearchBar/index.tsx b/apps/web/src/components/NavBar/SearchBar/index.tsx index 5088b6d40f0..b5c63a9cacd 100644 --- a/apps/web/src/components/NavBar/SearchBar/index.tsx +++ b/apps/web/src/components/NavBar/SearchBar/index.tsx @@ -1,6 +1,5 @@ // eslint-disable-next-line no-restricted-imports import { InterfaceElementName, InterfaceEventName, InterfaceSectionName } from '@uniswap/analytics-events' -import Column from 'components/Column' import { NavIcon } from 'components/NavBar/NavIcon' import { NAV_BREAKPOINT } from 'components/NavBar/ScreenSizes' import { SearchBarDropdown } from 'components/NavBar/SearchBar/SearchBarDropdown' @@ -30,9 +29,9 @@ import { useTrace } from 'utilities/src/telemetry/trace/TraceContext' const NAV_SEARCH_MAX_WIDTH = '400px' const NAV_SEARCH_MIN_WIDTH = '280px' -const Anchor = styled.div<{ fullScreen: boolean }>` - position: ${({ fullScreen }) => (fullScreen ? 'static' : 'relative')}; - ${({ fullScreen }) => fullScreen && `z-index: ${Z_INDEX.modal}`} +const Anchor = styled.div<{ $fullScreen: boolean }>` + position: ${({ $fullScreen }) => ($fullScreen ? 'static' : 'relative')}; + ${({ $fullScreen }) => $fullScreen && `z-index: ${Z_INDEX.modal}`} ` const FullScreenSearchContainer = css` position: absolute; @@ -96,30 +95,34 @@ const FullScreenSearchInput = css` max-width: none; border: none; border-radius: 0; + gap: 8px; + height: 64px; ` const ClosedSearchInputHover = css` &:hover { background-color: ${({ theme }) => theme.surface1Hovered}; } ` -const SearchInput = styled(Column)<{ isOpen: boolean; fullScreen: boolean }>` +const SearchInput = styled(Row)<{ $isOpen: boolean; $fullScreen: boolean }>` grid-area: input; - background-color: ${({ theme, isOpen }) => (isOpen ? theme.surface1 : theme.surface2)}; + background-color: ${({ theme, $isOpen }) => ($isOpen ? theme.surface1 : theme.surface2)}; border: 1px solid ${({ theme }) => theme.surface3}; min-width: ${NAV_SEARCH_MIN_WIDTH}; max-width: ${NAV_SEARCH_MAX_WIDTH}; width: 100vw; padding: 8px 16px; border-radius: 20px; - ${({ isOpen }) => isOpen && OpenSearchInput} - ${({ fullScreen }) => fullScreen && FullScreenSearchInput} height: 40px; justify-content: center; + align-items: center; + gap: 4px; + ${({ $isOpen }) => $isOpen && OpenSearchInput} + ${({ $fullScreen }) => $fullScreen && FullScreenSearchInput} + ${({ $isOpen }) => !$isOpen && ClosedSearchInputHover} @media screen and (max-width: ${BREAKPOINTS.xs}px) { border: none; } - ${({ isOpen }) => !isOpen && ClosedSearchInputHover} ` const OpenSearchDropdown = css` border-top-left-radius: 0; @@ -130,15 +133,15 @@ const FullScreenSearchDropdown = css` border: none; border-radius: 0; ` -const SearchBarDropdownContainer = styled.div<{ isOpen: boolean; fullScreen: boolean }>` +const SearchBarDropdownContainer = styled.div<{ $isOpen: boolean; $fullScreen: boolean }>` grid-area: dropdown; background-color: ${({ theme }) => theme.surface1}; border: 1px solid ${({ theme }) => theme.surface3}; border-radius: 20px; max-height: 100%; overflow-y: scroll; - ${({ isOpen }) => isOpen && OpenSearchDropdown} - ${({ fullScreen }) => fullScreen && FullScreenSearchDropdown} + ${({ $isOpen }) => $isOpen && OpenSearchDropdown} + ${({ $fullScreen }) => $fullScreen && FullScreenSearchDropdown} @media screen and (max-width: ${NAV_BREAKPOINT.isMobileDrawer}px) { border: none; } @@ -228,7 +231,7 @@ export const SearchBar = ({ return ( - + {(!!isNavSearchInputVisible || isOpen) && ( - - - - - - - !isOpen && toggleOpen()} - onChange={(event: any) => { - !isOpen && toggleOpen() - setSearchValue(event.target.value) - }} - onBlur={() => - sendAnalyticsEvent(InterfaceEventName.NAVBAR_SEARCH_EXITED, navbarSearchEventProperties) - } - value={searchValue} - /> - - {fullScreen && isOpen && } - {!isOpen && /} - + + + + + + !isOpen && toggleOpen()} + onChange={(event: any) => { + !isOpen && toggleOpen() + setSearchValue(event.target.value) + }} + onBlur={() => + sendAnalyticsEvent(InterfaceEventName.NAVBAR_SEARCH_EXITED, navbarSearchEventProperties) + } + value={searchValue} + /> + + {fullScreen && isOpen && } + {!isOpen && /} )} {isOpen && ( - + {Label} - + {items.map((item, index) => ( ( spellCheck="false" /> ) - } + }, ) Input.displayName = 'Input' diff --git a/apps/web/src/components/Polling/index.tsx b/apps/web/src/components/Polling/index.tsx index 3bd8d30cb57..f15b968b550 100644 --- a/apps/web/src/components/Polling/index.tsx +++ b/apps/web/src/components/Polling/index.tsx @@ -125,7 +125,7 @@ export default function Polling() { () => (isSupportedChain ? UNIVERSE_CHAIN_INFO[chainId]?.blockWaitMsBeforeWarning : undefined) ?? DEFAULT_MS_BEFORE_WARNING, - [chainId, isSupportedChain] + [chainId, isSupportedChain], ) const machineTime = useMachineTimeMs(AVERAGE_L1_BLOCK_TIME) const blockTime = useCurrentBlockTimestamp( @@ -134,8 +134,8 @@ export default function Polling() { blocksPerFetch: /* 5m / 12s = */ 25 * (isSupportedChain ? UNIVERSE_CHAIN_INFO[chainId].blockPerMainnetEpochForChainId : 1), }), - [chainId, isSupportedChain] - ) + [chainId, isSupportedChain], + ), ) const warning = Boolean(!!blockTime && machineTime - blockTime.mul(1000).toNumber() > waitMsBeforeWarning) @@ -153,7 +153,7 @@ export default function Polling() { clearTimeout(mountingTimer) } }, - [blockNumber] //useEffect will run only one time + [blockNumber], //useEffect will run only one time //if you pass a value to array, like this [data] than clearTimeout will run every time this value changes (useEffect re-run) ) diff --git a/apps/web/src/components/Pools/PoolDetails/ChartSection/hooks.ts b/apps/web/src/components/Pools/PoolDetails/ChartSection/hooks.ts index 85aa57256d1..402735a6701 100644 --- a/apps/web/src/components/Pools/PoolDetails/ChartSection/hooks.ts +++ b/apps/web/src/components/Pools/PoolDetails/ChartSection/hooks.ts @@ -26,7 +26,7 @@ export function usePDPPriceChartData( poolData: PoolData | undefined, tokenA: Token | undefined, tokenB: Token | undefined, - isReversed: boolean + isReversed: boolean, ): ChartQueryResult { const { data, loading } = usePoolPriceHistoryQuery({ variables }) @@ -60,7 +60,7 @@ export function usePDPPriceChartData( } export function usePDPVolumeChartData( - variables: PDPChartQueryVars + variables: PDPChartQueryVars, ): ChartQueryResult { const { data, loading } = usePoolVolumeHistoryQuery({ variables }) diff --git a/apps/web/src/components/Pools/PoolDetails/ChartSection/index.tsx b/apps/web/src/components/Pools/PoolDetails/ChartSection/index.tsx index ce732f9b052..bd48c8eb0ba 100644 --- a/apps/web/src/components/Pools/PoolDetails/ChartSection/index.tsx +++ b/apps/web/src/components/Pools/PoolDetails/ChartSection/index.tsx @@ -98,7 +98,7 @@ function usePDPChartState( tokenB: Token | undefined, isReversed: boolean, chain: Chain, - protocolVersion: ProtocolVersion + protocolVersion: ProtocolVersion, ): TDPChartState { const [timePeriod, setTimePeriod] = useState(TimePeriod.DAY) const [chartType, setChartType] = useState(ChartType.VOLUME) @@ -147,7 +147,7 @@ export default function ChartSection(props: ChartSectionProps) { currencyB?.wrapped, props.isReversed, props.chain ?? Chain.Ethereum, - props.poolData?.protocolVersion ?? ProtocolVersion.V3 + props.poolData?.protocolVersion ?? ProtocolVersion.V3, ) const refitChartContent = useAtomValue(refitChartContentAtom) @@ -279,7 +279,7 @@ function PriceChart({ {(crosshairData) => { const displayValue = crosshairData ?? lastPrice const currencyBAmountRaw = Math.floor( - (displayValue.value ?? displayValue.close) * 10 ** referenceToken.decimals + (displayValue.value ?? displayValue.close) * 10 ** referenceToken.decimals, ) const priceDisplay = ( diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsHeader.test.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsHeader.test.tsx index f01322cf456..ec2df9060ed 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsHeader.test.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsHeader.test.tsx @@ -18,7 +18,7 @@ describe('PoolDetailsHeader', () => { name: 'USD Coin', decimals: 6, }, - }) + }), ) store.dispatch( addSerializedToken({ @@ -29,7 +29,7 @@ describe('PoolDetailsHeader', () => { name: 'Wrapped Ether', decimals: 18, }, - }) + }), ) }) @@ -75,10 +75,10 @@ describe('PoolDetailsHeader', () => { expect(asFragment()).toMatchSnapshot() const usdcLink = document.querySelector( - 'a[href="/explore/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"]' + 'a[href="/explore/tokens/ethereum/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"]', ) const wethLink = document.querySelector( - 'a[href="/explore/tokens/ethereum/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"]' + 'a[href="/explore/tokens/ethereum/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"]', ) expect(usdcLink?.textContent).toBe('USDC') expect(wethLink?.textContent).toBe('WETH') diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsHeader.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsHeader.tsx index 9d801b5324f..56a5bf6488e 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsHeader.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsHeader.tsx @@ -184,7 +184,7 @@ const ContractsDropdownRow = ({ getExplorerLink( chainId, address, - isNative ? ExplorerDataType.NATIVE : isPool ? ExplorerDataType.ADDRESS : ExplorerDataType.TOKEN + isNative ? ExplorerDataType.NATIVE : isPool ? ExplorerDataType.ADDRESS : ExplorerDataType.TOKEN, ) if (!chainId || !explorerUrl) { @@ -304,7 +304,7 @@ export function PoolDetailsHeader({ const poolName = `${token0?.symbol} / ${token1?.symbol}` const currencies = useMemo( () => (token0 && token1 ? [gqlToCurrency(token0), gqlToCurrency(token1)] : []), - [token0, token1] + [token0, token1], ) if (loading) { diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsLink.test.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsLink.test.tsx index bcbe84ec6d3..b2d72090a6e 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsLink.test.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsLink.test.tsx @@ -19,7 +19,7 @@ describe('PoolDetailsHeader', () => { name: 'USD Coin', decimals: 6, }, - }) + }), ) store.dispatch( addSerializedToken({ @@ -30,7 +30,7 @@ describe('PoolDetailsHeader', () => { name: 'Wrapped Ether', decimals: 18, }, - }) + }), ) }) @@ -40,7 +40,7 @@ describe('PoolDetailsHeader', () => { address={usdcWethPoolAddress} chainId={UniverseChainId.Mainnet} tokens={[validBEPoolToken0, validBEPoolToken1]} - /> + />, ) expect(asFragment()).toMatchSnapshot() @@ -52,7 +52,7 @@ describe('PoolDetailsHeader', () => { it('renders link for token address', async () => { const { asFragment } = render( - + , ) expect(asFragment()).toMatchSnapshot() diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsLink.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsLink.tsx index db4a55ba914..f58cdd80a1e 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsLink.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsLink.tsx @@ -103,7 +103,7 @@ export function PoolDetailsLink({ address, chainId, tokens, loading }: PoolDetai getExplorerLink( chainId, address, - isNative ? ExplorerDataType.NATIVE : isPool ? ExplorerDataType.ADDRESS : ExplorerDataType.TOKEN + isNative ? ExplorerDataType.NATIVE : isPool ? ExplorerDataType.ADDRESS : ExplorerDataType.TOKEN, ) const navigate = useNavigate() @@ -127,7 +127,7 @@ export function PoolDetailsLink({ address, chainId, tokens, loading }: PoolDetai // This callback must run after it sets truncateAddress to 'start' to see if it needs to 'both'. // It checks if the textRef has overflow, and sets truncateAddress accordingly to avoid it. // eslint-disable-next-line react-hooks/exhaustive-deps - [truncateAddress] + [truncateAddress], ) if (loading || !address || !chainId) { diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsPositionsTable.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsPositionsTable.tsx index 0fdac960a2c..82af42351aa 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsPositionsTable.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsPositionsTable.tsx @@ -46,8 +46,8 @@ const StatusWrapper = styled(Row)<{ status: PositionStatus }>` status === PositionStatus.IN_RANGE ? theme.success : status === PositionStatus.OUT_OF_RANGE - ? theme.deprecated_accentWarning - : theme.neutral2}; + ? theme.deprecated_accentWarning + : theme.neutral2}; ` const RangeWrapper = styled(Row)` @@ -105,8 +105,8 @@ function PositionRow({ positionInfo }: { positionInfo: PositionInfo }) { const status = positionInfo.inRange ? PositionStatus.IN_RANGE : positionInfo.closed - ? PositionStatus.CLOSED - : PositionStatus.OUT_OF_RANGE + ? PositionStatus.CLOSED + : PositionStatus.OUT_OF_RANGE const priceUpper = positionInfo.position.token0PriceLower.invert() const priceLower = positionInfo.position.token0PriceUpper.invert() diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsStats.test.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsStats.test.tsx index ed235252114..5ecda2b1425 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsStats.test.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsStats.test.tsx @@ -26,7 +26,7 @@ describe('PoolDetailsStats', () => { name: 'USD Coin', decimals: 6, }, - }) + }), ) store.dispatch( addSerializedToken({ @@ -37,7 +37,7 @@ describe('PoolDetailsStats', () => { name: 'Wrapped Ether', decimals: 18, }, - }) + }), ) }) diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsStatsButtons.test.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsStatsButtons.test.tsx index ab5c19d65f8..221154e3c81 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsStatsButtons.test.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsStatsButtons.test.tsx @@ -43,7 +43,7 @@ describe('PoolDetailsStatsButton', () => { name: 'USD Coin', decimals: 6, }, - }) + }), ) store.dispatch( addSerializedToken({ @@ -54,7 +54,7 @@ describe('PoolDetailsStatsButton', () => { name: 'Wrapped Ether', decimals: 18, }, - }) + }), ) }) @@ -86,7 +86,7 @@ describe('PoolDetailsStatsButton', () => { await act(() => userEvent.click(screen.getByTestId('pool-details-add-liquidity-button'))) expect(globalThis.window.location.href).toContain( - '/add/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/500' + '/add/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/500', ) }) }) diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsStatsButtons.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsStatsButtons.tsx index a903baa8f88..da05ba18009 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsStatsButtons.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsStatsButtons.tsx @@ -148,7 +148,7 @@ function findMatchingPosition(positions: PositionInfo[], token0?: Token, token1? (position?.details.token1.toLowerCase() === token0?.address || position?.details.token1.toLowerCase() === token1?.address) && position?.details.fee == feeTier && - !position.closed + !position.closed, ) } @@ -156,7 +156,7 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load const account = useAccount() const { positions: userOwnedPositions } = useMultiChainPositions( account.address ?? '', - chainId ? [chainId] : undefined + chainId ? [chainId] : undefined, ) const position = userOwnedPositions && findMatchingPosition(userOwnedPositions, token0, token1, feeTier) const tokenId = position?.details.tokenId @@ -170,7 +170,7 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load const { data: balanceQuery } = useTokenBalancesQuery() const { balance0, balance1, balance0Fiat, balance1Fiat } = useMemo(() => { const filteredBalances = balanceQuery?.portfolios?.[0]?.tokenBalances?.filter( - (tokenBalance) => tokenBalance?.token?.chain === chainIdToBackendChain({ chainId, withFallback: true }) + (tokenBalance) => tokenBalance?.token?.chain === chainIdToBackendChain({ chainId, withFallback: true }), ) const tokenBalance0 = filteredBalances?.find((tokenBalance) => tokenBalance?.token?.address === token0?.address) const tokenBalance1 = filteredBalances?.find((tokenBalance) => tokenBalance?.token?.address === token1?.address) diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsTable.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsTable.tsx index 2d36bdebb1c..51ce5a98e09 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsTable.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsTable.tsx @@ -44,9 +44,9 @@ export function PoolDetailsTableTab({ positions?.filter( (position) => Pool.getAddress(position.pool.token0, position.pool.token1, position.pool.fee).toLowerCase() === - poolAddress.toLowerCase() + poolAddress.toLowerCase(), ) ?? [], - [poolAddress, positions] + [poolAddress, positions], ) return ( diff --git a/apps/web/src/components/Pools/PoolDetails/PoolDetailsTransactionsTable.tsx b/apps/web/src/components/Pools/PoolDetails/PoolDetailsTransactionsTable.tsx index a6dcffc181d..0baec47cbe4 100644 --- a/apps/web/src/components/Pools/PoolDetails/PoolDetailsTransactionsTable.tsx +++ b/apps/web/src/components/Pools/PoolDetails/PoolDetailsTransactionsTable.tsx @@ -84,7 +84,7 @@ export function PoolDetailsTransactionsTable({ chain.id, filter, token0, - protocolVersion + protocolVersion, ) const showLoadingSkeleton = loading || !!error @@ -173,7 +173,7 @@ export function PoolDetailsTransactionsTable({ {PoolTransactionTableType.getValue?.()} ), - } + }, ), columnHelper.accessor((row) => row.amountUSD, { id: 'fiat-value', diff --git a/apps/web/src/components/Pools/PoolTable/PoolTable.tsx b/apps/web/src/components/Pools/PoolTable/PoolTable.tsx index ed8c647246c..8622d18cb5f 100644 --- a/apps/web/src/components/Pools/PoolTable/PoolTable.tsx +++ b/apps/web/src/components/Pools/PoolTable/PoolTable.tsx @@ -153,7 +153,7 @@ export function TopPoolTable() { const { topPools, loading, errorV3, errorV2 } = useTopPools( { sortBy: sortMethod, sortDirection: sortAscending ? OrderDirection.Asc : OrderDirection.Desc }, - chain.id + chain.id, ) const combinedError = errorV2 && errorV3 @@ -239,7 +239,7 @@ export function PoolsTable({ }, } }) ?? [], - [chainId, filterString, pools] + [chainId, filterString, pools], ) const showLoadingSkeleton = loading || !!error diff --git a/apps/web/src/components/Popover/index.tsx b/apps/web/src/components/Popover/index.tsx index 90dedab5fcb..90e29fd3228 100644 --- a/apps/web/src/components/Popover/index.tsx +++ b/apps/web/src/components/Popover/index.tsx @@ -11,7 +11,9 @@ const PopoverContainer = styled.div<{ show: boolean }>` pointer-events: none; visibility: ${(props) => (props.show ? 'visible' : 'hidden')}; opacity: ${(props) => (props.show ? 1 : 0)}; - transition: visibility 150ms linear, opacity 150ms linear; + transition: + visibility 150ms linear, + opacity 150ms linear; color: ${({ theme }) => theme.neutral2}; ` @@ -109,7 +111,7 @@ export default function Popover({ { name: 'preventOverflow', options: { padding: 8 } }, ], }), - [placement, offsetX, offsetY, arrowElement] + [placement, offsetX, offsetY, arrowElement], ) const { styles, update, attributes } = usePopper(referenceElement, show ? popperElement : null, options) diff --git a/apps/web/src/components/PositionListItem/PositionListItem.test.tsx b/apps/web/src/components/PositionListItem/PositionListItem.test.tsx index 9d37f282905..aadc74bee6d 100644 --- a/apps/web/src/components/PositionListItem/PositionListItem.test.tsx +++ b/apps/web/src/components/PositionListItem/PositionListItem.test.tsx @@ -30,7 +30,7 @@ beforeEach(() => { FeeAmount.MEDIUM, '1745948049099224684665158875285708', '4203610460178577802', - 200019 + 200019, ), ]) }) diff --git a/apps/web/src/components/PositionPreview/index.tsx b/apps/web/src/components/PositionPreview/index.tsx index 3786f5a3c1b..d25793ea962 100644 --- a/apps/web/src/components/PositionPreview/index.tsx +++ b/apps/web/src/components/PositionPreview/index.tsx @@ -45,9 +45,9 @@ export const PositionPreview = ({ ? baseCurrencyDefault === currency0 ? currency0 : baseCurrencyDefault === currency1 - ? currency1 - : currency0 - : currency0 + ? currency1 + : currency0 + : currency0, ) const sorted = baseCurrency === currency0 diff --git a/apps/web/src/components/RoutingDiagram/RoutingDiagram.test.tsx b/apps/web/src/components/RoutingDiagram/RoutingDiagram.test.tsx index eb588aa4fac..e0764f9300a 100644 --- a/apps/web/src/components/RoutingDiagram/RoutingDiagram.test.tsx +++ b/apps/web/src/components/RoutingDiagram/RoutingDiagram.test.tsx @@ -30,7 +30,7 @@ jest.mock( 'components/Logo/CurrencyLogo', () => ({ currency }: { currency: Currency }) => - `CurrencyLogo currency=${currency.symbol}` + `CurrencyLogo currency=${currency.symbol}`, ) jest.mock('../Popover', () => () => 'Popover') diff --git a/apps/web/src/components/SearchModal/CommonBases.test.tsx b/apps/web/src/components/SearchModal/CommonBases.test.tsx index bc5fc2ec5b0..8b41ef78ab3 100644 --- a/apps/web/src/components/SearchModal/CommonBases.test.tsx +++ b/apps/web/src/components/SearchModal/CommonBases.test.tsx @@ -9,14 +9,14 @@ const mockOnSelect = jest.fn() describe('CommonBases', () => { it('renders without crashing', () => { const { container } = render( - + , ) expect(container).toMatchSnapshot() }) it('renders correct number of common bases', () => { const { getAllByTestId } = render( - + , ) const items = getAllByTestId(/common-base-/) expect(items.length).toBe(6) @@ -26,7 +26,7 @@ describe('CommonBases', () => { window.innerWidth = 400 window.dispatchEvent(new Event('resize')) const { getAllByTestId } = render( - + , ) const items = getAllByTestId(/common-base-/) expect(items.length).toBe(6) diff --git a/apps/web/src/components/SearchModal/CommonBases.tsx b/apps/web/src/components/SearchModal/CommonBases.tsx index 32cd46d8df4..d06eef10a15 100644 --- a/apps/web/src/components/SearchModal/CommonBases.tsx +++ b/apps/web/src/components/SearchModal/CommonBases.tsx @@ -36,7 +36,7 @@ const formatAnalyticsEventProperties = ( currency: Currency, searchQuery: string, isAddressSearch: string | false, - portfolioBalanceUsd: number | undefined + portfolioBalanceUsd: number | undefined, ) => ({ token_symbol: currency?.symbol, token_chain_id: currency?.chainId, diff --git a/apps/web/src/components/SearchModal/CurrencyList/index.test.tsx b/apps/web/src/components/SearchModal/CurrencyList/index.test.tsx index f505daaf3cf..fecdc02b846 100644 --- a/apps/web/src/components/SearchModal/CurrencyList/index.test.tsx +++ b/apps/web/src/components/SearchModal/CurrencyList/index.test.tsx @@ -26,7 +26,7 @@ jest.mock( 'components/Logo/CurrencyLogo', () => ({ currency }: { currency: Currency }) => - `CurrencyLogo currency=${currency.symbol}` + `CurrencyLogo currency=${currency.symbol}`, ) jest.mock('../../../state/connection/hooks', () => { @@ -49,7 +49,7 @@ it('renders loading rows when isLoading is true', () => { searchQuery="" isAddressSearch="" balances={{}} - /> + />, ) expect(component.findByTestId('loading-rows')).toBeTruthy() expect(screen.queryByText('Wrapped BTC')).not.toBeInTheDocument() @@ -69,7 +69,7 @@ it('renders currency rows correctly when currencies list is non-empty', () => { searchQuery="" isAddressSearch="" balances={{}} - /> + />, ) expect(screen.getByText('Wrapped BTC')).toBeInTheDocument() expect(screen.getByText('DAI')).toBeInTheDocument() @@ -93,7 +93,7 @@ it('renders currency rows correctly with balances', () => { balances={{ [`1-${DAI.address.toLowerCase()}`]: { usdValue: 2, balance: 2 }, }} - /> + />, ) expect(screen.getByText('Wrapped BTC')).toBeInTheDocument() expect(screen.getByText('DAI')).toBeInTheDocument() diff --git a/apps/web/src/components/SearchModal/CurrencyList/index.tsx b/apps/web/src/components/SearchModal/CurrencyList/index.tsx index 92f326184ff..4321ba12d8f 100644 --- a/apps/web/src/components/SearchModal/CurrencyList/index.tsx +++ b/apps/web/src/components/SearchModal/CurrencyList/index.tsx @@ -238,7 +238,7 @@ export const formatAnalyticsEventProperties = ( index: number, data: any[], searchQuery: string, - isAddressSearch: string | false + isAddressSearch: string | false, ) => ({ token_symbol: token?.symbol, token_address: token?.isToken ? token?.address : undefined, @@ -270,7 +270,7 @@ export class CurrencyListRow { disabled?: boolean showAddress?: boolean tooltip?: string - } + }, ) {} } @@ -370,7 +370,7 @@ export default function CurrencyList({ searchQuery, isAddressSearch, balances, - ] + ], ) const itemKey = useCallback((index: number, data: typeof currencies) => { diff --git a/apps/web/src/components/SearchModal/CurrencySearch.tsx b/apps/web/src/components/SearchModal/CurrencySearch.tsx index 58e0496dd74..e62484cd628 100644 --- a/apps/web/src/components/SearchModal/CurrencySearch.tsx +++ b/apps/web/src/components/SearchModal/CurrencySearch.tsx @@ -7,7 +7,6 @@ import CommonBases from 'components/SearchModal/CommonBases' import CurrencyList, { CurrencyRow, formatAnalyticsEventProperties } from 'components/SearchModal/CurrencyList' import { PaddedColumn, SearchInput, Separator } from 'components/SearchModal/styled' import { useCurrencySearchResults } from 'components/SearchModal/useCurrencySearchResults' -import { useAccount } from 'hooks/useAccount' import useDebounce from 'hooks/useDebounce' import { useOnClickOutside } from 'hooks/useOnClickOutside' import useSelectChain from 'hooks/useSelectChain' @@ -76,9 +75,7 @@ export function CurrencySearch({ ...DEFAULT_CURRENCY_SEARCH_FILTERS, ...filters, } - const { chainId: swapChainId, multichainUXEnabled } = useSwapAndLimitContext() - const account = useAccount() - const chainId = multichainUXEnabled ? swapChainId : account.chainId + const { chainId } = useSwapAndLimitContext() const theme = useTheme() @@ -119,7 +116,7 @@ export function CurrencySearch({ onDismiss() } }, - [chainId, onCurrencySelect, onDismiss, selectChain] + [chainId, onCurrencySelect, onDismiss, selectChain], ) // clear the input on open @@ -157,7 +154,7 @@ export function CurrencySearch({ } } }, - [allCurrencyRows, debouncedQuery, native, handleCurrencySelect] + [allCurrencyRows, debouncedQuery, native, handleCurrencySelect], ) // menu ui @@ -213,7 +210,7 @@ export function CurrencySearch({ isSelected={Boolean(searchCurrency && selectedCurrency && selectedCurrency.equals(searchCurrency))} onSelect={(hasWarning: boolean) => searchCurrency && handleCurrencySelect(searchCurrency, hasWarning)} otherSelected={Boolean( - searchCurrency && otherSelectedCurrency && otherSelectedCurrency.equals(searchCurrency) + searchCurrency && otherSelectedCurrency && otherSelectedCurrency.equals(searchCurrency), )} showCurrencyAmount={showCurrencyAmount} eventProperties={formatAnalyticsEventProperties( @@ -221,7 +218,7 @@ export function CurrencySearch({ 0, [searchCurrency], searchQuery, - isAddressSearch + isAddressSearch, )} balance={ tryParseCurrencyAmount(String(balanceMap[currencyKey(searchCurrency)]?.balance ?? 0), searchCurrency) ?? diff --git a/apps/web/src/components/SearchModal/CurrencySearchModal.tsx b/apps/web/src/components/SearchModal/CurrencySearchModal.tsx index c61fafb6aaf..0264d2d4102 100644 --- a/apps/web/src/components/SearchModal/CurrencySearchModal.tsx +++ b/apps/web/src/components/SearchModal/CurrencySearchModal.tsx @@ -56,7 +56,7 @@ export default memo(function CurrencySearchModal({ onDismiss() } }, - [onDismiss, onCurrencySelect, userAddedTokens] + [onDismiss, onCurrencySelect, userAddedTokens], ) // used for token safety const [warningToken, setWarningToken] = useState() diff --git a/apps/web/src/components/SearchModal/styled.tsx b/apps/web/src/components/SearchModal/styled.tsx index 555dbd83e7d..27e1e219382 100644 --- a/apps/web/src/components/SearchModal/styled.tsx +++ b/apps/web/src/components/SearchModal/styled.tsx @@ -5,6 +5,7 @@ import { RowBetween } from 'components/Row' import styled from 'styled-components' export const PaddedColumn = styled(AutoColumn)` + position: relative; padding: 20px; ` diff --git a/apps/web/src/components/SearchModal/useCurrencySearchResults.ts b/apps/web/src/components/SearchModal/useCurrencySearchResults.ts index ec32eb97e4e..31112fe631b 100644 --- a/apps/web/src/components/SearchModal/useCurrencySearchResults.ts +++ b/apps/web/src/components/SearchModal/useCurrencySearchResults.ts @@ -4,7 +4,6 @@ import { CurrencySearchFilters } from 'components/SearchModal/CurrencySearch' import { chainIdToBackendChain, useSupportedChainId } from 'constants/chains' import { gqlTokenToCurrencyInfo } from 'graphql/data/types' import { useFallbackListTokens, useToken } from 'hooks/Tokens' -import { useAccount } from 'hooks/useAccount' import { useTokenBalances } from 'hooks/useTokenBalances' import { t } from 'i18next' import { getTokenFilter } from 'lib/hooks/useTokenList/filtering' @@ -53,9 +52,7 @@ export function useCurrencySearchResults({ selectedCurrency, otherSelectedCurrency, }: CurrencySearchParams): CurrencySearchResults { - const { chainId: swapChainId, multichainUXEnabled } = useSwapAndLimitContext() - const account = useAccount() - const chainId = multichainUXEnabled ? swapChainId : account.chainId + const { chainId } = useSwapAndLimitContext() const supportedChain = useSupportedChainId(chainId) /** @@ -118,7 +115,7 @@ export function useCurrencySearchResults({ .filter(getTokenFilter(searchQuery)) .filter( (userAddedToken) => - !searchResults?.searchTokens?.find((token) => isSameAddress(token?.address, userAddedToken.address)) + !searchResults?.searchTokens?.find((token) => isSameAddress(token?.address, userAddedToken.address)), ), ] } else { diff --git a/apps/web/src/components/Settings/MenuButton/index.test.tsx b/apps/web/src/components/Settings/MenuButton/index.test.tsx index ac48cc0b355..88a05b4f40c 100644 --- a/apps/web/src/components/Settings/MenuButton/index.test.tsx +++ b/apps/web/src/components/Settings/MenuButton/index.test.tsx @@ -35,7 +35,7 @@ describe('MenuButton', () => { renderButton() expect(screen.getByTestId('settings-icon-with-slippage')).toHaveStyleRule( 'background-color', - lightDeprecatedTheme.deprecated_accentWarningSoft + lightDeprecatedTheme.deprecated_accentWarningSoft, ) }) }) diff --git a/apps/web/src/components/Settings/MultipleRoutingOptions.test.tsx b/apps/web/src/components/Settings/MultipleRoutingOptions.test.tsx index 3ba50cb7a16..9a236d6e9c9 100644 --- a/apps/web/src/components/Settings/MultipleRoutingOptions.test.tsx +++ b/apps/web/src/components/Settings/MultipleRoutingOptions.test.tsx @@ -18,7 +18,7 @@ describe('Multiple routing options', () => { render( - + , ) expect(screen.getByTestId('route-preference-toggle-Optimal')).toBeInTheDocument() @@ -31,7 +31,7 @@ describe('Multiple routing options', () => { render( - + , ) const optimalToggle = screen.getByTestId('route-preference-toggle-Optimal') @@ -53,7 +53,7 @@ describe('Multiple routing options', () => { render( - + , ) const optimalToggle = screen.getByTestId('route-preference-toggle-Optimal') @@ -88,7 +88,7 @@ describe('Multiple routing options', () => { render( - + , ) expect(screen.queryByTestId('route-preference-toggle-UniswapX')).toBeFalsy() diff --git a/apps/web/src/components/Settings/MultipleRoutingOptions.tsx b/apps/web/src/components/Settings/MultipleRoutingOptions.tsx index e0165d8d34c..10e12f3e667 100644 --- a/apps/web/src/components/Settings/MultipleRoutingOptions.tsx +++ b/apps/web/src/components/Settings/MultipleRoutingOptions.tsx @@ -144,7 +144,7 @@ export default function MultipleRoutingOptions({ chainId }: { chainId?: number } setRoutePreferenceOptions(options) setRoutingPreferences(routingPreferences) }, - [setRoutePreferenceOptions, setRoutingPreferences] + [setRoutePreferenceOptions, setRoutingPreferences], ) const handleRoutePreferenceToggle = useCallback( @@ -171,7 +171,7 @@ export default function MultipleRoutingOptions({ chainId }: { chainId?: number } [toggledPreferenceOption]: !routePreferenceOptions[toggledPreferenceOption], }) }, - [handleSetRoutePreferenceOptions, routePreferenceOptions] + [handleSetRoutePreferenceOptions, routePreferenceOptions], ) return ( diff --git a/apps/web/src/components/Settings/index.tsx b/apps/web/src/components/Settings/index.tsx index d3693f58508..b14082772b5 100644 --- a/apps/web/src/components/Settings/index.tsx +++ b/apps/web/src/components/Settings/index.tsx @@ -44,7 +44,10 @@ const MenuFlyout = styled(AutoColumn)` min-width: 20.125rem; background-color: ${({ theme }) => theme.surface1}; border: 1px solid ${({ theme }) => theme.surface3}; - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), + box-shadow: + 0px 0px 1px rgba(0, 0, 0, 0.01), + 0px 4px 8px rgba(0, 0, 0, 0.04), + 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 24px 32px rgba(0, 0, 0, 0.01); border-radius: 12px; position: absolute; @@ -159,7 +162,7 @@ export default function SettingsTab({ )} ), - [autoSlippage, chainId, multipleRoutingOptionsEnabled, showDeadlineSettings, showRoutingSettings, trade] + [autoSlippage, chainId, multipleRoutingOptionsEnabled, showDeadlineSettings, showRoutingSettings, trade], ) return ( diff --git a/apps/web/src/components/Slider/index.tsx b/apps/web/src/components/Slider/index.tsx index 60333baf9e7..56817a0a380 100644 --- a/apps/web/src/components/Slider/index.tsx +++ b/apps/web/src/components/Slider/index.tsx @@ -27,7 +27,10 @@ const StyledRangeInput = styled.input<{ size: number }>` &:hover, &:focus { - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1), 0px 4px 8px rgba(0, 0, 0, 0.08), 0px 16px 24px rgba(0, 0, 0, 0.06), + box-shadow: + 0px 0px 1px rgba(0, 0, 0, 0.1), + 0px 4px 8px rgba(0, 0, 0, 0.08), + 0px 16px 24px rgba(0, 0, 0, 0.06), 0px 24px 32px rgba(0, 0, 0, 0.04); } } @@ -42,7 +45,10 @@ const StyledRangeInput = styled.input<{ size: number }>` &:hover, &:focus { - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1), 0px 4px 8px rgba(0, 0, 0, 0.08), 0px 16px 24px rgba(0, 0, 0, 0.06), + box-shadow: + 0px 0px 1px rgba(0, 0, 0, 0.1), + 0px 4px 8px rgba(0, 0, 0, 0.08), + 0px 16px 24px rgba(0, 0, 0, 0.06), 0px 24px 32px rgba(0, 0, 0, 0.04); } } @@ -56,7 +62,10 @@ const StyledRangeInput = styled.input<{ size: number }>` &:hover, &:focus { - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.1), 0px 4px 8px rgba(0, 0, 0, 0.08), 0px 16px 24px rgba(0, 0, 0, 0.06), + box-shadow: + 0px 0px 1px rgba(0, 0, 0, 0.1), + 0px 4px 8px rgba(0, 0, 0, 0.08), + 0px 16px 24px rgba(0, 0, 0, 0.06), 0px 24px 32px rgba(0, 0, 0, 0.04); } } @@ -109,7 +118,7 @@ export default function Slider({ (e: ChangeEvent) => { onChange(parseInt(e.target.value)) }, - [onChange] + [onChange], ) return ( diff --git a/apps/web/src/components/Table/Filter.tsx b/apps/web/src/components/Table/Filter.tsx index 76f9c6c19a6..801234ba45d 100644 --- a/apps/web/src/components/Table/Filter.tsx +++ b/apps/web/src/components/Table/Filter.tsx @@ -76,7 +76,7 @@ export function Filter({ setFilters([...activeFilter, filter]) } }, - [activeFilter, setFilters] + [activeFilter, setFilters], ) // Need to put the modal in a Portal when on mobile to show over promo banner const Wrapper = isMobile ? Portal : Fragment diff --git a/apps/web/src/components/TextInput/index.test.tsx b/apps/web/src/components/TextInput/index.test.tsx index da38bca49ea..8a2380911df 100644 --- a/apps/web/src/components/TextInput/index.test.tsx +++ b/apps/web/src/components/TextInput/index.test.tsx @@ -11,7 +11,7 @@ describe('TextInput', () => { onUserInput={noop} placeholder="Test Placeholder" fontSize="12" - /> + />, ) expect(asFragment()).toMatchSnapshot() }) @@ -25,7 +25,7 @@ describe('TextInput', () => { onUserInput={onUserInputSpy} placeholder="Test Placeholder" fontSize="12" - /> + />, ) fireEvent.change(screen.getByPlaceholderText('Test Placeholder'), { target: { value: 'New value' } }) @@ -44,7 +44,7 @@ describe('ResizableTextArea', () => { onUserInput={noop} placeholder="Test Placeholder" fontSize="12" - /> + />, ) expect(asFragment()).toMatchSnapshot() }) @@ -58,7 +58,7 @@ describe('ResizableTextArea', () => { onUserInput={onUserInputSpy} placeholder="Test Placeholder" fontSize="12" - /> + />, ) fireEvent.change(screen.getByPlaceholderText('Test Placeholder'), { target: { value: 'New value' } }) diff --git a/apps/web/src/components/TextInput/index.tsx b/apps/web/src/components/TextInput/index.tsx index 3b8d6cec891..7abb4590cb4 100644 --- a/apps/web/src/components/TextInput/index.tsx +++ b/apps/web/src/components/TextInput/index.tsx @@ -80,7 +80,7 @@ export const TextInput = ({ (event: ChangeEvent) => { onUserInput(event.target.value) }, - [onUserInput] + [onUserInput], ) return ( @@ -122,7 +122,7 @@ export const ResizingTextArea = memo( inputRef.current.style.height = inputRef.current.scrollHeight + 'px' onUserInput(event.target.value) }, - [onUserInput] + [onUserInput], ) return ( @@ -140,7 +140,7 @@ export const ResizingTextArea = memo( ref={inputRef} /> ) - } + }, ) ResizingTextArea.displayName = 'ResizingTextArea' diff --git a/apps/web/src/components/Toggle/PillMultiToggle.tsx b/apps/web/src/components/Toggle/PillMultiToggle.tsx index f5d93f62085..6b6579fc666 100644 --- a/apps/web/src/components/Toggle/PillMultiToggle.tsx +++ b/apps/web/src/components/Toggle/PillMultiToggle.tsx @@ -22,7 +22,9 @@ const ActivePill = styled.div<{ activePillColor?: string }>` top: ${togglePadding}px; background-color: ${({ theme, activePillColor }) => activePillColor || theme.neutral3}; border-radius: 16px; - transition: left 0.3s ease, width 0.3s ease; + transition: + left 0.3s ease, + width 0.3s ease; ` const OptionButton = styled.button<{ active: boolean; activeTextColor?: string }>` flex: 1; @@ -98,7 +100,7 @@ export default function PillMultiToggle({ left: current?.offsetLeft, width: current?.offsetWidth, } - : { display: 'none' } + : { display: 'none' }, ) }, [buttonRefs, activeIndex]) diff --git a/apps/web/src/components/Tokens/TokenDetails/BalanceSummary.tsx b/apps/web/src/components/Tokens/TokenDetails/BalanceSummary.tsx index 86bf612b56a..c7a988b1698 100644 --- a/apps/web/src/components/Tokens/TokenDetails/BalanceSummary.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/BalanceSummary.tsx @@ -164,7 +164,7 @@ const OtherChainsBalanceSummary = ({ getTokenDetailsURL({ address: balance.token?.address, chain: balance.token?.chain ?? Chain.Ethereum, - }) + }), ) } /> diff --git a/apps/web/src/components/Tokens/TokenDetails/ChartSection/ChartTypeSelector.tsx b/apps/web/src/components/Tokens/TokenDetails/ChartSection/ChartTypeSelector.tsx index 7ae3a511df4..72b2f1df74e 100644 --- a/apps/web/src/components/Tokens/TokenDetails/ChartSection/ChartTypeSelector.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/ChartSection/ChartTypeSelector.tsx @@ -24,7 +24,7 @@ interface ChartTypeSelectorOption { } function getChartTypeSelectorOption( - option: ChartTypeSelectorOption | T + option: ChartTypeSelectorOption | T, ): ChartTypeSelectorOption { if (typeof option === 'string') { return { value: option } diff --git a/apps/web/src/components/Tokens/TokenDetails/ChartSection/hooks.ts b/apps/web/src/components/Tokens/TokenDetails/ChartSection/hooks.ts index e54b8478a6e..f0d5951f486 100644 --- a/apps/web/src/components/Tokens/TokenDetails/ChartSection/hooks.ts +++ b/apps/web/src/components/Tokens/TokenDetails/ChartSection/hooks.ts @@ -40,7 +40,7 @@ const CANDLESTICK_FALLBACK_THRESHOLD = 0.1 export function useTDPPriceChartData( variables: TDPChartQueryVariables, skip: boolean, - priceChartType: PriceChartType + priceChartType: PriceChartType, ): ChartQueryResult & { disableCandlestickUI: boolean } { const [fallback, enablePriceHistoryFallback] = useReducer(() => true, false) const { data, loading } = useTokenPriceQuery({ variables: { ...variables, fallback }, skip }) @@ -149,7 +149,7 @@ export function useTDPPriceChartData( export function useTDPVolumeChartData( variables: TDPChartQueryVariables, - skip: boolean + skip: boolean, ): ChartQueryResult { const { data, loading } = useTokenHistoricalVolumesQuery({ variables, skip }) return useMemo(() => { @@ -168,7 +168,7 @@ function toStackedLineData(entry: { timestamp: number; value: number }): Stacked export function useTDPTVLChartData( variables: TDPChartQueryVariables, - skip: boolean + skip: boolean, ): ChartQueryResult { const { data, loading } = useTokenHistoricalTvlsQuery({ variables, skip }) return useMemo(() => { diff --git a/apps/web/src/components/Tokens/TokenDetails/ChartSection/util.ts b/apps/web/src/components/Tokens/TokenDetails/ChartSection/util.ts index 13ec02d31ac..cff4dc7a9eb 100644 --- a/apps/web/src/components/Tokens/TokenDetails/ChartSection/util.ts +++ b/apps/web/src/components/Tokens/TokenDetails/ChartSection/util.ts @@ -46,7 +46,7 @@ const CHART_DURATION_STALE_THRESHOLD_MAP: Record { describe('getLoadingTitle', () => { it('should return correct title', () => { const { asFragment } = render( - <>{getLoadingTitle(USDC_MAINNET, USDC_MAINNET.address, UniverseChainId.Mainnet, 'ethereum')} + <>{getLoadingTitle(USDC_MAINNET, USDC_MAINNET.address, UniverseChainId.Mainnet, 'ethereum')}, ) expect(asFragment()).toMatchSnapshot() expect(asFragment().textContent).toContain('token data for') diff --git a/apps/web/src/components/Tokens/TokenDetails/Skeleton.tsx b/apps/web/src/components/Tokens/TokenDetails/Skeleton.tsx index 950f5595302..c90380487ca 100644 --- a/apps/web/src/components/Tokens/TokenDetails/Skeleton.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/Skeleton.tsx @@ -181,7 +181,7 @@ export function getLoadingTitle( token: Currency | undefined, tokenAddress: string, chainId: number, - chainName: string | undefined + chainName: string | undefined, ): ReactNode { let tokenName = '' if (token?.name && token?.symbol) { diff --git a/apps/web/src/components/Tokens/TokenDetails/TokenDescription.tsx b/apps/web/src/components/Tokens/TokenDetails/TokenDescription.tsx index c96e4a9f3ef..9f5ea8b923b 100644 --- a/apps/web/src/components/Tokens/TokenDetails/TokenDescription.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/TokenDescription.tsx @@ -79,7 +79,7 @@ export function TokenDescription() { const explorerUrl = getExplorerLink( currency.chainId, address, - currency.isNative ? ExplorerDataType.NATIVE : ExplorerDataType.TOKEN + currency.isNative ? ExplorerDataType.NATIVE : ExplorerDataType.TOKEN, ) const [isCopied, setCopied] = useCopyClipboard() @@ -91,7 +91,7 @@ export function TokenDescription() { const truncatedDescription = truncateDescription(description ?? '', TRUNCATE_CHARACTER_COUNT) const shouldTruncate = !!description && description.length > TRUNCATE_CHARACTER_COUNT const showTruncatedDescription = shouldTruncate && isDescriptionTruncated - const { inputTax: sellFee, outputTax: buyFee } = useSwapTaxes(address, address) + const { inputTax: sellFee, outputTax: buyFee } = useSwapTaxes(address, address, currency.chainId) const { formatPercent } = useFormatter() const { sellFeeString, buyFeeString } = { sellFeeString: formatPercent(sellFee), diff --git a/apps/web/src/components/Tokens/TokenDetails/TokenDetailsHeader.tsx b/apps/web/src/components/Tokens/TokenDetails/TokenDetailsHeader.tsx index 1a47106382b..f2e1c9b64f4 100644 --- a/apps/web/src/components/Tokens/TokenDetails/TokenDetailsHeader.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/TokenDetailsHeader.tsx @@ -137,7 +137,7 @@ export const TokenDetailsHeader = () => { const explorerUrl = getExplorerLink( currency.chainId, address, - currency.isNative ? ExplorerDataType.NATIVE : ExplorerDataType.TOKEN + currency.isNative ? ExplorerDataType.NATIVE : ExplorerDataType.TOKEN, ) const { homepageUrl, twitterName } = tokenQuery.data?.token?.project ?? {} diff --git a/apps/web/src/components/Tokens/TokenDetails/index.tsx b/apps/web/src/components/Tokens/TokenDetails/index.tsx index db81937dd89..4a91cc3038a 100644 --- a/apps/web/src/components/Tokens/TokenDetails/index.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/index.tsx @@ -13,6 +13,7 @@ import StatsSection from 'components/Tokens/TokenDetails/StatsSection' import { TokenDescription } from 'components/Tokens/TokenDetails/TokenDescription' import { TokenDetailsHeader } from 'components/Tokens/TokenDetails/TokenDetailsHeader' import { Hr } from 'components/Tokens/TokenDetails/shared' +import { CHAIN_ID_TO_BACKEND_NAME, isSupportedChainId } from 'constants/chains' import { NATIVE_CHAIN_ID } from 'constants/tokens' import { getTokenDetailsURL } from 'graphql/data/util' import { useCurrency } from 'hooks/Tokens' @@ -31,7 +32,6 @@ import styled from 'styled-components' import { FeatureFlags } from 'uniswap/src/features/gating/flags' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import Trace from 'uniswap/src/features/telemetry/Trace' -import { UniverseChainId } from 'uniswap/src/types/chains' import { addressesAreEquivalent } from 'utils/addressesAreEquivalent' import { getInitialLogoUrl } from 'utils/getInitialLogoURL' @@ -86,9 +86,8 @@ function useSwapInitialInputCurrency() { } function TDPSwapComponent() { - const { address, currency, currencyChain, warning } = useTDPContext() + const { address, currency, currencyChainId, warning } = useTDPContext() const account = useAccount() - const appChainId = account.chainId ?? UniverseChainId.Mainnet const navigate = useNavigate() const handleCurrencyChange = useCallback( @@ -111,12 +110,15 @@ function TDPSwapComponent() { const preloadedLogoSrc = getInitialLogoUrl( newDefaultToken.wrapped.address, newDefaultToken.chainId, - newDefaultToken.isNative + newDefaultToken.isNative, ) const url = getTokenDetailsURL({ // The function falls back to "NATIVE" if the address is null address: newDefaultToken.isNative ? null : newDefaultToken.address, - chain: currencyChain, + chain: + CHAIN_ID_TO_BACKEND_NAME[ + isSupportedChainId(newDefaultToken.chainId) ? newDefaultToken.chainId : currencyChainId + ], inputAddress: // If only one token was selected before we navigate, then it was the default token and it's being replaced. // On the new page, the *new* default token becomes the output, and we don't have another option to set as the input token. @@ -124,7 +126,7 @@ function TDPSwapComponent() { }) navigate(url, { state: { preloadedLogoSrc } }) }, - [address, currencyChain, navigate] + [address, currencyChainId, navigate], ) // Other token to prefill the swap form with @@ -138,7 +140,7 @@ function TDPSwapComponent() { continueSwap?.resolve(value) setContinueSwap(undefined) }, - [continueSwap, setContinueSwap] + [continueSwap, setContinueSwap], ) const isBlockedToken = warning?.canProceed === false @@ -154,7 +156,7 @@ function TDPSwapComponent() { initialInputCurrency={initialInputCurrency} initialOutputCurrency={currency} onCurrencyChange={handleCurrencyChange} - disableTokenInputs={currency.chainId !== appChainId} + disableTokenInputs={currency.chainId !== account.chainId} compact /> diff --git a/apps/web/src/components/Tokens/TokenDetails/shared.ts b/apps/web/src/components/Tokens/TokenDetails/shared.ts index 8a027865b89..86f799bb4f8 100644 --- a/apps/web/src/components/Tokens/TokenDetails/shared.ts +++ b/apps/web/src/components/Tokens/TokenDetails/shared.ts @@ -61,7 +61,7 @@ export const truncateDescription = (desc: string, maxCharacterCount = TRUNCATE_C //re-trim if we are in the middle of a word tokenDescriptionTruncated = `${tokenDescriptionTruncated.slice( 0, - Math.min(tokenDescriptionTruncated.length, tokenDescriptionTruncated.lastIndexOf(' ')) + Math.min(tokenDescriptionTruncated.length, tokenDescriptionTruncated.lastIndexOf(' ')), )}...` return tokenDescriptionTruncated } diff --git a/apps/web/src/components/Tokens/TokenDetails/tables/TokenDetailsPoolsTable.test.tsx b/apps/web/src/components/Tokens/TokenDetails/tables/TokenDetailsPoolsTable.test.tsx index 9589cb2253f..90595a0a560 100644 --- a/apps/web/src/components/Tokens/TokenDetails/tables/TokenDetailsPoolsTable.test.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/tables/TokenDetailsPoolsTable.test.tsx @@ -34,7 +34,7 @@ describe('TDPPoolTable', () => { }) const { asFragment } = render( - + , ) expect(screen.getAllByTestId('cell-loading-bubble')).not.toBeNull() expect(asFragment()).toMatchSnapshot() @@ -50,7 +50,7 @@ describe('TDPPoolTable', () => { }) const { asFragment } = render( - + , ) expect(screen.getByTestId('table-error-modal')).not.toBeNull() expect(asFragment()).toMatchSnapshot() @@ -80,7 +80,7 @@ describe('TDPPoolTable', () => { }) const { asFragment } = render( - + , ) expect(screen.getByTestId(`tdp-pools-table-${validBEPoolToken0.id}`)).not.toBeNull() expect(asFragment()).toMatchSnapshot() diff --git a/apps/web/src/components/Tokens/TokenDetails/tables/TokenDetailsPoolsTable.tsx b/apps/web/src/components/Tokens/TokenDetails/tables/TokenDetailsPoolsTable.tsx index e05be5f2570..bffdee4498d 100644 --- a/apps/web/src/components/Tokens/TokenDetails/tables/TokenDetailsPoolsTable.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/tables/TokenDetailsPoolsTable.tsx @@ -21,12 +21,12 @@ export function TokenDetailsPoolsTable({ const sortAscending = useAtomValue(sortAscendingAtom) const sortState = useMemo( () => ({ sortBy: sortMethod, sortDirection: sortAscending ? OrderDirection.Asc : OrderDirection.Desc }), - [sortAscending, sortMethod] + [sortAscending, sortMethod], ) const { pools, loading, errorV2, errorV3, loadMore } = usePoolsFromTokenAddress( referenceToken.address, sortState, - chainId + chainId, ) const combinedError = errorV2 && errorV3 diff --git a/apps/web/src/components/Tokens/TokenDetails/tables/TransactionsTable.tsx b/apps/web/src/components/Tokens/TokenDetails/tables/TransactionsTable.tsx index 1084c628fe4..e8495715755 100644 --- a/apps/web/src/components/Tokens/TokenDetails/tables/TransactionsTable.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/tables/TransactionsTable.tsx @@ -66,7 +66,7 @@ export function TransactionsTable({ const { transactions, loading, loadMore, errorV2, errorV3 } = useTokenTransactions( referenceToken.address, chainId, - filter + filter, ) const combinedError = errorV2 && errorV3 @@ -103,7 +103,7 @@ export function TransactionsTable({ makerAddress: transaction.account, } }), - [transactions] + [transactions], ) const showLoadingSkeleton = allDataStillLoading || !!combinedError @@ -182,7 +182,7 @@ export function TransactionsTable({ ), - } + }, ), columnHelper.accessor( (row) => { @@ -213,7 +213,7 @@ export function TransactionsTable({ {swapOutput.getValue?.()} ), - } + }, ), columnHelper.accessor((row) => row.usdValue, { id: 'fiat-value', diff --git a/apps/web/src/components/Tokens/TokenTable/index.tsx b/apps/web/src/components/Tokens/TokenTable/index.tsx index 7b243b05a17..14c545a6614 100644 --- a/apps/web/src/components/Tokens/TokenTable/index.tsx +++ b/apps/web/src/components/Tokens/TokenTable/index.tsx @@ -213,7 +213,7 @@ function TokenTable({ linkState: { preloadedLogoSrc: token?.project?.logoUrl }, } }) ?? [], - [chainId, filterString, formatDelta, sparklines, timePeriod, tokenSortRank, tokens] + [chainId, filterString, formatDelta, sparklines, timePeriod, tokenSortRank, tokens], ) const showLoadingSkeleton = loading || !!error diff --git a/apps/web/src/components/Tooltip/index.tsx b/apps/web/src/components/Tooltip/index.tsx index 9e5edcc46c9..3d74e9766e2 100644 --- a/apps/web/src/components/Tooltip/index.tsx +++ b/apps/web/src/components/Tooltip/index.tsx @@ -1,7 +1,7 @@ import { Placement } from '@popperjs/core' import Popover, { PopoverProps } from 'components/Popover' import { transparentize } from 'polished' -import { PropsWithChildren, ReactNode, useEffect, useState } from 'react' +import { PropsWithChildren, ReactNode, useCallback, useEffect, useState } from 'react' import styled from 'styled-components' import noop from 'utilities/src/react/noop' @@ -120,3 +120,59 @@ export function MouseoverTooltip(props: MouseoverTooltipProps) { ) } + +const CursorFollowerContainer = styled.div` + position: fixed; + pointer-events: none; + transform: translate(-50%, -50%); +` + +type MouseFollowTooltipProps = Omit + +export function MouseFollowTooltip(props: MouseFollowTooltipProps) { + const [position, setPosition] = useState<{ x?: number; y?: number }>({ + x: undefined, + y: undefined, + }) + const { text, disabled, children, onOpen, forceShow, ...rest } = props + const [show, setShow] = useState(false) + const open = () => { + setShow(true) + onOpen?.() + } + const close = () => setShow(false) + + const handleMouseMove = useCallback((event: MouseEvent) => { + setPosition({ x: event.clientX, y: event.clientY }) + }, []) + + useEffect(() => { + if (show && !disabled) { + document.addEventListener('mousemove', handleMouseMove) + } + return () => document.removeEventListener('mousemove', handleMouseMove) + }, [disabled, handleMouseMove, show]) + + return ( +
+ + + +
+ {children} +
+
+ ) +} diff --git a/apps/web/src/components/TopLevelModals/index.tsx b/apps/web/src/components/TopLevelModals/index.tsx index 4f0d729057b..db269f03ca2 100644 --- a/apps/web/src/components/TopLevelModals/index.tsx +++ b/apps/web/src/components/TopLevelModals/index.tsx @@ -15,7 +15,7 @@ import Bag from 'nft/components/bag/Bag' import TransactionCompleteModal from 'nft/components/collection/TransactionCompleteModal' import { useModalIsOpen, useToggleModal } from 'state/application/hooks' import { ApplicationModal } from 'state/application/reducer' -import { isBetaEnv, isDevEnv } from 'uniswap/src/utils/env' +import { isBetaEnv, isDevEnv } from 'utilities/src/environment' export default function TopLevelModals() { const addressClaimOpen = useModalIsOpen(ApplicationModal.ADDRESS_CLAIM) diff --git a/apps/web/src/components/TransactionConfirmationModal/ConfirmationModalContent.test.tsx b/apps/web/src/components/TransactionConfirmationModal/ConfirmationModalContent.test.tsx index 60ef6427ecb..6293662e985 100644 --- a/apps/web/src/components/TransactionConfirmationModal/ConfirmationModalContent.test.tsx +++ b/apps/web/src/components/TransactionConfirmationModal/ConfirmationModalContent.test.tsx @@ -11,7 +11,7 @@ describe('ConfirmationModalContent', () => { topContent={() =>
topContent
} bottomContent={() =>
bottomContent
} headerContent={() =>
headerContent
} - /> + />, ) expect(screen.getByTestId('confirmation-modal-chain-icon')).toBeInTheDocument() }) @@ -23,7 +23,7 @@ describe('ConfirmationModalContent', () => { onDismiss={jest.fn()} topContent={() =>
topContent
} bottomContent={() =>
bottomContent
} - /> + />, ) expect(screen.queryByTestId('confirmation-modal-chain-icon')).not.toBeInTheDocument() }) diff --git a/apps/web/src/components/WalletModal/useOrderedConnections.tsx b/apps/web/src/components/WalletModal/useOrderedConnections.tsx index 3d328deef15..f4f5a6178db 100644 --- a/apps/web/src/components/WalletModal/useOrderedConnections.tsx +++ b/apps/web/src/components/WalletModal/useOrderedConnections.tsx @@ -11,13 +11,13 @@ const SHOULD_THROW = { shouldThrow: true } as const function getConnectorWithId( connectors: readonly Connector[], id: ConnectorID, - options: { shouldThrow: true } + options: { shouldThrow: true }, ): Connector function getConnectorWithId(connectors: readonly Connector[], id: ConnectorID): Connector | undefined function getConnectorWithId( connectors: readonly Connector[], id: ConnectorID, - options?: { shouldThrow: true } + options?: { shouldThrow: true }, ): Connector | undefined { const connector = connectors.find((c) => c.id === id) if (!connector && options?.shouldThrow) { @@ -33,7 +33,7 @@ export function useConnectorWithId(id: ConnectorID, options?: { shouldThrow: tru const { connectors } = useConnect() return useMemo( () => (options?.shouldThrow ? getConnectorWithId(connectors, id, options) : getConnectorWithId(connectors, id)), - [connectors, id, options] + [connectors, id, options], ) } @@ -79,7 +79,7 @@ export function useOrderedConnections(excludeUniswapConnections?: boolean) { return 0 } }, - [recentConnectorId] + [recentConnectorId], ) return useMemo(() => { @@ -90,7 +90,7 @@ export function useOrderedConnections(excludeUniswapConnections?: boolean) { const uniswapWalletConnectConnector = getConnectorWithId( connectors, CONNECTION.UNISWAP_WALLET_CONNECT_CONNECTOR_ID, - SHOULD_THROW + SHOULD_THROW, ) if (!coinbaseSdkConnector || !walletConnectConnector || !uniswapWalletConnectConnector) { throw new Error('Expected connector(s) missing from wagmi context.') diff --git a/apps/web/src/components/WalletOneLinkQR/index.tsx b/apps/web/src/components/WalletOneLinkQR/index.tsx new file mode 100644 index 00000000000..75c15a0b4d4 --- /dev/null +++ b/apps/web/src/components/WalletOneLinkQR/index.tsx @@ -0,0 +1,424 @@ +import { ComponentProps } from 'react' +import { useTheme } from 'styled-components' + +export const WalletOneLinkQR = (props: ComponentProps<'svg'>) => { + const theme = useTheme() + const bg = theme.darkMode ? 'black' : 'white' + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} diff --git a/apps/web/src/components/Web3Provider/index.test.tsx b/apps/web/src/components/Web3Provider/index.test.tsx index f641fbf2c4c..15c1f6e1c12 100644 --- a/apps/web/src/components/Web3Provider/index.test.tsx +++ b/apps/web/src/components/Web3Provider/index.test.tsx @@ -51,7 +51,7 @@ describe('Web3Provider', () => { peer_wallet_agent: '(Injected)', }) expect(first(mocked(sendAnalyticsEvent).mock.invocationCallOrder)).toBeGreaterThan( - last(mocked(setUserProperty).mock.invocationCallOrder) + last(mocked(setUserProperty).mock.invocationCallOrder), ) }) diff --git a/apps/web/src/components/Web3Provider/index.tsx b/apps/web/src/components/Web3Provider/index.tsx index c0701a4509e..4eec66d0df4 100644 --- a/apps/web/src/components/Web3Provider/index.tsx +++ b/apps/web/src/components/Web3Provider/index.tsx @@ -93,7 +93,7 @@ function Updater() { const peerWalletAgent = provider ? getWalletMeta(provider)?.agent : undefined const isReconnect = connectedWallets.some( - (wallet) => wallet.account === account.address && wallet.walletType === walletType + (wallet) => wallet.account === account.address && wallet.walletType === walletType, ) provider diff --git a/apps/web/src/components/swap/GasEstimateTooltip.tsx b/apps/web/src/components/swap/GasEstimateTooltip.tsx index f1beb4d1875..89df73acad0 100644 --- a/apps/web/src/components/swap/GasEstimateTooltip.tsx +++ b/apps/web/src/components/swap/GasEstimateTooltip.tsx @@ -6,9 +6,9 @@ import Row, { RowFixed } from 'components/Row' import { MouseoverTooltip, TooltipSize } from 'components/Tooltip' import { GasBreakdownTooltip } from 'components/swap/GasBreakdownTooltip' import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains' -import { useAccount } from 'hooks/useAccount' import { SubmittableTrade } from 'state/routing/types' import { isUniswapXTrade } from 'state/routing/utils' +import { useSwapAndLimitContext } from 'state/swap/hooks' import styled from 'styled-components' import { ThemedText } from 'theme/components' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' @@ -24,7 +24,7 @@ const StyledGasIcon = styled(Gas)` ` export default function GasEstimateTooltip({ trade, loading }: { trade?: SubmittableTrade; loading: boolean }) { - const { chainId } = useAccount() + const { chainId } = useSwapAndLimitContext() const { formatNumber } = useFormatter() if (!trade || !chainId || !SUPPORTED_GAS_ESTIMATE_CHAIN_IDS.includes(chainId)) { diff --git a/apps/web/src/components/swap/MaxSlippageTooltip.tsx b/apps/web/src/components/swap/MaxSlippageTooltip.tsx deleted file mode 100644 index f6de0f5e536..00000000000 --- a/apps/web/src/components/swap/MaxSlippageTooltip.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Percent, TradeType } from '@uniswap/sdk-core' -import Column from 'components/Column' -import { RowBetween } from 'components/Row' -import { Trans } from 'i18n' -import { InterfaceTrade } from 'state/routing/types' -import { ExternalLink, Separator, ThemedText } from 'theme/components' -import { NumberType, useFormatter } from 'utils/formatNumbers' - -const ExactInMessage = ({ amount }: { amount: string }) => ( - -) - -const ExactOutMessage = ({ amount }: { amount: string }) => ( - -) - -function SlippageHeader({ amount, isExactIn }: { amount: string; isExactIn: boolean }) { - return ( - - - {isExactIn ? : } - - {amount} - - ) -} - -export function MaxSlippageTooltip({ trade, allowedSlippage }: { trade: InterfaceTrade; allowedSlippage: Percent }) { - const isExactIn = trade.tradeType === TradeType.EXACT_INPUT - const amount = isExactIn ? trade.minimumAmountOut(allowedSlippage) : trade.maximumAmountIn(allowedSlippage) - - const formattedAmount = useFormatter().formatCurrencyAmount({ amount, type: NumberType.SwapDetailsAmount }) - const displayAmount = `${formattedAmount} ${amount.currency.symbol}` - - return ( - - - -
- {isExactIn ? : }{' '} - - - -
-
- ) -} diff --git a/apps/web/src/components/swap/SwapBuyFiatButton.test.tsx b/apps/web/src/components/swap/SwapBuyFiatButton.test.tsx index 91eb9e3138b..7ff51706060 100644 --- a/apps/web/src/components/swap/SwapBuyFiatButton.test.tsx +++ b/apps/web/src/components/swap/SwapBuyFiatButton.test.tsx @@ -112,7 +112,7 @@ describe('SwapBuyFiatButton.tsx', () => { expect(error).toHaveBeenCalledTimes(2) expect(error).toHaveBeenCalledWith( 'Warning: flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task.%s', - expect.anything() + expect.anything(), ) }) }) diff --git a/apps/web/src/components/swap/SwapDetails.test.tsx b/apps/web/src/components/swap/SwapDetails.test.tsx index 0882ececd0a..9d2cb98df1f 100644 --- a/apps/web/src/components/swap/SwapDetails.test.tsx +++ b/apps/web/src/components/swap/SwapDetails.test.tsx @@ -1,15 +1,18 @@ import { Percent } from '@uniswap/sdk-core' import { SwapDetails } from 'components/swap/SwapDetails' +import { SlippageTooltipContent } from 'components/swap/SwapLineItem' import { LIMIT_ORDER_TRADE, PREVIEW_EXACT_IN_TRADE, TEST_ALLOWED_SLIPPAGE, TEST_TRADE_EXACT_INPUT, } from 'test-utils/constants' -import { render, screen, within } from 'test-utils/render' +import { render, renderHook, screen, within } from 'test-utils/render' +import { NumberType, useFormatter } from 'utils/formatNumbers' describe('SwapDetails.tsx', () => { it('matches base snapshot, test trade exact input', () => { + const { formatCurrencyAmount } = renderHook(() => useFormatter()).result.current const { asFragment } = render( { }} showAcceptChanges={false} onAcceptChanges={jest.fn()} - /> + />, ) expect(asFragment()).toMatchSnapshot() + const tradeMinAmount = TEST_TRADE_EXACT_INPUT.minimumAmountOut(TEST_ALLOWED_SLIPPAGE ?? new Percent(0)) + const formattedAmount = formatCurrencyAmount({ amount: tradeMinAmount, type: NumberType.SwapDetailsAmount }) + expect( screen.getByText( - 'The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert.' - ) + `If the price moves so that you will receive less than ${formattedAmount} ${tradeMinAmount.currency.symbol}, your transaction will revert.`, + ), ).toBeInTheDocument() expect(screen.getByText('The impact your trade has on the market price of this pool.')).toBeInTheDocument() + expect(screen.getByText('The maximum price movement before your transaction will revert.')).toBeInTheDocument() }) it('shows accept changes section when available', () => { @@ -63,7 +70,7 @@ describe('SwapDetails.tsx', () => { }} showAcceptChanges={true} onAcceptChanges={mockAcceptChanges} - /> + />, ) const showAcceptChanges = screen.getByTestId('show-accept-changes') expect(showAcceptChanges).toBeInTheDocument() @@ -91,7 +98,7 @@ describe('SwapDetails.tsx', () => { }} showAcceptChanges={false} onAcceptChanges={jest.fn()} - /> + />, ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText('Finalizing quote...')).toBeInTheDocument() @@ -117,7 +124,7 @@ describe('SwapDetails.tsx', () => { }} showAcceptChanges={false} onAcceptChanges={jest.fn()} - /> + />, ) expect(screen.getByText('Limit price')).toBeInTheDocument() expect(screen.getByText('Expiry')).toBeInTheDocument() @@ -125,8 +132,14 @@ describe('SwapDetails.tsx', () => { expect(screen.getByText('Network cost')).toBeInTheDocument() expect( screen.getByText( - 'Please be aware that the execution for limits may vary based on real-time market fluctuations and Ethereum network congestion. Limits may not execute exactly when tokens reach the specified price.' - ) + 'Please be aware that the execution for limits may vary based on real-time market fluctuations and Ethereum network congestion. Limits may not execute exactly when tokens reach the specified price.', + ), ).toBeInTheDocument() }) + + it('renders slippage tooltip', () => { + const { asFragment } = render() + expect(asFragment()).toMatchSnapshot() + expect(screen.getByText('The maximum price movement before your transaction will revert.')).toBeInTheDocument() + }) }) diff --git a/apps/web/src/components/swap/SwapDetailsDropdown.test.tsx b/apps/web/src/components/swap/SwapDetailsDropdown.test.tsx index c4045431f2b..677b5675b7d 100644 --- a/apps/web/src/components/swap/SwapDetailsDropdown.test.tsx +++ b/apps/web/src/components/swap/SwapDetailsDropdown.test.tsx @@ -18,14 +18,14 @@ describe('SwapDetailsDropdown.tsx', () => { syncing={false} loading={false} allowedSlippage={TEST_ALLOWED_SLIPPAGE} - /> + />, ) expect(asFragment()).toMatchSnapshot() }) it('renders loading state', () => { render( - + , ) expect(screen.getByText('Fetching best price...')).toBeInTheDocument() }) @@ -38,7 +38,7 @@ describe('SwapDetailsDropdown.tsx', () => { syncing={false} loading={false} allowedSlippage={TEST_ALLOWED_SLIPPAGE} - /> + />, ) expect(screen.getByTestId('swap-details-header-row')).toBeInTheDocument() expect(screen.getByTestId('trade-price-container')).toBeInTheDocument() @@ -53,14 +53,14 @@ describe('SwapDetailsDropdown.tsx', () => { syncing={false} loading={true} allowedSlippage={TEST_ALLOWED_SLIPPAGE} - /> + />, ) await act(() => userEvent.click(screen.getByTestId('swap-details-header-row'))) expect( screen.getByText( - 'Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees.' - ) + 'Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees.', + ), ).toBeInTheDocument() expect(screen.getByText(`${TEST_TOKEN_1.symbol} fee`)).toBeInTheDocument() }) @@ -72,14 +72,14 @@ describe('SwapDetailsDropdown.tsx', () => { syncing={false} loading={true} allowedSlippage={TEST_ALLOWED_SLIPPAGE} - /> + />, ) await act(() => userEvent.click(screen.getByTestId('swap-details-header-row'))) expect( screen.getByText( - 'Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees.' - ) + 'Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees.', + ), ).toBeInTheDocument() expect(screen.getByText(`${TEST_TOKEN_2.symbol} fee`)).toBeInTheDocument() }) diff --git a/apps/web/src/components/swap/SwapDetailsDropdown.tsx b/apps/web/src/components/swap/SwapDetailsDropdown.tsx index e96219ef43f..1ad39b87b39 100644 --- a/apps/web/src/components/swap/SwapDetailsDropdown.tsx +++ b/apps/web/src/components/swap/SwapDetailsDropdown.tsx @@ -110,6 +110,7 @@ function AdvancedSwapDetails(props: SwapDetailsProps & { open: boolean }) { + diff --git a/apps/web/src/components/swap/SwapHeader.test.tsx b/apps/web/src/components/swap/SwapHeader.test.tsx index 95c9c3f7f03..b07ba01e9f9 100644 --- a/apps/web/src/components/swap/SwapHeader.test.tsx +++ b/apps/web/src/components/swap/SwapHeader.test.tsx @@ -23,9 +23,11 @@ function Wrapper(props: PropsWithChildren) { inputCurrency: undefined, outputCurrency: undefined, }, + initialChainId: props.chainId ?? UniverseChainId.Mainnet, chainId: props.chainId ?? UniverseChainId.Mainnet, currentTab: SwapTab.Swap, setCurrentTab: props.setCurrentTab ?? jest.fn(), + isSwapAndLimitContext: true, }} > { const { asFragment } = render( - + , ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText('Swap')).toBeInTheDocument() @@ -62,7 +64,7 @@ describe('SwapHeader.tsx', () => { render( - + , ) act(() => { screen.getByText('Limit').click() diff --git a/apps/web/src/components/swap/SwapHeader.tsx b/apps/web/src/components/swap/SwapHeader.tsx index 9d5b9f115d2..d6a85f0d8c4 100644 --- a/apps/web/src/components/swap/SwapHeader.tsx +++ b/apps/web/src/components/swap/SwapHeader.tsx @@ -36,7 +36,7 @@ const PathnameToTab: { [key: string]: SwapTab } = { } export default function SwapHeader({ compact, syncTabToUrl }: { compact: boolean; syncTabToUrl: boolean }) { - const { chainId, currentTab, setCurrentTab } = useSwapAndLimitContext() + const { initialChainId, currentTab, setCurrentTab } = useSwapAndLimitContext() const { derivedSwapInfo: { trade, autoSlippage }, } = useSwapContext() @@ -65,7 +65,7 @@ export default function SwapHeader({ compact, syncTabToUrl }: { compact: boolean setCurrentTab(tab) } }, - [navigate, setCurrentTab, syncTabToUrl] + [navigate, setCurrentTab, syncTabToUrl], ) return ( @@ -115,7 +115,7 @@ export default function SwapHeader({ compact, syncTabToUrl }: { compact: boolean {currentTab === SwapTab.Swap && ( - + )} diff --git a/apps/web/src/components/swap/SwapLineItem.test.tsx b/apps/web/src/components/swap/SwapLineItem.test.tsx index 15aa82bbcd8..653185ffefd 100644 --- a/apps/web/src/components/swap/SwapLineItem.test.tsx +++ b/apps/web/src/components/swap/SwapLineItem.test.tsx @@ -43,7 +43,7 @@ function testTradeLineItems(trade: InterfaceTrade, props: Partial ( ))} - + , ) expect(asFragment()).toMatchSnapshot() } @@ -85,7 +85,7 @@ describe('SwapLineItem.tsx', () => { type={SwapLineItemType.EXPIRY} syncing={false} allowedSlippage={new Percent(0)} - /> + />, ) // TODO: mock Date Time so we can use a snapshot test here expect(screen.getByText('Expiry')).toBeInTheDocument() diff --git a/apps/web/src/components/swap/SwapLineItem.tsx b/apps/web/src/components/swap/SwapLineItem.tsx index 731d67138c9..41dcacf832e 100644 --- a/apps/web/src/components/swap/SwapLineItem.tsx +++ b/apps/web/src/components/swap/SwapLineItem.tsx @@ -7,13 +7,12 @@ import { TooltipSize } from 'components/Tooltip' import { DetailLineItem, LineItemData } from 'components/swap/DetailLineItem' import { GasBreakdownTooltip, UniswapXDescription } from 'components/swap/GasBreakdownTooltip' import GasEstimateTooltip from 'components/swap/GasEstimateTooltip' -import { MaxSlippageTooltip } from 'components/swap/MaxSlippageTooltip' import { RoutingTooltip, SwapRoute } from 'components/swap/SwapRoute' import TradePrice from 'components/swap/TradePrice' import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains' import { useUSDPrice } from 'hooks/useUSDPrice' import { Trans, t } from 'i18n' -import React, { useEffect, useState } from 'react' +import React, { ReactNode, useEffect, useState } from 'react' import { SpringValue, animated } from 'react-spring' import { InterfaceTrade, SubmittableTrade, TradeFillType } from 'state/routing/types' import { isLimitTrade, isPreviewTrade, isUniswapXTrade, isUniswapXTradeType } from 'state/routing/utils' @@ -56,26 +55,51 @@ const AutoBadge = styled(ThemedText.LabelMicro).attrs({ fontWeight: 535 })` } ` -export function FOTTooltipContent() { +function BaseTooltipContent({ children, url }: { children: ReactNode; url: string }) { return ( <> - {' '} - + {children} +
+ ) } +export function FOTTooltipContent() { + return ( + + + + ) +} + function SwapFeeTooltipContent({ hasFee }: { hasFee: boolean }) { const message = hasFee ? : return ( - <> - {message}{' '} - - - - + + {message} + + ) +} + +export function SlippageTooltipContent() { + return ( + + + + ) +} + +function MinimumOutputTooltipContent({ amount }: { amount: CurrencyAmount }) { + const { formatCurrencyAmount } = useFormatter() + const formattedAmount = formatCurrencyAmount({ amount, type: NumberType.SwapDetailsAmount }) + + return ( + + + ) } @@ -161,7 +185,7 @@ function useLineItem(props: SwapLineItemProps): LineItemData | undefined { case SwapLineItemType.MAX_SLIPPAGE: return { Label: () => , - TooltipBody: () => , + TooltipBody: () => , Value: () => ( {isAutoSlippage && } {formatPercent(allowedSlippage)} @@ -193,12 +217,11 @@ function useLineItem(props: SwapLineItemProps): LineItemData | undefined { loaderWidth: 70, } case SwapLineItemType.MINIMUM_OUTPUT: - if (trade.tradeType === TradeType.EXACT_OUTPUT) { - return - } return { Label: () => , - TooltipBody: () => , + TooltipBody: () => ( + + ), Value: () => , loaderWidth: 70, } diff --git a/apps/web/src/components/swap/SwapPreview.test.tsx b/apps/web/src/components/swap/SwapPreview.test.tsx index 5dd17cf3571..fc524d5688d 100644 --- a/apps/web/src/components/swap/SwapPreview.test.tsx +++ b/apps/web/src/components/swap/SwapPreview.test.tsx @@ -16,7 +16,7 @@ import { render, screen } from 'test-utils/render' describe('SwapPreview.tsx', () => { it('matches base snapshot, test trade exact input', () => { const { asFragment } = render( - + , ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText(/Output is estimated. You will receive at least /i)).toBeInTheDocument() @@ -30,7 +30,7 @@ describe('SwapPreview.tsx', () => { inputCurrency={ETH_MAINNET} trade={TEST_DUTCH_TRADE_ETH_INPUT} allowedSlippage={TEST_ALLOWED_SLIPPAGE} - /> + />, ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText(/Output is estimated. You will receive at least /i)).toBeInTheDocument() @@ -44,7 +44,7 @@ describe('SwapPreview.tsx', () => { inputCurrency={ETH_MAINNET} trade={TEST_DUTCH_V2_TRADE_ETH_INPUT} allowedSlippage={TEST_ALLOWED_SLIPPAGE} - /> + />, ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText(/Output is estimated. You will receive at least /i)).toBeInTheDocument() @@ -54,7 +54,7 @@ describe('SwapPreview.tsx', () => { it('test trade exact output, no recipient', () => { const { asFragment } = render( - + , ) expect(asFragment()).toMatchSnapshot() expect(screen.getByText(/Input is estimated. You will sell at most/i)).toBeInTheDocument() @@ -69,7 +69,7 @@ describe('SwapPreview.tsx', () => { inputCurrency={TEST_TOKEN_2} trade={PREVIEW_EXACT_IN_TRADE} allowedSlippage={TEST_ALLOWED_SLIPPAGE} - /> + />, ) expect(asFragment()).toMatchSnapshot() }) diff --git a/apps/web/src/components/swap/SwapRoute.tsx b/apps/web/src/components/swap/SwapRoute.tsx index 2faf983b8f1..34d50ef455a 100644 --- a/apps/web/src/components/swap/SwapRoute.tsx +++ b/apps/web/src/components/swap/SwapRoute.tsx @@ -4,7 +4,6 @@ import RoutingDiagram from 'components/RoutingDiagram/RoutingDiagram' import { RowBetween } from 'components/Row' import { UniswapXDescription } from 'components/swap/GasBreakdownTooltip' import { SUPPORTED_GAS_ESTIMATE_CHAIN_IDS } from 'constants/chains' -import useAutoRouterSupported from 'hooks/useAutoRouterSupported' import { Trans } from 'i18n' import { ClassicTrade, SubmittableTrade } from 'state/routing/types' import { isClassicTrade } from 'state/routing/utils' @@ -66,7 +65,7 @@ export function SwapRoute({ trade }: { trade: ClassicTrade }) { const routes = getRoutingDiagramEntries(trade) const gasPrice = useGasPrice(trade) - return useAutoRouterSupported() ? ( + return ( @@ -75,7 +74,5 @@ export function SwapRoute({ trade }: { trade: ClassicTrade }) { - ) : ( - ) } diff --git a/apps/web/src/components/swap/UnsupportedCurrencyFooter.test.tsx b/apps/web/src/components/swap/UnsupportedCurrencyFooter.test.tsx index c0fb0066d0f..905a6fee01a 100644 --- a/apps/web/src/components/swap/UnsupportedCurrencyFooter.test.tsx +++ b/apps/web/src/components/swap/UnsupportedCurrencyFooter.test.tsx @@ -38,21 +38,21 @@ describe('UnsupportedCurrencyFooter.tsx with unsupported tokens', () => { await act(() => userEvent.click(screen.getByTestId('read-more-button'))) expect(screen.getByText('Unsupported assets')).toBeInTheDocument() expect( - screen.getByText((content) => content.startsWith('Some assets are not available through this interface')) + screen.getByText((content) => content.startsWith('Some assets are not available through this interface')), ).toBeInTheDocument() expect(screen.getAllByTestId('unsupported-token-card').length).toBe(1) const unsupportedCard = screen.getByTestId('unsupported-token-card') expect(within(unsupportedCard).getByText(unsupportedTokenSymbol)).toBeInTheDocument() expect(within(unsupportedCard).getByText(unsupportedTokenAddress).closest('a')).toHaveAttribute( 'href', - unsupportedTokenExplorerLink + unsupportedTokenExplorerLink, ) await act(() => userEvent.click(screen.getByTestId('close-icon'))) await waitForElementToBeRemoved(rendered.queryByTestId('unsupported-token-card')) expect(rendered.queryByText('Unsupported Assets')).toBeNull() expect(rendered.queryByTestId('unsupported-token-card')).toBeNull() expect( - rendered.queryByText((content) => content.startsWith('Some assets are not available through this interface')) + rendered.queryByText((content) => content.startsWith('Some assets are not available through this interface')), ).toBeNull() }) }) @@ -69,14 +69,14 @@ describe('UnsupportedCurrencyFooter.tsx with no unsupported tokens', () => { await act(() => userEvent.click(screen.getByTestId('read-more-button'))) expect(screen.getByText('Unsupported assets')).toBeInTheDocument() expect( - screen.getByText((content) => content.startsWith('Some assets are not available through this interface')) + screen.getByText((content) => content.startsWith('Some assets are not available through this interface')), ).toBeInTheDocument() expect(rendered.queryByTestId('unsupported-token-card')).toBeNull() await act(() => userEvent.click(screen.getByTestId('close-icon'))) await waitForElementToBeRemoved(screen.getByText('Unsupported assets')) expect(rendered.queryByText('Unsupported assets')).toBeNull() expect( - rendered.queryByText((content) => content.startsWith('Some assets are not available through this interface')) + rendered.queryByText((content) => content.startsWith('Some assets are not available through this interface')), ).toBeNull() }) }) diff --git a/apps/web/src/components/swap/__snapshots__/SwapDetails.test.tsx.snap b/apps/web/src/components/swap/__snapshots__/SwapDetails.test.tsx.snap index bea3f9d46c3..dd23d5fd6ff 100644 --- a/apps/web/src/components/swap/__snapshots__/SwapDetails.test.tsx.snap +++ b/apps/web/src/components/swap/__snapshots__/SwapDetails.test.tsx.snap @@ -367,7 +367,7 @@ exports[`SwapDetails.tsx matches base snapshot, test trade exact input 1`] = ` } `; + +exports[`SwapDetails.tsx renders slippage tooltip 1`] = ` + + .c0 { + -webkit-text-decoration: none; + text-decoration: none; + cursor: pointer; + -webkit-transition-duration: 125ms; + transition-duration: 125ms; + color: #FC72FF; + stroke: #FC72FF; + font-weight: 500; +} + +.c0:hover { + opacity: 0.6; +} + +.c0:active { + opacity: 0.4; +} + + + + The maximum price movement before your transaction will revert. +
+ + Learn more + +
+
+
+`; diff --git a/apps/web/src/components/swap/__snapshots__/SwapDetailsDropdown.test.tsx.snap b/apps/web/src/components/swap/__snapshots__/SwapDetailsDropdown.test.tsx.snap index da724b854a8..6f2d6a364f2 100644 --- a/apps/web/src/components/swap/__snapshots__/SwapDetailsDropdown.test.tsx.snap +++ b/apps/web/src/components/swap/__snapshots__/SwapDetailsDropdown.test.tsx.snap @@ -376,6 +376,29 @@ exports[`SwapDetailsDropdown.tsx renders a trade 1`] = ` +
+
+
+ Receive at least +
+
+
+
+ 0.00000000000000098 DEF +
+
+
+
+
@@ -751,7 +718,7 @@ exports[`SwapLineItem.tsx dutch v2 order eth input 1`] = ` min-width: 0; } -.c21 { +.c19 { box-sizing: border-box; margin: 0; min-width: 0; @@ -811,7 +778,7 @@ exports[`SwapLineItem.tsx dutch v2 order eth input 1`] = ` gap: 8px; } -.c22 { +.c20 { width: auto; display: -webkit-box; display: -webkit-flex; @@ -882,19 +849,13 @@ exports[`SwapLineItem.tsx dutch v2 order eth input 1`] = ` opacity: 0.4; } -.c20 { - width: 100%; - height: 1px; - background-color: #22222212; -} - .c13 { z-index: 1070; pointer-events: none; visibility: hidden; opacity: 0; - -webkit-transition: visibility 150ms linear,opacity 150ms linear; - transition: visibility 150ms linear,opacity 150ms linear; + -webkit-transition: visibility 150ms linear, opacity 150ms linear; + transition: visibility 150ms linear, opacity 150ms linear; color: #7D7D7D; } @@ -986,21 +947,6 @@ exports[`SwapLineItem.tsx dutch v2 order eth input 1`] = ` overflow-wrap: break-word; } -.c19 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; - gap: 4px; -} - .c15 { display: inline; } @@ -1232,38 +1178,16 @@ exports[`SwapLineItem.tsx dutch v2 order eth input 1`] = `
- Fees are applied to ensure the best experience with Uniswap. There is no fee associated with this swap. + Fees are applied on select token pairs to ensure the best experience with Uniswap. There is no fee associated with this swap. +
- The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert. + If the price moves so that you will receive less than 0.0000000000000009 DEF, your transaction will revert. +
+
+ Learn more +
@@ -1486,7 +1420,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` min-width: 0; } -.c36 { +.c34 { box-sizing: border-box; margin: 0; min-width: 0; @@ -1546,7 +1480,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` gap: 8px; } -.c37 { +.c35 { width: 100%; display: -webkit-box; display: -webkit-flex; @@ -1571,14 +1505,14 @@ exports[`SwapLineItem.tsx exact input 1`] = ` justify-content: space-between; } -.c38 { +.c36 { -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; margin: -1px; } -.c38 > * { +.c36 > * { margin: 1px !important; } @@ -1605,7 +1539,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` letter-spacing: -0.01em; } -.c20 { +.c18 { -webkit-letter-spacing: -0.01em; -moz-letter-spacing: -0.01em; -ms-letter-spacing: -0.01em; @@ -1631,19 +1565,13 @@ exports[`SwapLineItem.tsx exact input 1`] = ` opacity: 0.4; } -.c19 { - width: 100%; - height: 1px; - background-color: #22222212; -} - .c13 { z-index: 1070; pointer-events: none; visibility: hidden; opacity: 0; - -webkit-transition: visibility 150ms linear,opacity 150ms linear; - transition: visibility 150ms linear,opacity 150ms linear; + -webkit-transition: visibility 150ms linear, opacity 150ms linear; + transition: visibility 150ms linear, opacity 150ms linear; color: #7D7D7D; } @@ -1725,7 +1653,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c21 { +.c19 { max-width: 400px; width: calc(100vw - 16px); cursor: default; @@ -1742,7 +1670,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c41 { +.c39 { max-width: 200px; width: calc(100vw - 16px); cursor: default; @@ -1769,22 +1697,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` overflow-wrap: break-word; } -.c18 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; - gap: 4px; -} - -.c22 { +.c20 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -1808,7 +1721,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` fill: #CECECE; } -.c32 { +.c30 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -1829,7 +1742,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` font-weight: 535; } -.c26 { +.c24 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -1843,7 +1756,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` left: 0; } -.c25 { +.c23 { position: relative; display: -webkit-box; display: -webkit-flex; @@ -1851,7 +1764,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` display: flex; } -.c27 { +.c25 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -1862,13 +1775,13 @@ exports[`SwapLineItem.tsx exact input 1`] = ` left: 0; } -.c27 img { +.c25 img { width: 20px; height: 20px; border-radius: 50%; } -.c40 { +.c38 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -1879,29 +1792,29 @@ exports[`SwapLineItem.tsx exact input 1`] = ` left: 0; } -.c40 img { +.c38 img { width: 10px; height: 20px; object-fit: cover; } -.c40 img:first-child { +.c38 img:first-child { border-radius: 10px 0 0 10px; object-position: 0 0; } -.c40 img:last-child { +.c38 img:last-child { border-radius: 0 10px 10px 0; object-position: 100% 0; } -.c28 { +.c26 { width: 10px; height: 20px; border-radius: 50%; } -.c23 { +.c21 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -1909,12 +1822,12 @@ exports[`SwapLineItem.tsx exact input 1`] = ` width: 100%; } -.c24 { +.c22 { display: grid; grid-template-columns: 24px 1fr 24px; } -.c29 { +.c27 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -1931,7 +1844,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` position: relative; } -.c39 { +.c37 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -1939,7 +1852,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` padding: 4px 4px; } -.c30 { +.c28 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -1954,11 +1867,11 @@ exports[`SwapLineItem.tsx exact input 1`] = ` opacity: 0.5; } -.c31 path { +.c29 path { stroke: #22222212; } -.c33 { +.c31 { background-color: #F9F9F9; border-radius: 8px; display: grid; @@ -1972,7 +1885,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` z-index: 1020; } -.c34 { +.c32 { background-color: #F9F9F9; border-radius: 4px; color: #7D7D7D; @@ -1981,7 +1894,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` z-index: 1021; } -.c35 { +.c33 { word-break: normal; } @@ -2167,38 +2080,16 @@ exports[`SwapLineItem.tsx exact input 1`] = `
- Fees are applied to ensure the best experience with Uniswap. There is no fee associated with this swap. + Fees are applied on select token pairs to ensure the best experience with Uniswap. There is no fee associated with this swap. +
- The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert. + If the price moves so that you will receive less than 0.00000000000000098 DEF, your transaction will revert. +
+
+ Learn more +
Uniswap Client
@@ -2328,34 +2229,34 @@ exports[`SwapLineItem.tsx exact input 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
@@ -2363,38 +2264,38 @@ exports[`SwapLineItem.tsx exact input 1`] = `
dot_line.svg
V3
100%
@@ -2403,29 +2304,29 @@ exports[`SwapLineItem.tsx exact input 1`] = ` >
1%
@@ -2437,7 +2338,7 @@ exports[`SwapLineItem.tsx exact input 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
ABC/DEF 1% pool
@@ -2449,19 +2350,19 @@ exports[`SwapLineItem.tsx exact input 1`] = `
@@ -2506,7 +2407,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` min-width: 0; } -.c36 { +.c34 { box-sizing: border-box; margin: 0; min-width: 0; @@ -2566,7 +2467,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` gap: 8px; } -.c37 { +.c35 { width: 100%; display: -webkit-box; display: -webkit-flex; @@ -2591,14 +2492,14 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` justify-content: space-between; } -.c38 { +.c36 { -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; margin: -1px; } -.c38 > * { +.c36 > * { margin: 1px !important; } @@ -2625,7 +2526,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` letter-spacing: -0.01em; } -.c20 { +.c18 { -webkit-letter-spacing: -0.01em; -moz-letter-spacing: -0.01em; -ms-letter-spacing: -0.01em; @@ -2651,19 +2552,13 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` opacity: 0.4; } -.c19 { - width: 100%; - height: 1px; - background-color: #22222212; -} - .c13 { z-index: 1070; pointer-events: none; visibility: hidden; opacity: 0; - -webkit-transition: visibility 150ms linear,opacity 150ms linear; - transition: visibility 150ms linear,opacity 150ms linear; + -webkit-transition: visibility 150ms linear, opacity 150ms linear; + transition: visibility 150ms linear, opacity 150ms linear; color: #7D7D7D; } @@ -2745,7 +2640,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c21 { +.c19 { max-width: 400px; width: calc(100vw - 16px); cursor: default; @@ -2762,7 +2657,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c41 { +.c39 { max-width: 200px; width: calc(100vw - 16px); cursor: default; @@ -2789,22 +2684,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` overflow-wrap: break-word; } -.c18 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; - gap: 4px; -} - -.c22 { +.c20 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -2828,7 +2708,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` fill: #CECECE; } -.c32 { +.c30 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -2849,7 +2729,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` font-weight: 535; } -.c26 { +.c24 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -2863,7 +2743,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` left: 0; } -.c25 { +.c23 { position: relative; display: -webkit-box; display: -webkit-flex; @@ -2871,7 +2751,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` display: flex; } -.c27 { +.c25 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -2882,13 +2762,13 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` left: 0; } -.c27 img { +.c25 img { width: 20px; height: 20px; border-radius: 50%; } -.c40 { +.c38 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -2899,29 +2779,29 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` left: 0; } -.c40 img { +.c38 img { width: 10px; height: 20px; object-fit: cover; } -.c40 img:first-child { +.c38 img:first-child { border-radius: 10px 0 0 10px; object-position: 0 0; } -.c40 img:last-child { +.c38 img:last-child { border-radius: 0 10px 10px 0; object-position: 100% 0; } -.c28 { +.c26 { width: 10px; height: 20px; border-radius: 50%; } -.c23 { +.c21 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -2929,12 +2809,12 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` width: 100%; } -.c24 { +.c22 { display: grid; grid-template-columns: 24px 1fr 24px; } -.c29 { +.c27 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -2951,7 +2831,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` position: relative; } -.c39 { +.c37 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -2959,7 +2839,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` padding: 4px 4px; } -.c30 { +.c28 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -2974,11 +2854,11 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` opacity: 0.5; } -.c31 path { +.c29 path { stroke: #22222212; } -.c33 { +.c31 { background-color: #F9F9F9; border-radius: 8px; display: grid; @@ -2992,7 +2872,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` z-index: 1020; } -.c34 { +.c32 { background-color: #F9F9F9; border-radius: 4px; color: #7D7D7D; @@ -3001,7 +2881,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` z-index: 1021; } -.c35 { +.c33 { word-break: normal; } @@ -3187,38 +3067,16 @@ exports[`SwapLineItem.tsx exact input api 1`] = `
- Fees are applied to ensure the best experience with Uniswap. There is no fee associated with this swap. + Fees are applied on select token pairs to ensure the best experience with Uniswap. There is no fee associated with this swap. +
- The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert. + If the price moves so that you will receive less than 0.00000000000000098 DEF, your transaction will revert. +
+
+ Learn more +
Uniswap API
@@ -3348,34 +3216,34 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
@@ -3383,38 +3251,38 @@ exports[`SwapLineItem.tsx exact input api 1`] = `
dot_line.svg
V3
100%
@@ -3423,29 +3291,29 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` >
1%
@@ -3457,7 +3325,7 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
ABC/DEF 1% pool
@@ -3469,19 +3337,19 @@ exports[`SwapLineItem.tsx exact input api 1`] = `
@@ -3526,7 +3394,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` min-width: 0; } -.c36 { +.c34 { box-sizing: border-box; margin: 0; min-width: 0; @@ -3586,7 +3454,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` gap: 8px; } -.c37 { +.c35 { width: 100%; display: -webkit-box; display: -webkit-flex; @@ -3611,14 +3479,14 @@ exports[`SwapLineItem.tsx exact output 1`] = ` justify-content: space-between; } -.c38 { +.c36 { -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; margin: -1px; } -.c38 > * { +.c36 > * { margin: 1px !important; } @@ -3645,7 +3513,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` letter-spacing: -0.01em; } -.c20 { +.c18 { -webkit-letter-spacing: -0.01em; -moz-letter-spacing: -0.01em; -ms-letter-spacing: -0.01em; @@ -3671,19 +3539,13 @@ exports[`SwapLineItem.tsx exact output 1`] = ` opacity: 0.4; } -.c19 { - width: 100%; - height: 1px; - background-color: #22222212; -} - .c13 { z-index: 1070; pointer-events: none; visibility: hidden; opacity: 0; - -webkit-transition: visibility 150ms linear,opacity 150ms linear; - transition: visibility 150ms linear,opacity 150ms linear; + -webkit-transition: visibility 150ms linear, opacity 150ms linear; + transition: visibility 150ms linear, opacity 150ms linear; color: #7D7D7D; } @@ -3765,7 +3627,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c21 { +.c19 { max-width: 400px; width: calc(100vw - 16px); cursor: default; @@ -3782,7 +3644,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c41 { +.c39 { max-width: 200px; width: calc(100vw - 16px); cursor: default; @@ -3809,22 +3671,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` overflow-wrap: break-word; } -.c18 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; - gap: 4px; -} - -.c22 { +.c20 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -3848,7 +3695,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` fill: #CECECE; } -.c32 { +.c30 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -3869,7 +3716,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` font-weight: 535; } -.c26 { +.c24 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -3883,7 +3730,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` left: 0; } -.c25 { +.c23 { position: relative; display: -webkit-box; display: -webkit-flex; @@ -3891,7 +3738,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` display: flex; } -.c27 { +.c25 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -3902,13 +3749,13 @@ exports[`SwapLineItem.tsx exact output 1`] = ` left: 0; } -.c27 img { +.c25 img { width: 20px; height: 20px; border-radius: 50%; } -.c40 { +.c38 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -3919,29 +3766,29 @@ exports[`SwapLineItem.tsx exact output 1`] = ` left: 0; } -.c40 img { +.c38 img { width: 10px; height: 20px; object-fit: cover; } -.c40 img:first-child { +.c38 img:first-child { border-radius: 10px 0 0 10px; object-position: 0 0; } -.c40 img:last-child { +.c38 img:last-child { border-radius: 0 10px 10px 0; object-position: 100% 0; } -.c28 { +.c26 { width: 10px; height: 20px; border-radius: 50%; } -.c23 { +.c21 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -3949,12 +3796,12 @@ exports[`SwapLineItem.tsx exact output 1`] = ` width: 100%; } -.c24 { +.c22 { display: grid; grid-template-columns: 24px 1fr 24px; } -.c29 { +.c27 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -3971,7 +3818,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` position: relative; } -.c39 { +.c37 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -3979,7 +3826,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` padding: 4px 4px; } -.c30 { +.c28 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -3994,11 +3841,11 @@ exports[`SwapLineItem.tsx exact output 1`] = ` opacity: 0.5; } -.c31 path { +.c29 path { stroke: #22222212; } -.c33 { +.c31 { background-color: #F9F9F9; border-radius: 8px; display: grid; @@ -4012,7 +3859,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` z-index: 1020; } -.c34 { +.c32 { background-color: #F9F9F9; border-radius: 4px; color: #7D7D7D; @@ -4021,7 +3868,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` z-index: 1021; } -.c35 { +.c33 { word-break: normal; } @@ -4207,38 +4054,16 @@ exports[`SwapLineItem.tsx exact output 1`] = `
- Fees are applied to ensure the best experience with Uniswap. There is no fee associated with this swap. + Fees are applied on select token pairs to ensure the best experience with Uniswap. There is no fee associated with this swap. +
+
+
Uniswap Client
@@ -4368,34 +4244,34 @@ exports[`SwapLineItem.tsx exact output 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
@@ -4403,38 +4279,38 @@ exports[`SwapLineItem.tsx exact output 1`] = `
dot_line.svg
V3
100%
@@ -4443,29 +4319,29 @@ exports[`SwapLineItem.tsx exact output 1`] = ` >
0.3%
@@ -4477,7 +4353,7 @@ exports[`SwapLineItem.tsx exact output 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
ABC/GHI 0.3% pool
@@ -4489,19 +4365,19 @@ exports[`SwapLineItem.tsx exact output 1`] = `
@@ -4546,7 +4422,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` min-width: 0; } -.c36 { +.c34 { box-sizing: border-box; margin: 0; min-width: 0; @@ -4606,7 +4482,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` gap: 8px; } -.c37 { +.c35 { width: 100%; display: -webkit-box; display: -webkit-flex; @@ -4631,14 +4507,14 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` justify-content: space-between; } -.c38 { +.c36 { -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; margin: -1px; } -.c38 > * { +.c36 > * { margin: 1px !important; } @@ -4665,7 +4541,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` letter-spacing: -0.01em; } -.c20 { +.c18 { -webkit-letter-spacing: -0.01em; -moz-letter-spacing: -0.01em; -ms-letter-spacing: -0.01em; @@ -4691,19 +4567,13 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` opacity: 0.4; } -.c19 { - width: 100%; - height: 1px; - background-color: #22222212; -} - .c13 { z-index: 1070; pointer-events: none; visibility: hidden; opacity: 0; - -webkit-transition: visibility 150ms linear,opacity 150ms linear; - transition: visibility 150ms linear,opacity 150ms linear; + -webkit-transition: visibility 150ms linear, opacity 150ms linear; + transition: visibility 150ms linear, opacity 150ms linear; color: #7D7D7D; } @@ -4785,7 +4655,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c21 { +.c19 { max-width: 400px; width: calc(100vw - 16px); cursor: default; @@ -4802,7 +4672,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c41 { +.c39 { max-width: 200px; width: calc(100vw - 16px); cursor: default; @@ -4829,22 +4699,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` overflow-wrap: break-word; } -.c18 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; - gap: 4px; -} - -.c22 { +.c20 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -4868,7 +4723,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` fill: #CECECE; } -.c32 { +.c30 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -4889,7 +4744,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` font-weight: 535; } -.c26 { +.c24 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -4903,7 +4758,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` left: 0; } -.c25 { +.c23 { position: relative; display: -webkit-box; display: -webkit-flex; @@ -4911,7 +4766,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` display: flex; } -.c27 { +.c25 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -4922,13 +4777,13 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` left: 0; } -.c27 img { +.c25 img { width: 20px; height: 20px; border-radius: 50%; } -.c40 { +.c38 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -4939,29 +4794,29 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` left: 0; } -.c40 img { +.c38 img { width: 10px; height: 20px; object-fit: cover; } -.c40 img:first-child { +.c38 img:first-child { border-radius: 10px 0 0 10px; object-position: 0 0; } -.c40 img:last-child { +.c38 img:last-child { border-radius: 0 10px 10px 0; object-position: 100% 0; } -.c28 { +.c26 { width: 10px; height: 20px; border-radius: 50%; } -.c23 { +.c21 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -4969,12 +4824,12 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` width: 100%; } -.c24 { +.c22 { display: grid; grid-template-columns: 24px 1fr 24px; } -.c29 { +.c27 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -4991,7 +4846,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` position: relative; } -.c39 { +.c37 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -4999,7 +4854,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` padding: 4px 4px; } -.c30 { +.c28 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -5014,11 +4869,11 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` opacity: 0.5; } -.c31 path { +.c29 path { stroke: #22222212; } -.c33 { +.c31 { background-color: #F9F9F9; border-radius: 8px; display: grid; @@ -5032,7 +4887,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` z-index: 1020; } -.c34 { +.c32 { background-color: #F9F9F9; border-radius: 4px; color: #7D7D7D; @@ -5041,7 +4896,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` z-index: 1021; } -.c35 { +.c33 { word-break: normal; } @@ -5224,7 +5079,8 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
- Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees. + Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees. +
-
- Fees are applied to ensure the best experience with Uniswap. There is no fee associated with this swap. + Fees are applied on select token pairs to ensure the best experience with Uniswap. There is no fee associated with this swap. +
- The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert. + If the price moves so that you will receive less than 0.00000000000000098 DEF, your transaction will revert. +
+
+ Learn more +
Uniswap API
@@ -5441,34 +5285,34 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
@@ -5476,38 +5320,38 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
dot_line.svg
V3
100%
@@ -5516,29 +5360,29 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` >
1%
@@ -5550,7 +5394,7 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
ABC/DEF 1% pool
@@ -5562,19 +5406,19 @@ exports[`SwapLineItem.tsx fee on buy 1`] = `
@@ -5619,7 +5463,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` min-width: 0; } -.c36 { +.c34 { box-sizing: border-box; margin: 0; min-width: 0; @@ -5679,7 +5523,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` gap: 8px; } -.c37 { +.c35 { width: 100%; display: -webkit-box; display: -webkit-flex; @@ -5704,14 +5548,14 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` justify-content: space-between; } -.c38 { +.c36 { -webkit-flex-wrap: wrap; -ms-flex-wrap: wrap; flex-wrap: wrap; margin: -1px; } -.c38 > * { +.c36 > * { margin: 1px !important; } @@ -5738,7 +5582,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` letter-spacing: -0.01em; } -.c20 { +.c18 { -webkit-letter-spacing: -0.01em; -moz-letter-spacing: -0.01em; -ms-letter-spacing: -0.01em; @@ -5764,19 +5608,13 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` opacity: 0.4; } -.c19 { - width: 100%; - height: 1px; - background-color: #22222212; -} - .c13 { z-index: 1070; pointer-events: none; visibility: hidden; opacity: 0; - -webkit-transition: visibility 150ms linear,opacity 150ms linear; - transition: visibility 150ms linear,opacity 150ms linear; + -webkit-transition: visibility 150ms linear, opacity 150ms linear; + transition: visibility 150ms linear, opacity 150ms linear; color: #7D7D7D; } @@ -5858,7 +5696,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c21 { +.c19 { max-width: 400px; width: calc(100vw - 16px); cursor: default; @@ -5875,7 +5713,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` box-shadow: 0 4px 8px 0 rgba(47,128,237,0.1); } -.c41 { +.c39 { max-width: 200px; width: calc(100vw - 16px); cursor: default; @@ -5902,22 +5740,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` overflow-wrap: break-word; } -.c18 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; - gap: 4px; -} - -.c22 { +.c20 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -5941,7 +5764,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` fill: #CECECE; } -.c32 { +.c30 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -5962,7 +5785,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` font-weight: 535; } -.c26 { +.c24 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -5976,7 +5799,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` left: 0; } -.c25 { +.c23 { position: relative; display: -webkit-box; display: -webkit-flex; @@ -5984,7 +5807,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` display: flex; } -.c27 { +.c25 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -5995,13 +5818,13 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` left: 0; } -.c27 img { +.c25 img { width: 20px; height: 20px; border-radius: 50%; } -.c40 { +.c38 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -6012,29 +5835,29 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` left: 0; } -.c40 img { +.c38 img { width: 10px; height: 20px; object-fit: cover; } -.c40 img:first-child { +.c38 img:first-child { border-radius: 10px 0 0 10px; object-position: 0 0; } -.c40 img:last-child { +.c38 img:last-child { border-radius: 0 10px 10px 0; object-position: 100% 0; } -.c28 { +.c26 { width: 10px; height: 20px; border-radius: 50%; } -.c23 { +.c21 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -6042,12 +5865,12 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` width: 100%; } -.c24 { +.c22 { display: grid; grid-template-columns: 24px 1fr 24px; } -.c29 { +.c27 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -6064,7 +5887,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` position: relative; } -.c39 { +.c37 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -6072,7 +5895,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` padding: 4px 4px; } -.c30 { +.c28 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -6087,11 +5910,11 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` opacity: 0.5; } -.c31 path { +.c29 path { stroke: #22222212; } -.c33 { +.c31 { background-color: #F9F9F9; border-radius: 8px; display: grid; @@ -6105,7 +5928,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` z-index: 1020; } -.c34 { +.c32 { background-color: #F9F9F9; border-radius: 4px; color: #7D7D7D; @@ -6114,7 +5937,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` z-index: 1021; } -.c35 { +.c33 { word-break: normal; } @@ -6297,7 +6120,8 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
- Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees. + Some tokens take a fee when they are bought or sold, which is set by the token issuer. Uniswap does not receive any of these fees. +
-
- Fees are applied to ensure the best experience with Uniswap. There is no fee associated with this swap. + Fees are applied on select token pairs to ensure the best experience with Uniswap. There is no fee associated with this swap. +
- The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert. + If the price moves so that you will receive less than 0.00000000000000098 DEF, your transaction will revert. +
+
+ Learn more +
Uniswap API
@@ -6514,34 +6326,34 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
@@ -6549,38 +6361,38 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
dot_line.svg
V3
100%
@@ -6589,29 +6401,29 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` >
1%
@@ -6623,7 +6435,7 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` style="position: fixed; left: 0px; top: 0px;" >
ABC/DEF 1% pool
@@ -6635,19 +6447,19 @@ exports[`SwapLineItem.tsx fee on sell 1`] = `
@@ -6774,19 +6586,13 @@ exports[`SwapLineItem.tsx preview exact in 1`] = ` opacity: 0.4; } -.c16 { - width: 100%; - height: 1px; - background-color: #22222212; -} - .c8 { z-index: 1070; pointer-events: none; visibility: hidden; opacity: 0; - -webkit-transition: visibility 150ms linear,opacity 150ms linear; - transition: visibility 150ms linear,opacity 150ms linear; + -webkit-transition: visibility 150ms linear, opacity 150ms linear; + transition: visibility 150ms linear, opacity 150ms linear; color: #7D7D7D; } @@ -6873,7 +6679,7 @@ exports[`SwapLineItem.tsx preview exact in 1`] = ` color: #7D7D7D; } -.c17 { +.c15 { cursor: auto; color: #7D7D7D; } @@ -6883,21 +6689,6 @@ exports[`SwapLineItem.tsx preview exact in 1`] = ` overflow-wrap: break-word; } -.c15 { - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - -webkit-box-pack: start; - -webkit-justify-content: flex-start; - -ms-flex-pack: start; - justify-content: flex-start; - gap: 4px; -} - .c14 { display: -webkit-box; display: -webkit-flex; @@ -7020,38 +6811,16 @@ exports[`SwapLineItem.tsx preview exact in 1`] = `
Fee @@ -7114,7 +6883,16 @@ exports[`SwapLineItem.tsx preview exact in 1`] = `
- The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert. + If the price moves so that you will receive less than 0.00000000000000098 DEF, your transaction will revert. +
+ + Learn more +
Order routing diff --git a/apps/web/src/components/swap/confirmPriceImpactWithoutFee.ts b/apps/web/src/components/swap/confirmPriceImpactWithoutFee.ts index 81824fe2f6f..dd2b55b1b84 100644 --- a/apps/web/src/components/swap/confirmPriceImpactWithoutFee.ts +++ b/apps/web/src/components/swap/confirmPriceImpactWithoutFee.ts @@ -11,15 +11,15 @@ export default function confirmPriceImpactWithoutFee(priceImpactWithoutFee: Perc return ( window.prompt( `This swap has a price impact of at least ${PRICE_IMPACT_WITHOUT_FEE_CONFIRM_MIN.toFixed( - 0 - )}%. Please type the word "confirm" to continue with this swap.` + 0, + )}%. Please type the word "confirm" to continue with this swap.`, ) === 'confirm' ) } else if (!priceImpactWithoutFee.lessThan(ALLOWED_PRICE_IMPACT_HIGH)) { return window.confirm( `This swap has a price impact of at least ${ALLOWED_PRICE_IMPACT_HIGH.toFixed( - 0 - )}%. Please confirm that you would like to continue with this swap.` + 0, + )}%. Please confirm that you would like to continue with this swap.`, ) } return true diff --git a/apps/web/src/connection/web3reactShim.ts b/apps/web/src/connection/web3reactShim.ts index 842fc4aa644..52ca2978221 100644 --- a/apps/web/src/connection/web3reactShim.ts +++ b/apps/web/src/connection/web3reactShim.ts @@ -14,6 +14,6 @@ export function useWeb3React() { chainId: account.chainId ?? UniverseChainId.Mainnet, provider, }), - [account.address, account.chainId, provider] + [account.address, account.chainId, provider], ) } diff --git a/apps/web/src/constants/chains.ts b/apps/web/src/constants/chains.ts index 8967b0f39da..af9678c43e6 100644 --- a/apps/web/src/constants/chains.ts +++ b/apps/web/src/constants/chains.ts @@ -32,7 +32,7 @@ function useFeatureFlaggedChainIds(): Partial ({ [UniverseChainId.Zora]: zoraEnabled, }), - [zoraEnabled] + [zoraEnabled], ) } @@ -51,7 +51,7 @@ export function useIsSupportedChainIdCallback() { const chainIsNotEnabled = featureFlaggedChains[chainId as SupportedInterfaceChainId] === false return chainIsNotEnabled ? false : isSupportedChainId(chainId) }, - [featureFlaggedChains] + [featureFlaggedChains], ) } @@ -90,12 +90,12 @@ export function getChain({ return chainId ? UNIVERSE_CHAIN_INFO[chainId] : withFallback - ? UNIVERSE_CHAIN_INFO[UniverseChainId.Mainnet] - : undefined + ? UNIVERSE_CHAIN_INFO[UniverseChainId.Mainnet] + : undefined } export const CHAIN_IDS_TO_NAMES = Object.fromEntries( - Object.entries(UNIVERSE_CHAIN_INFO).map(([key, value]) => [key, value.interfaceName]) + Object.entries(UNIVERSE_CHAIN_INFO).map(([key, value]) => [key, value.interfaceName]), ) as { [chainId in SupportedInterfaceChainId]: string } export const GQL_MAINNET_CHAINS = Object.values(UNIVERSE_CHAIN_INFO) @@ -110,7 +110,7 @@ const GQL_TESTNET_CHAINS = Object.values(UNIVERSE_CHAIN_INFO) export const UX_SUPPORTED_GQL_CHAINS = [...GQL_MAINNET_CHAINS, ...GQL_TESTNET_CHAINS] export const CHAIN_ID_TO_BACKEND_NAME = Object.fromEntries( - Object.entries(UNIVERSE_CHAIN_INFO).map(([key, value]) => [key, value.backendChain.chain]) + Object.entries(UNIVERSE_CHAIN_INFO).map(([key, value]) => [key, value.backendChain.chain]), ) as { [chainId in SupportedInterfaceChainId]: InterfaceGqlChain } export function chainIdToBackendChain(options: { chainId: UniverseChainId }): InterfaceGqlChain @@ -129,14 +129,14 @@ export function chainIdToBackendChain({ return chainId ? CHAIN_ID_TO_BACKEND_NAME[chainId] : withFallback - ? CHAIN_ID_TO_BACKEND_NAME[UniverseChainId.Mainnet] - : undefined + ? CHAIN_ID_TO_BACKEND_NAME[UniverseChainId.Mainnet] + : undefined } export const CHAIN_NAME_TO_CHAIN_ID = Object.fromEntries( Object.entries(UNIVERSE_CHAIN_INFO) .filter(([, value]) => !value.backendChain.isSecondaryChain) - .map(([key, value]) => [value.backendChain.chain, parseInt(key) as SupportedInterfaceChainId]) + .map(([key, value]) => [value.backendChain.chain, parseInt(key) as SupportedInterfaceChainId]), ) as { [chain in InterfaceGqlChain]: SupportedInterfaceChainId } export const SUPPORTED_GAS_ESTIMATE_CHAIN_IDS = Object.keys(UNIVERSE_CHAIN_INFO) @@ -179,13 +179,13 @@ export const BACKEND_SUPPORTED_CHAINS = Object.keys(UNIVERSE_CHAIN_INFO) .map((key) => UNIVERSE_CHAIN_INFO[parseInt(key) as SupportedInterfaceChainId].backendChain.chain as InterfaceGqlChain) export const BACKEND_NOT_YET_SUPPORTED_CHAIN_IDS = GQL_MAINNET_CHAINS.filter( - (chain) => !BACKEND_SUPPORTED_CHAINS.includes(chain) + (chain) => !BACKEND_SUPPORTED_CHAINS.includes(chain), ).map((chain) => CHAIN_NAME_TO_CHAIN_ID[chain]) as [SupportedInterfaceChainId] export const INFURA_PREFIX_TO_CHAIN_ID: { [prefix: string]: SupportedInterfaceChainId } = Object.fromEntries( Object.entries(UNIVERSE_CHAIN_INFO) .filter(([, value]) => !!value.infuraPrefix) - .map(([key, value]) => [value.infuraPrefix, parseInt(key) as SupportedInterfaceChainId]) + .map(([key, value]) => [value.infuraPrefix, parseInt(key) as SupportedInterfaceChainId]), ) /** @@ -211,7 +211,7 @@ export function isStablecoin(currency?: Currency): boolean { } return getChain({ chainId: currency.chainId as SupportedInterfaceChainId }).stablecoins.some((stablecoin) => - stablecoin.equals(currency) + stablecoin.equals(currency), ) } diff --git a/apps/web/src/constants/providers.ts b/apps/web/src/constants/providers.ts index 12b73d376e9..4cba0f9fa13 100644 --- a/apps/web/src/constants/providers.ts +++ b/apps/web/src/constants/providers.ts @@ -8,12 +8,12 @@ function getAppProvider(chainId: SupportedInterfaceChainId) { const info = UNIVERSE_CHAIN_INFO[chainId] return new AppJsonRpcProvider( info.rpcUrls.appOnly.http.map( - (url) => new ConfiguredJsonRpcProvider(url, { chainId, name: CHAIN_IDS_TO_NAMES[chainId] }) - ) + (url) => new ConfiguredJsonRpcProvider(url, { chainId, name: CHAIN_IDS_TO_NAMES[chainId] }), + ), ) } /** These are the only JsonRpcProviders used directly by the interface. */ export const RPC_PROVIDERS = Object.fromEntries( - WEB_SUPPORTED_CHAIN_IDS.map((chain) => [chain as SupportedInterfaceChainId, getAppProvider(chain)]) + WEB_SUPPORTED_CHAIN_IDS.map((chain) => [chain as SupportedInterfaceChainId, getAppProvider(chain)]), ) as Record diff --git a/apps/web/src/constants/routing.ts b/apps/web/src/constants/routing.ts index 8d81a446d68..06cb0d1d8a6 100644 --- a/apps/web/src/constants/routing.ts +++ b/apps/web/src/constants/routing.ts @@ -62,7 +62,7 @@ type ChainCurrencyList = { const WRAPPED_NATIVE_CURRENCIES_ONLY: ChainTokenList = Object.fromEntries( Object.entries(WRAPPED_NATIVE_CURRENCY) .map(([key, value]) => [key, [value]]) - .filter(Boolean) + .filter(Boolean), ) export function buildCurrencyInfo(commonBase: Currency): CurrencyInfo { @@ -122,7 +122,7 @@ export const COMMON_BASES: ChainCurrencyList = { WETH9[UniverseChainId.Optimism], ].map(buildCurrencyInfo), [UniverseChainId.OptimismGoerli]: [nativeOnChain(UniverseChainId.OptimismGoerli), USDC_OPTIMISM_GOERLI].map( - buildCurrencyInfo + buildCurrencyInfo, ), [UniverseChainId.Base]: [ diff --git a/apps/web/src/constants/tokens.ts b/apps/web/src/constants/tokens.ts index a72d80beb8f..87e90aa61a8 100644 --- a/apps/web/src/constants/tokens.ts +++ b/apps/web/src/constants/tokens.ts @@ -10,70 +10,70 @@ export const USDC_MAINNET = new Token( '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_OPTIMISM = new Token( UniverseChainId.Optimism, '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_OPTIMISM_GOERLI = new Token( UniverseChainId.OptimismGoerli, '0xe05606174bac4A6364B31bd0eCA4bf4dD368f8C6', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_ARBITRUM = new Token( UniverseChainId.ArbitrumOne, '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_ARBITRUM_GOERLI = new Token( UniverseChainId.ArbitrumGoerli, '0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_POLYGON = new Token( UniverseChainId.Polygon, '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) export const USDC_POLYGON_MUMBAI = new Token( UniverseChainId.PolygonMumbai, '0x0fa8781a83e46826621b3bc094ea2a0212e71b23', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) export const USDC_CELO = new Token( UniverseChainId.Celo, '0xceba9300f2b948710d2653dd7b07f33a8b32118c', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) export const USDC_CELO_ALFAJORES = new Token( UniverseChainId.CeloAlfajores, '0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B', 6, 'USDC', - 'USDC CELO Testnet' + 'USDC CELO Testnet', ) export const USDC_BASE = new Token( UniverseChainId.Base, '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) export const DAI = new Token( @@ -81,42 +81,42 @@ export const DAI = new Token( '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', - 'Dai Stablecoin' + 'Dai Stablecoin', ) export const DAI_ARBITRUM_ONE = new Token( UniverseChainId.ArbitrumOne, '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', 18, 'DAI', - 'Dai stable coin' + 'Dai stable coin', ) export const DAI_OPTIMISM = new Token( UniverseChainId.Optimism, '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', 18, 'DAI', - 'Dai stable coin' + 'Dai stable coin', ) export const MATIC_MAINNET = new Token( UniverseChainId.Mainnet, '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0', 18, 'MATIC', - 'Polygon Matic' + 'Polygon Matic', ) export const MATIC_POLYGON = new Token( UniverseChainId.Polygon, '0x0000000000000000000000000000000000001010', 18, 'MATIC', - 'Matic' + 'Matic', ) export const DAI_POLYGON = new Token( UniverseChainId.Polygon, '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', 18, 'DAI', - 'Dai Stablecoin' + 'Dai Stablecoin', ) export const USDT_POLYGON = new Token( @@ -124,63 +124,63 @@ export const USDT_POLYGON = new Token( '0xc2132d05d31c914a87c6611c10748aeb04b58e8f', 6, 'USDT', - 'Tether USD' + 'Tether USD', ) export const WBTC_POLYGON = new Token( UniverseChainId.Polygon, '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6', 8, 'WBTC', - 'Wrapped BTC' + 'Wrapped BTC', ) export const USDT = new Token( UniverseChainId.Mainnet, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', - 'Tether USD' + 'Tether USD', ) export const USDT_ARBITRUM_ONE = new Token( UniverseChainId.ArbitrumOne, '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', 6, 'USDT', - 'Tether USD' + 'Tether USD', ) export const USDT_OPTIMISM = new Token( UniverseChainId.Optimism, '0x94b008aA00579c1307B0EF2c499aD98a8ce58e58', 6, 'USDT', - 'Tether USD' + 'Tether USD', ) export const WBTC = new Token( UniverseChainId.Mainnet, '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 8, 'WBTC', - 'Wrapped BTC' + 'Wrapped BTC', ) export const WBTC_ARBITRUM_ONE = new Token( UniverseChainId.ArbitrumOne, '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', 8, 'WBTC', - 'Wrapped BTC' + 'Wrapped BTC', ) export const WBTC_OPTIMISM = new Token( UniverseChainId.Optimism, '0x68f180fcCe6836688e9084f035309E29Bf0A2095', 8, 'WBTC', - 'Wrapped BTC' + 'Wrapped BTC', ) export const WETH_POLYGON_MUMBAI = new Token( UniverseChainId.PolygonMumbai, '0xa6fa4fb5f76172d178d61b04b0ecd319c5d1c0aa', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ) export const WETH_POLYGON = new Token( @@ -188,7 +188,7 @@ export const WETH_POLYGON = new Token( '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ) const CELO_CELO = new Token(UniverseChainId.Celo, '0x471EcE3750Da237f93B8E339c536989b8978a438', 18, 'CELO', 'Celo') export const CUSD_CELO = new Token( @@ -196,49 +196,49 @@ export const CUSD_CELO = new Token( '0x765DE816845861e75A25fCA122bb6898B8B1282a', 18, 'cUSD', - 'Celo Dollar' + 'Celo Dollar', ) export const CEUR_CELO = new Token( UniverseChainId.Celo, '0xD8763CBa276a3738E6DE85b4b3bF5FDed6D6cA73', 18, 'cEUR', - 'Celo Euro Stablecoin' + 'Celo Euro Stablecoin', ) export const PORTAL_ETH_CELO = new Token( UniverseChainId.Celo, '0x66803FB87aBd4aaC3cbB3fAd7C3aa01f6F3FB207', 18, 'ETH', - 'Portal Ether' + 'Portal Ether', ) export const WBTC_CELO = new Token( UniverseChainId.Celo, '0xd71Ffd0940c920786eC4DbB5A12306669b5b81EF', 18, 'WBTC', - 'Wrapped BTC' + 'Wrapped BTC', ) const CELO_CELO_ALFAJORES = new Token( UniverseChainId.CeloAlfajores, '0xF194afDf50B03e69Bd7D057c1Aa9e10c9954E4C9', 18, 'CELO', - 'Celo' + 'Celo', ) export const CUSD_CELO_ALFAJORES = new Token( UniverseChainId.CeloAlfajores, '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1', 18, 'CUSD', - 'Celo Dollar' + 'Celo Dollar', ) export const CEUR_CELO_ALFAJORES = new Token( UniverseChainId.CeloAlfajores, '0x10c892A6EC43a53E45D0B916B4b7D383B1b78C0F', 18, 'CEUR', - 'Celo Euro Stablecoin' + 'Celo Euro Stablecoin', ) export const USDC_BSC = new Token(UniverseChainId.Bnb, '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', 18, 'USDC', 'USDC') @@ -248,7 +248,7 @@ export const ETH_BSC = new Token( '0x2170Ed0880ac9A755fd29B2688956BD959F933F8', 18, 'ETH', - 'Ethereum' + 'Ethereum', ) export const BTC_BSC = new Token(UniverseChainId.Bnb, '0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c', 18, 'BTCB', 'BTCB') export const BUSD_BSC = new Token(UniverseChainId.Bnb, '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', 18, 'BUSD', 'BUSD') @@ -259,28 +259,28 @@ export const USDC_AVALANCHE = new Token( '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', 6, 'USDC', - 'USDC Token' + 'USDC Token', ) export const USDT_AVALANCHE = new Token( UniverseChainId.Avalanche, '0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7', 6, 'USDT', - 'Tether USD' + 'Tether USD', ) export const WETH_AVALANCHE = new Token( UniverseChainId.Avalanche, '0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ) export const DAI_AVALANCHE = new Token( UniverseChainId.Avalanche, '0xd586E7F844cEa2F87f50152665BCbc2C279D8d70', 18, 'DAI.e', - 'Dai.e Token' + 'Dai.e Token', ) export const UNI: { [chainId: number]: Token } = { @@ -289,28 +289,28 @@ export const UNI: { [chainId: number]: Token } = { UNI_ADDRESSES[UniverseChainId.Mainnet], 18, 'UNI', - 'Uniswap' + 'Uniswap', ), [UniverseChainId.Optimism]: new Token( UniverseChainId.Optimism, UNI_ADDRESSES[UniverseChainId.Optimism], 18, 'UNI', - 'Uniswap' + 'Uniswap', ), [UniverseChainId.Goerli]: new Token( UniverseChainId.Goerli, UNI_ADDRESSES[UniverseChainId.Goerli], 18, 'UNI', - 'Uniswap' + 'Uniswap', ), [UniverseChainId.Sepolia]: new Token( UniverseChainId.Sepolia, UNI_ADDRESSES[UniverseChainId.Sepolia], 18, 'UNI', - 'Uniswap' + 'Uniswap', ), } @@ -319,7 +319,7 @@ export const ARB = new Token( '0x912CE59144191C1204E64559FE8253a0e49E6548', 18, 'ARB', - 'Arbitrum' + 'Arbitrum', ) export const OP = new Token( @@ -327,7 +327,7 @@ export const OP = new Token( '0x4200000000000000000000000000000000000042', 18, 'OP', - 'Optimism' + 'Optimism', ) export const LDO = new Token( @@ -335,21 +335,21 @@ export const LDO = new Token( '0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32', 18, 'LDO', - 'Lido DAO Token' + 'Lido DAO Token', ) export const NMR = new Token( UniverseChainId.Mainnet, '0x1776e1F26f98b1A5dF9cD347953a26dd3Cb46671', 18, 'NMR', - 'Numeraire' + 'Numeraire', ) export const MNW = new Token( UniverseChainId.Mainnet, '0xd3E4Ba569045546D09CF021ECC5dFe42b1d7f6E4', 18, 'MNW', - 'Morpheus.Network' + 'Morpheus.Network', ) export const WRAPPED_NATIVE_CURRENCY: { [chainId: number]: Token | undefined } = { @@ -359,105 +359,105 @@ export const WRAPPED_NATIVE_CURRENCY: { [chainId: number]: Token | undefined } = '0x4200000000000000000000000000000000000006', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), [UniverseChainId.OptimismGoerli]: new Token( UniverseChainId.OptimismGoerli, '0x4200000000000000000000000000000000000006', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), [UniverseChainId.Base]: new Token( UniverseChainId.Base, '0x4200000000000000000000000000000000000006', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), [UniverseChainId.ArbitrumOne]: new Token( UniverseChainId.ArbitrumOne, '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), [UniverseChainId.ArbitrumGoerli]: new Token( UniverseChainId.ArbitrumGoerli, '0xe39Ab88f8A4777030A534146A9Ca3B52bd5D43A3', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), [UniverseChainId.Sepolia]: new Token( UniverseChainId.Sepolia, '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), [UniverseChainId.Polygon]: new Token( UniverseChainId.Polygon, '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', 18, 'WMATIC', - 'Wrapped MATIC' + 'Wrapped MATIC', ), [UniverseChainId.PolygonMumbai]: new Token( UniverseChainId.PolygonMumbai, '0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889', 18, 'WMATIC', - 'Wrapped MATIC' + 'Wrapped MATIC', ), [UniverseChainId.Celo]: new Token( UniverseChainId.Celo, '0x471ece3750da237f93b8e339c536989b8978a438', 18, 'CELO', - 'Celo native asset' + 'Celo native asset', ), [UniverseChainId.CeloAlfajores]: new Token( UniverseChainId.CeloAlfajores, '0xf194afdf50b03e69bd7d057c1aa9e10c9954e4c9', 18, 'CELO', - 'Celo native asset' + 'Celo native asset', ), [UniverseChainId.Bnb]: new Token( UniverseChainId.Bnb, '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c', 18, 'WBNB', - 'Wrapped BNB' + 'Wrapped BNB', ), [UniverseChainId.Avalanche]: new Token( UniverseChainId.Avalanche, '0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7', 18, 'WAVAX', - 'Wrapped AVAX' + 'Wrapped AVAX', ), [UniverseChainId.Blast]: new Token( UniverseChainId.Blast, '0x4300000000000000000000000000000000000004', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), [UniverseChainId.Zora]: new Token( UniverseChainId.Zora, '0x4200000000000000000000000000000000000006', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), [UniverseChainId.Zksync]: new Token( UniverseChainId.Zksync, '0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91', 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ), } diff --git a/apps/web/src/dev/DevFlagsBox.tsx b/apps/web/src/dev/DevFlagsBox.tsx index dab1829e2d1..39b949a72da 100644 --- a/apps/web/src/dev/DevFlagsBox.tsx +++ b/apps/web/src/dev/DevFlagsBox.tsx @@ -9,7 +9,7 @@ import styled from 'styled-components' import { ThemedText } from 'theme/components' import { Z_INDEX } from 'theme/zIndex' import { Statsig } from 'uniswap/src/features/gating/sdk/statsig' -import { isBetaEnv, isDevEnv } from 'uniswap/src/utils/env' +import { isBetaEnv, isDevEnv } from 'utilities/src/environment' const Box = styled.div` position: fixed; diff --git a/apps/web/src/graphql/data/RecentTokenTransfers.ts b/apps/web/src/graphql/data/RecentTokenTransfers.ts index 2a1c22b6ac6..6f6757c5d10 100644 --- a/apps/web/src/graphql/data/RecentTokenTransfers.ts +++ b/apps/web/src/graphql/data/RecentTokenTransfers.ts @@ -28,7 +28,7 @@ export function useRecentTokenTransfers(address?: string) { (activity.details as TransactionDetails).assetChanges.length === 1 && (activity.details as TransactionDetails).assetChanges[0]?.__typename === 'TokenTransfer' && ((activity.details as TransactionDetails).assetChanges as TokenTransfer[])[0].asset.project && - !((activity.details as TransactionDetails).assetChanges as TokenTransfer[])[0].asset.project?.isSpam + !((activity.details as TransactionDetails).assetChanges as TokenTransfer[])[0].asset.project?.isSpam, ) // Safe to unwrap based on filter expression above .map((activity) => ((activity!.details as TransactionDetails).assetChanges as TokenTransfer[])[0]) diff --git a/apps/web/src/graphql/data/SearchTokens.ts b/apps/web/src/graphql/data/SearchTokens.ts index 92670226fc0..90fe7fc27e4 100644 --- a/apps/web/src/graphql/data/SearchTokens.ts +++ b/apps/web/src/graphql/data/SearchTokens.ts @@ -49,7 +49,7 @@ function searchTokenSortFunction( searchChain: Chain, wrappedNativeAddress: string | undefined, a: SearchToken, - b: SearchToken + b: SearchToken, ) { if (a.standard === NATIVE_CHAIN_ID) { if (b.standard === NATIVE_CHAIN_ID) { @@ -80,7 +80,6 @@ export function useSearchTokens(searchQuery: string | undefined, chainId: Suppor searchQuery: searchQuery ?? '', }, skip: !searchQuery, - errorPolicy: 'all', }) const sortedTokens = useMemo(() => { @@ -89,7 +88,7 @@ export function useSearchTokens(searchQuery: string | undefined, chainId: Suppor const selectionMap: { [projectId: string]: SearchToken } = {} const filteredTokens = data?.searchTokens?.filter( (token): token is Token => - token !== undefined && (BACKEND_SUPPORTED_CHAINS as ReadonlyArray).includes(token.chain) + token !== undefined && (BACKEND_SUPPORTED_CHAINS as ReadonlyArray).includes(token.chain), ) filteredTokens?.forEach((token) => { if (token.project?.id) { @@ -98,7 +97,7 @@ export function useSearchTokens(searchQuery: string | undefined, chainId: Suppor } }) return Object.values(selectionMap).sort( - searchTokenSortFunction.bind(null, searchChain, WRAPPED_NATIVE_CURRENCY[chainId]?.address) + searchTokenSortFunction.bind(null, searchChain, WRAPPED_NATIVE_CURRENCY[chainId]?.address), ) }, [data, chainId]) diff --git a/apps/web/src/graphql/data/TopTokens.ts b/apps/web/src/graphql/data/TopTokens.ts index bb2f00f1e76..9cb5fb3267e 100644 --- a/apps/web/src/graphql/data/TopTokens.ts +++ b/apps/web/src/graphql/data/TopTokens.ts @@ -102,7 +102,7 @@ export function useTopTokens(chain: Chain): UseTopTokensReturnValue { variables: { duration, chain }, skip: !isWindowVisible, }), - PollingInterval.Slow + PollingInterval.Slow, ) const sparklines = useMemo(() => { @@ -125,12 +125,12 @@ export function useTopTokens(chain: Chain): UseTopTokensReturnValue { variables: { duration, chain }, skip: !isWindowVisible, }), - PollingInterval.Fast + PollingInterval.Fast, ) const unwrappedTokens = useMemo( () => chainId && data?.topTokens?.map((token) => unwrapToken(chainId, token)), - [chainId, data] + [chainId, data], ) const sortedTokens = useSortedTokens(unwrappedTokens) const tokenSortRank = useMemo( @@ -144,11 +144,11 @@ export function useTopTokens(chain: Chain): UseTopTokensReturnValue { [cur.address]: i + 1, } }, {}) ?? {}, - [sortedTokens] + [sortedTokens], ) const filteredTokens = useFilteredTokens(sortedTokens) return useMemo( () => ({ tokens: filteredTokens, tokenSortRank, loadingTokens, sparklines, error }), - [filteredTokens, tokenSortRank, loadingTokens, sparklines, error] + [filteredTokens, tokenSortRank, loadingTokens, sparklines, error], ) } diff --git a/apps/web/src/graphql/data/TrendingTokens.ts b/apps/web/src/graphql/data/TrendingTokens.ts index 55c9a344704..435e1a87fe9 100644 --- a/apps/web/src/graphql/data/TrendingTokens.ts +++ b/apps/web/src/graphql/data/TrendingTokens.ts @@ -9,6 +9,6 @@ export default function useTrendingTokens(chainId?: SupportedInterfaceChainId) { return useMemo( () => ({ data: data?.topTokens?.map((token) => unwrapToken(chainId ?? 1, token)), loading }), - [chainId, data?.topTokens, loading] + [chainId, data?.topTokens, loading], ) } diff --git a/apps/web/src/graphql/data/apollo/AdaptiveRefetch.tsx b/apps/web/src/graphql/data/apollo/AdaptiveRefetch.tsx index 89754c610e9..6ee88817ab8 100644 --- a/apps/web/src/graphql/data/apollo/AdaptiveRefetch.tsx +++ b/apps/web/src/graphql/data/apollo/AdaptiveRefetch.tsx @@ -58,7 +58,7 @@ export function createAdaptiveRefetchContext() { refetch, subscribe, }), - [refetch, query, subscribe] + [refetch, query, subscribe], )} > {children} diff --git a/apps/web/src/graphql/data/apollo/AssetActivityProvider.tsx b/apps/web/src/graphql/data/apollo/AssetActivityProvider.tsx index f2106ef88d4..dda9ee3a627 100644 --- a/apps/web/src/graphql/data/apollo/AssetActivityProvider.tsx +++ b/apps/web/src/graphql/data/apollo/AssetActivityProvider.tsx @@ -57,7 +57,7 @@ export function AssetActivityProvider({ children }: PropsWithChildren) { const [lazyFetch, query] = useActivityWebLazyQuery() const fetch = useCallback( () => lazyFetch({ variables: { account: account.address ?? '', chains: GQL_MAINNET_CHAINS_MUTABLE } }), - [account.address, lazyFetch] + [account.address, lazyFetch], ) return ( diff --git a/apps/web/src/graphql/data/apollo/TokenBalancesProvider.test.tsx b/apps/web/src/graphql/data/apollo/TokenBalancesProvider.test.tsx index e54a03a4da5..cdf409a9616 100644 --- a/apps/web/src/graphql/data/apollo/TokenBalancesProvider.test.tsx +++ b/apps/web/src/graphql/data/apollo/TokenBalancesProvider.test.tsx @@ -105,7 +105,7 @@ describe('TokenBalancesProvider', () => { const { rerender } = render(
hi
-
+ , ) const wrappedComponent = screen.getByText('hi') @@ -113,7 +113,7 @@ describe('TokenBalancesProvider', () => { rerender(
hi
-
+ , ) // Should not fetch balances before hover @@ -134,7 +134,7 @@ describe('TokenBalancesProvider', () => { rerender(
hi
-
+ , ) expect(mockLazyFetch).toHaveBeenCalledTimes(1) fireEvent.mouseEnter(wrappedComponent) diff --git a/apps/web/src/graphql/data/apollo/TokenBalancesProvider.tsx b/apps/web/src/graphql/data/apollo/TokenBalancesProvider.tsx index 58d7a9da600..bbe9b1ab032 100644 --- a/apps/web/src/graphql/data/apollo/TokenBalancesProvider.tsx +++ b/apps/web/src/graphql/data/apollo/TokenBalancesProvider.tsx @@ -80,7 +80,7 @@ function usePortfolioValueModifiers(): { includeSmallBalances: !hideSmallBalances, includeSpamTokens: !hideSpamTokens, }), - [hideSmallBalances, hideSpamTokens] + [hideSmallBalances, hideSpamTokens], ) } diff --git a/apps/web/src/graphql/data/nft/Asset.ts b/apps/web/src/graphql/data/nft/Asset.ts index 64ab1ac31b5..46963707bfa 100644 --- a/apps/web/src/graphql/data/nft/Asset.ts +++ b/apps/web/src/graphql/data/nft/Asset.ts @@ -98,7 +98,7 @@ export function useNftAssets(params: AssetFetcherParams) { after: data?.nftAssets?.pageInfo?.endCursor, }, }), - [data, fetchMore] + [data, fetchMore], ) // TODO: setup polling while handling pagination @@ -109,7 +109,7 @@ export function useNftAssets(params: AssetFetcherParams) { data?.nftAssets?.edges?.map((queryAsset) => { return formatAssetQueryData(queryAsset as NonNullable, data.nftAssets?.totalCount) }), - [data?.nftAssets?.edges, data?.nftAssets?.totalCount] + [data?.nftAssets?.edges, data?.nftAssets?.totalCount], ) return useMemo(() => { @@ -146,7 +146,7 @@ function useSweepFetcherVars({ contractAddress, markets, price, traits }: SweepF marketplaces: markets && markets.length > 0 ? markets?.map((market) => market.toUpperCase() as NftMarketplace) : undefined, }), - [markets, price?.high, price?.low, traits] + [markets, price?.high, price?.low, traits], ) return useMemo( () => ({ @@ -156,7 +156,7 @@ function useSweepFetcherVars({ contractAddress, markets, price, traits }: SweepF first: DEFAULT_SWEEP_AMOUNT, filter, }), - [contractAddress, filter] + [contractAddress, filter], ) } @@ -172,7 +172,7 @@ export function useSweepNftAssets(params: SweepFetcherParams) { data?.nftAssets?.edges?.map((queryAsset) => { return formatAssetQueryData(queryAsset as NonNullable, data.nftAssets?.totalCount) }), - [data?.nftAssets?.edges, data?.nftAssets?.totalCount] + [data?.nftAssets?.edges, data?.nftAssets?.totalCount], ) return useMemo(() => ({ data: assets, loading }), [assets, loading]) } diff --git a/apps/web/src/graphql/data/nft/Collection.ts b/apps/web/src/graphql/data/nft/Collection.ts index de106143c1c..f85e1f32780 100644 --- a/apps/web/src/graphql/data/nft/Collection.ts +++ b/apps/web/src/graphql/data/nft/Collection.ts @@ -7,7 +7,7 @@ import { export function formatCollectionQueryData( queryCollection: NonNullable, - address?: string + address?: string, ): GenieCollection { const market = queryCollection?.markets?.[0] if (!address && !queryCollection?.nftContracts?.[0]?.address) { diff --git a/apps/web/src/graphql/data/nft/CollectionSearch.ts b/apps/web/src/graphql/data/nft/CollectionSearch.ts index f18c249967c..b8b4bb0818c 100644 --- a/apps/web/src/graphql/data/nft/CollectionSearch.ts +++ b/apps/web/src/graphql/data/nft/CollectionSearch.ts @@ -30,7 +30,7 @@ function useCollectionQuerySearch(query: string, skip?: boolean): useCollectionS ?.filter( (collectionEdge) => collectionEdge.node.nftContracts?.[0]?.address && - !blocklistedCollections.includes(collectionEdge.node.nftContracts?.[0]?.address) + !blocklistedCollections.includes(collectionEdge.node.nftContracts?.[0]?.address), ) .slice(0, MAX_SEARCH_RESULTS) .map((collectionEdge) => { @@ -51,6 +51,6 @@ export function useCollectionSearch(queryOrAddress: string): useCollectionSearch return isName ? queryResult : invalidCollectionAddress - ? { data: [], loading: false } - : { data: [addressResult.data], loading: addressResult.loading } + ? { data: [], loading: false } + : { data: [addressResult.data], loading: addressResult.loading } } diff --git a/apps/web/src/graphql/data/nft/Details.ts b/apps/web/src/graphql/data/nft/Details.ts index 7234fef898b..cc1d88f2936 100644 --- a/apps/web/src/graphql/data/nft/Details.ts +++ b/apps/web/src/graphql/data/nft/Details.ts @@ -6,7 +6,7 @@ import { NftAsset, useNftDetailsQuery } from 'uniswap/src/data/graphql/uniswap-d export function useNftAssetDetails( address: string, - tokenId: string + tokenId: string, ): { data: [GenieAsset, CollectionInfoForAsset]; loading: boolean } { const { data: queryData, loading } = useNftDetailsQuery({ variables: { @@ -85,6 +85,6 @@ export function useNftAssetDetails( ], loading, }), - [address, asset, collection, ethPrice, listing?.marketplace, loading, tokenId] + [address, asset, collection, ethPrice, listing?.marketplace, loading, tokenId], ) } diff --git a/apps/web/src/graphql/data/nft/NftActivity.ts b/apps/web/src/graphql/data/nft/NftActivity.ts index feafc1b85e3..cc111533bd7 100644 --- a/apps/web/src/graphql/data/nft/NftActivity.ts +++ b/apps/web/src/graphql/data/nft/NftActivity.ts @@ -24,7 +24,7 @@ export function useNftActivity(filter: NftActivityFilterInput, first?: number, f after: data?.nftActivity?.pageInfo?.endCursor, }, }), - [data, fetchMore] + [data, fetchMore], ) const nftActivity: ActivityEvent[] | undefined = useMemo( @@ -65,11 +65,11 @@ export function useNftActivity(filter: NftActivityFilterInput, first?: number, f eventTimestamp: activity.timestamp * 1000, } }), - [data] + [data], ) return useMemo( () => ({ nftActivity, hasNext, loadMore, loading, error }), - [hasNext, loadMore, loading, nftActivity, error] + [hasNext, loadMore, loading, nftActivity, error], ) } diff --git a/apps/web/src/graphql/data/nft/NftBalance.ts b/apps/web/src/graphql/data/nft/NftBalance.ts index ad5dc1dcafc..26f0480905b 100644 --- a/apps/web/src/graphql/data/nft/NftBalance.ts +++ b/apps/web/src/graphql/data/nft/NftBalance.ts @@ -3,18 +3,35 @@ import { parseEther } from 'ethers/lib/utils' import { GenieCollection, WalletAsset } from 'nft/types' import { wrapScientificNotation } from 'nft/utils' import { useCallback, useMemo } from 'react' -import { NftAsset, useNftBalanceQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { + Chain, + NftAsset, + useNftBalanceQuery, +} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -export function useNftBalance( - ownerAddress: string, - collectionFilters?: string[], - assetsFilter?: { address: string; tokenId: string }[], - first?: number, - after?: string, - last?: number, - before?: string, - skip = false -) { +type UseNftBalanceParams = { + ownerAddress: string + collectionFilters?: string[] + assetsFilter?: { address: string; tokenId: string }[] + first?: number + after?: string + last?: number + before?: string + skip?: boolean + chains?: Chain[] +} + +export function useNftBalance({ + ownerAddress, + collectionFilters, + assetsFilter, + first, + after, + last, + before, + skip = false, + chains, +}: UseNftBalanceParams) { const { data, loading, fetchMore } = useNftBalanceQuery({ variables: { ownerAddress, @@ -26,6 +43,7 @@ export function useNftBalance( : { addresses: collectionFilters, }, + chains, first, after, last, @@ -42,7 +60,7 @@ export function useNftBalance( after: data?.nftBalances?.pageInfo?.endCursor, }, }), - [data?.nftBalances?.pageInfo?.endCursor, fetchMore] + [data?.nftBalances?.pageInfo?.endCursor, fetchMore], ) const walletAssets: WalletAsset[] | undefined = data?.nftBalances?.edges?.map((queryAsset) => { @@ -85,6 +103,7 @@ export function useNftBalance( date_acquired: queryAsset.node.lastPrice?.timestamp?.toString(), sellOrders: asset?.listings?.edges.map((edge: any) => edge.node), floor_sell_order_price: asset?.listings?.edges?.[0]?.node?.price?.value, + chain: asset.chain, } }) return useMemo(() => ({ walletAssets, hasNext, loadMore, loading }), [hasNext, loadMore, loading, walletAssets]) diff --git a/apps/web/src/graphql/data/nft/TrendingCollections.ts b/apps/web/src/graphql/data/nft/TrendingCollections.ts index 299a928dde9..0c44511ceeb 100644 --- a/apps/web/src/graphql/data/nft/TrendingCollections.ts +++ b/apps/web/src/graphql/data/nft/TrendingCollections.ts @@ -35,7 +35,7 @@ export function useTrendingCollections(size: number, timePeriod: HistoryDuration totalSupply: collection.nftContracts?.[0]?.totalSupply, } }), - [data?.topCollections?.edges] + [data?.topCollections?.edges], ) return { diff --git a/apps/web/src/graphql/data/pools/usePoolData.ts b/apps/web/src/graphql/data/pools/usePoolData.ts index aaef79efd1c..5a6b8fb8dee 100644 --- a/apps/web/src/graphql/data/pools/usePoolData.ts +++ b/apps/web/src/graphql/data/pools/usePoolData.ts @@ -53,7 +53,7 @@ function calc24HVolChange(historicalVolume?: (VolumeChange | undefined)[]) { const volume48h = historicalVolume .filter( (entry): entry is VolumeChange => - entry?.timestamp !== undefined && entry.timestamp >= twoDaysAgo && entry.timestamp < dayAgo + entry?.timestamp !== undefined && entry.timestamp >= twoDaysAgo && entry.timestamp < dayAgo, ) .reduce((acc, cur) => acc + cur.value, 0) return ((volume24h - volume48h) / volume48h) * 100 @@ -67,7 +67,7 @@ function calc24HVolChange(historicalVolume?: (VolumeChange | undefined)[]) { */ export function usePoolData( poolAddress: string, - chainId?: SupportedInterfaceChainId + chainId?: SupportedInterfaceChainId, ): { loading: boolean error: boolean diff --git a/apps/web/src/graphql/data/pools/usePoolTransactions.ts b/apps/web/src/graphql/data/pools/usePoolTransactions.ts index 51a1f948b49..ec3ac0773b3 100644 --- a/apps/web/src/graphql/data/pools/usePoolTransactions.ts +++ b/apps/web/src/graphql/data/pools/usePoolTransactions.ts @@ -53,7 +53,7 @@ export function usePoolTransactions( ], token0?: Token, protocolVersion: ProtocolVersion = ProtocolVersion.V3, - first = PoolTransactionDefaultQuerySize + first = PoolTransactionDefaultQuerySize, ) { const { loading: loadingV3, @@ -119,7 +119,7 @@ export function usePoolTransactions( }, }) }, - [fetchMore, transactions, protocolVersion] + [fetchMore, transactions, protocolVersion], ) const filteredTransactions = useMemo(() => { @@ -140,8 +140,8 @@ export function usePoolTransactions( ? PoolTableTransactionType.SELL : PoolTableTransactionType.BUY : tx.type === PoolTransactionType.Remove - ? PoolTableTransactionType.REMOVE - : PoolTableTransactionType.ADD + ? PoolTableTransactionType.REMOVE + : PoolTableTransactionType.ADD if (!filter.includes(type)) { return undefined } diff --git a/apps/web/src/graphql/data/pools/usePoolsFromTokenAddress.ts b/apps/web/src/graphql/data/pools/usePoolsFromTokenAddress.ts index e2e1f40c3df..ababd1abaef 100644 --- a/apps/web/src/graphql/data/pools/usePoolsFromTokenAddress.ts +++ b/apps/web/src/graphql/data/pools/usePoolsFromTokenAddress.ts @@ -11,7 +11,7 @@ const DEFAULT_QUERY_SIZE = 20 export function usePoolsFromTokenAddress( tokenAddress: string, sortState: PoolTableSortState, - chainId?: SupportedInterfaceChainId + chainId?: SupportedInterfaceChainId, ) { const { loading: loadingV3, @@ -89,7 +89,7 @@ export function usePoolsFromTokenAddress( }, }) }, - [dataV2?.topV2Pairs, dataV3?.topV3Pools, fetchMoreV2, fetchMoreV3] + [dataV2?.topV2Pairs, dataV3?.topV3Pools, fetchMoreV2, fetchMoreV3], ) return useMemo(() => { diff --git a/apps/web/src/graphql/data/pools/useTopPools.ts b/apps/web/src/graphql/data/pools/useTopPools.ts index ae3d7e81980..285a0562035 100644 --- a/apps/web/src/graphql/data/pools/useTopPools.ts +++ b/apps/web/src/graphql/data/pools/useTopPools.ts @@ -30,8 +30,8 @@ export function sortPools(pools: TablePool[], sortState: PoolTableSortState) { ? 1 : -1 : a.oneDayApr.greaterThan(b.oneDayApr) - ? 1 - : -1 + ? 1 + : -1 default: return sortState.sortDirection === OrderDirection.Desc ? b.tvl - a.tvl : a.tvl - b.tvl } @@ -104,7 +104,7 @@ function useFilteredPools(pools: TablePool[]) { poolNameIncludesFilterString ) }), - [lowercaseFilterString, pools] + [lowercaseFilterString, pools], ) } diff --git a/apps/web/src/graphql/data/protocolStats.ts b/apps/web/src/graphql/data/protocolStats.ts index 48ea5736479..61748e8f84e 100644 --- a/apps/web/src/graphql/data/protocolStats.ts +++ b/apps/web/src/graphql/data/protocolStats.ts @@ -17,7 +17,7 @@ import { function mapDataByTimestamp( v2Data?: readonly TimestampedAmount[], - v3Data?: readonly TimestampedAmount[] + v3Data?: readonly TimestampedAmount[], ): Record> { const dataByTime: Record> = {} v2Data?.forEach((v2Point) => { @@ -37,7 +37,7 @@ function mapDataByTimestamp( export function useHistoricalProtocolVolume( chain: Chain, - duration: HistoryDuration + duration: HistoryDuration, ): ChartQueryResult { const isWindowVisible = useIsWindowVisible() const { data: queryData, loading } = useHistoricalProtocolVolumeQuery({ diff --git a/apps/web/src/graphql/data/types.ts b/apps/web/src/graphql/data/types.ts index faba7485608..88695af4b79 100644 --- a/apps/web/src/graphql/data/types.ts +++ b/apps/web/src/graphql/data/types.ts @@ -1,6 +1,11 @@ -import { gqlToCurrency } from 'graphql/data/util' +import { SupportedInterfaceChainId, isSupportedChainId } from 'constants/chains' +import { COMMON_BASES, buildCurrencyInfo } from 'constants/routing' +import { USDC_OPTIMISM } from 'constants/tokens' +import { fiatOnRampToCurrency, gqlToCurrency } from 'graphql/data/util' import { Token as GqlToken, SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' +import { FORSupportedToken } from 'uniswap/src/features/fiatOnRamp/types' +import { isSameAddress } from 'utilities/src/addresses' import { currencyId } from 'utils/currencyId' // TODO(WEB-3839): replace all usage of Currency in the web app with CurrencyInfo @@ -26,3 +31,40 @@ export function gqlTokenToCurrencyInfo(token?: GqlToken): CurrencyInfo | undefin } return currencyInfo } + +export function meldSupportedCurrencyToCurrencyInfo(forCurrency: FORSupportedToken): CurrencyInfo | undefined { + if (!isSupportedChainId(Number(forCurrency.chainId))) { + return + } + + const supportedChainId = Number(forCurrency.chainId) as SupportedInterfaceChainId + const commonBases = COMMON_BASES[supportedChainId] + + const currencyInfo = commonBases.find((base) => { + if (base.currency.isNative) { + return !forCurrency.address + } + return isSameAddress(base.currency.address, forCurrency.address) + }) + + if (currencyInfo) { + return currencyInfo + } + + // Special case for *bridged* USDC on Optimism, which we otherwise don't use in our app. + if (isSameAddress(forCurrency.address, '0x7f5c764cbc14f9669b88837ca1490cca17c31607')) { + return buildCurrencyInfo(USDC_OPTIMISM) + } + + const currency = fiatOnRampToCurrency(forCurrency) + if (!currency) { + return + } + return { + currency, + currencyId: currencyId(currency), + logoUrl: forCurrency.symbol, + safetyLevel: SafetyLevel.Verified, + isSpam: false, + } +} diff --git a/apps/web/src/graphql/data/useAllTransactions.ts b/apps/web/src/graphql/data/useAllTransactions.ts index 7689711985e..2c8e67ec61a 100644 --- a/apps/web/src/graphql/data/useAllTransactions.ts +++ b/apps/web/src/graphql/data/useAllTransactions.ts @@ -24,7 +24,7 @@ const ALL_TX_DEFAULT_QUERY_SIZE = 20 export function useAllTransactions( chain: Chain, - filter: TransactionType[] = [TransactionType.SWAP, TransactionType.ADD, TransactionType.REMOVE] + filter: TransactionType[] = [TransactionType.SWAP, TransactionType.ADD, TransactionType.REMOVE], ) { const isWindowVisible = useIsWindowVisible() @@ -94,17 +94,17 @@ export function useAllTransactions( }, }) }, - [dataV2?.v2Transactions, dataV3?.v3Transactions, fetchMoreV2, fetchMoreV3] + [dataV2?.v2Transactions, dataV3?.v3Transactions, fetchMoreV2, fetchMoreV3], ) const transactions: PoolTransaction[] = useMemo(() => { const v3Transactions = dataV3?.v3Transactions?.filter( - (tx): tx is PoolTransaction => tx.type && filter.includes(BETypeToTransactionType[tx.type]) + (tx): tx is PoolTransaction => tx.type && filter.includes(BETypeToTransactionType[tx.type]), ) ?? [] const v2Transactions = dataV2?.v2Transactions?.filter( - (tx): tx is PoolTransaction => tx !== undefined && tx.type && filter.includes(BETypeToTransactionType[tx.type]) + (tx): tx is PoolTransaction => tx !== undefined && tx.type && filter.includes(BETypeToTransactionType[tx.type]), ) ?? [] return [...v3Transactions, ...v2Transactions] .sort((a, b) => (b?.timestamp || 0) - (a?.timestamp || 0)) diff --git a/apps/web/src/graphql/data/useTokenTransactions.ts b/apps/web/src/graphql/data/useTokenTransactions.ts index 0c1f1bfd65e..388858a69ec 100644 --- a/apps/web/src/graphql/data/useTokenTransactions.ts +++ b/apps/web/src/graphql/data/useTokenTransactions.ts @@ -18,7 +18,7 @@ const TokenTransactionDefaultQuerySize = 25 export function useTokenTransactions( address: string, chainId: SupportedInterfaceChainId, - filter: TokenTransactionType[] = [TokenTransactionType.BUY, TokenTransactionType.SELL] + filter: TokenTransactionType[] = [TokenTransactionType.BUY, TokenTransactionType.SELL], ) { const { data: dataV3, @@ -102,7 +102,7 @@ export function useTokenTransactions( }, }) }, - [dataV2?.token?.v2Transactions, dataV3?.token?.v3Transactions, fetchMoreV2, fetchMoreV3] + [dataV2?.token?.v2Transactions, dataV3?.token?.v3Transactions, fetchMoreV2, fetchMoreV3], ) const transactions = useMemo( @@ -132,10 +132,10 @@ export function useTokenTransactions( }) ?? []), ] .sort((a, b): number => - a?.timestamp && b?.timestamp ? b.timestamp - a.timestamp : a?.timestamp === null ? -1 : 1 + a?.timestamp && b?.timestamp ? b.timestamp - a.timestamp : a?.timestamp === null ? -1 : 1, ) .slice(0, querySizeRef.current), - [address, dataV2?.token?.v2Transactions, dataV3?.token?.v3Transactions, filter] + [address, dataV2?.token?.v2Transactions, dataV3?.token?.v3Transactions, filter], ) return useMemo(() => { diff --git a/apps/web/src/graphql/data/util.tsx b/apps/web/src/graphql/data/util.tsx index f8b29a5e204..b1695de5934 100644 --- a/apps/web/src/graphql/data/util.tsx +++ b/apps/web/src/graphql/data/util.tsx @@ -11,6 +11,7 @@ import { SupportedInterfaceChainId, UX_SUPPORTED_GQL_CHAINS, chainIdToBackendChain, + isSupportedChainId, } from 'constants/chains' import { NATIVE_CHAIN_ID, WRAPPED_NATIVE_CURRENCY, nativeOnChain } from 'constants/tokens' import ms from 'ms' @@ -27,6 +28,7 @@ import { PriceSource, TokenStandard, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { FORSupportedToken } from 'uniswap/src/features/fiatOnRamp/types' import { UniverseChainId, UniverseChainInfo } from 'uniswap/src/types/chains' import { getNativeTokenDBAddress } from 'utils/nativeTokens' @@ -40,7 +42,7 @@ export enum PollingInterval { // Polls a query only when the current component is mounted, as useQuery's pollInterval prop will continue to poll after unmount export function usePollQueryWhileMounted( queryResult: QueryResult, - interval: PollingInterval + interval: PollingInterval, ) { const { startPolling, stopPolling } = queryResult @@ -109,22 +111,36 @@ export function gqlToCurrency(token: DeepPartial): Currency | undefine token.address, token.decimals ?? 18, token.symbol ?? undefined, - token.project?.name ?? token.name ?? undefined + token.project?.name ?? token.name ?? undefined, ) } } +export function fiatOnRampToCurrency(forCurrency: FORSupportedToken): Currency | undefined { + if (!isSupportedChainId(Number(forCurrency.chainId))) { + return + } + const supportedChainId = Number(forCurrency.chainId) as SupportedInterfaceChainId + + if (!forCurrency.address) { + return nativeOnChain(supportedChainId) + } else { + // The Meld code may not match the currency's symbol (e.g. codes like USDC_BASE), so these should not be used for display. + return new Token(supportedChainId, forCurrency.address, 18, forCurrency.cryptoCurrencyCode, forCurrency.displayName) + } +} + export function getSupportedGraphQlChain( chain: UniverseChainInfo | undefined, - options?: undefined + options?: undefined, ): UniverseChainInfo | undefined export function getSupportedGraphQlChain( chain: UniverseChainInfo | undefined, - options: { fallbackToEthereum: true } + options: { fallbackToEthereum: true }, ): UniverseChainInfo export function getSupportedGraphQlChain( chain: UniverseChainInfo | undefined, - options?: { fallbackToEthereum?: boolean } + options?: { fallbackToEthereum?: boolean }, ): UniverseChainInfo | undefined { const fallbackChain = options?.fallbackToEthereum ? UNIVERSE_CHAIN_INFO[UniverseChainId.Mainnet] : undefined return chain?.backendChain.backendSupported ? chain : fallbackChain @@ -176,7 +192,7 @@ export function unwrapToken< address?: string | null project?: { name?: string | null } } - | undefined + | undefined, >(chainId: number, token: T): T { if (!token?.address) { return token @@ -231,9 +247,9 @@ export function apolloQueryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, - TQueryKey extends QueryKey = QueryKey + TQueryKey extends QueryKey = QueryKey, >( - options: Pick, 'queryKey' | 'queryFn'> + options: Pick, 'queryKey' | 'queryFn'>, ): Pick< UndefinedInitialDataOptions & { queryKey: DataTag diff --git a/apps/web/src/hooks/Tokens.ts b/apps/web/src/hooks/Tokens.ts index 84840003c9c..3e54fd9192b 100644 --- a/apps/web/src/hooks/Tokens.ts +++ b/apps/web/src/hooks/Tokens.ts @@ -35,7 +35,7 @@ type Maybe = T | undefined // reduce token map into standard address <-> Token mapping, optionally include user added tokens function useTokensFromMap( tokenMap: TokenAddressMap, - chainId: Maybe + chainId: Maybe, ): { [address: string]: TokenFromList } { return useMemo(() => { if (!chainId) { @@ -66,7 +66,7 @@ export function useFallbackListTokens(chainId: Maybe): { [addr }, // must make a copy because reduce modifies the map, and we do not // want to make a copy in every iteration - { ...tokensFromMap } + { ...tokensFromMap }, ) ) }, [tokensFromMap, userAddedTokens]) @@ -96,7 +96,7 @@ export function useCurrencyInfo(address?: string, chainId?: InterfaceChainId, sk export function useCurrencyInfo( addressOrCurrency?: string | Currency, chainId?: InterfaceChainId, - skip?: boolean + skip?: boolean, ): Maybe { const { chainId: connectedChainId } = useAccount() const fallbackListTokens = useFallbackListTokens(chainId ?? connectedChainId) @@ -105,8 +105,8 @@ export function useCurrencyInfo( typeof addressOrCurrency === 'string' ? addressOrCurrency : addressOrCurrency?.isNative - ? NATIVE_CHAIN_ID - : addressOrCurrency?.address + ? NATIVE_CHAIN_ID + : addressOrCurrency?.address const chainIdWithFallback = (typeof addressOrCurrency === 'string' ? chainId : addressOrCurrency?.chainId) ?? connectedChainId @@ -123,7 +123,7 @@ export function useCurrencyInfo( ? COMMON_BASES[chainIdWithFallback]?.find( (base) => (base.currency.isNative && isNative) || - (base.currency.isToken && isSameAddress(base.currency.address, address)) + (base.currency.isToken && isSameAddress(base.currency.address, address)), ) : undefined @@ -165,13 +165,14 @@ export function useCurrencyInfo( } export function useToken(tokenAddress?: string, chainId?: SupportedInterfaceChainId): Maybe { + const formattedAddress = isAddress(tokenAddress) const { chainId: connectedChainId } = useAccount() - const currency = useCurrency(tokenAddress, chainId ?? connectedChainId) + const currency = useCurrency(formattedAddress ? formattedAddress : undefined, chainId ?? connectedChainId) // Some chains are not supported by the backend, so we need to fetch token // details directly from the blockchain. const networkToken = useTokenFromActiveNetwork( - tokenAddress, - getChain({ chainId: chainId ?? connectedChainId })?.backendChain.backendSupported + formattedAddress ? formattedAddress : undefined, + getChain({ chainId: chainId ?? connectedChainId })?.backendChain.backendSupported, ) return useMemo(() => { @@ -189,9 +190,9 @@ function parseStringOrBytes32(str: string | undefined, bytes32: string | undefin return str && str.length > 0 ? str : // need to check for proper bytes string and valid terminator - bytes32 && BYTES32_REGEX.test(bytes32) && arrayify(bytes32)[31] === 0 - ? parseBytes32String(bytes32) - : defaultValue + bytes32 && BYTES32_REGEX.test(bytes32) && arrayify(bytes32)[31] === 0 + ? parseBytes32String(bytes32) + : defaultValue } const UNKNOWN_TOKEN_NAME = 'Unknown Token' @@ -218,17 +219,17 @@ function useTokenFromActiveNetwork(tokenAddress: string | undefined, skip?: bool const isLoading = useMemo( () => decimals.loading || symbol.loading || tokenName.loading, - [decimals.loading, symbol.loading, tokenName.loading] + [decimals.loading, symbol.loading, tokenName.loading], ) const parsedDecimals = useMemo(() => decimals?.result?.[0] ?? DEFAULT_ERC20_DECIMALS, [decimals.result]) const parsedSymbol = useMemo( () => parseStringOrBytes32(symbol.result?.[0], symbolBytes32.result?.[0], UNKNOWN_TOKEN_SYMBOL), - [symbol.result, symbolBytes32.result] + [symbol.result, symbolBytes32.result], ) const parsedName = useMemo( () => parseStringOrBytes32(tokenName.result?.[0], tokenNameBytes32.result?.[0], UNKNOWN_TOKEN_NAME), - [tokenName.result, tokenNameBytes32.result] + [tokenName.result, tokenNameBytes32.result], ) return useMemo(() => { diff --git a/apps/web/src/hooks/screenSize/useScreenSize.ts b/apps/web/src/hooks/screenSize/useScreenSize.ts index 1aa1ff5580a..8e068eb4eb1 100644 --- a/apps/web/src/hooks/screenSize/useScreenSize.ts +++ b/apps/web/src/hooks/screenSize/useScreenSize.ts @@ -27,7 +27,7 @@ function getScreenSize(): Record { ? window.innerWidth > BREAKPOINTS_ADDITIONAL[key as keyof typeof BREAKPOINTS_ADDITIONAL] : false, }), - {} as Record + {} as Record, ) } diff --git a/apps/web/src/hooks/useAccount.ts b/apps/web/src/hooks/useAccount.ts index a2a2a3f1a8a..f072102d8d5 100644 --- a/apps/web/src/hooks/useAccount.ts +++ b/apps/web/src/hooks/useAccount.ts @@ -7,8 +7,8 @@ import { UseAccountReturnType as UseAccountReturnTypeWagmi, useAccount as useAcc type ReplaceChainId = T extends { chainId: number } ? Omit & { chainId: SupportedInterfaceChainId | undefined } : T extends { chainId: number | undefined } - ? Omit & { chainId: SupportedInterfaceChainId | undefined } - : T + ? Omit & { chainId: SupportedInterfaceChainId | undefined } + : T type UseAccountReturnType = ReplaceChainId @@ -22,6 +22,6 @@ export function useAccount(): UseAccountReturnType { ...rest, chainId: supportedChainId, }), - [rest, supportedChainId] + [rest, supportedChainId], ) } diff --git a/apps/web/src/hooks/useActiveLocalCurrency.ts b/apps/web/src/hooks/useActiveLocalCurrency.ts index 81a52da2050..61e677faabc 100644 --- a/apps/web/src/hooks/useActiveLocalCurrency.ts +++ b/apps/web/src/hooks/useActiveLocalCurrency.ts @@ -7,7 +7,7 @@ import { getFiatCurrencyComponents } from 'utils/formatNumbers' export const activeLocalCurrencyAtom = atomWithStorage( 'activeLocalCurrency', - DEFAULT_LOCAL_CURRENCY + DEFAULT_LOCAL_CURRENCY, ) function useUrlLocalCurrency() { @@ -20,7 +20,7 @@ function useUrlLocalCurrency() { const lowerCaseSupportedLocalCurrency = parsedLocalCurrency.toLowerCase() return SUPPORTED_LOCAL_CURRENCIES.find( - (localCurrency) => localCurrency.toLowerCase() === lowerCaseSupportedLocalCurrency + (localCurrency) => localCurrency.toLowerCase() === lowerCaseSupportedLocalCurrency, ) } @@ -37,6 +37,6 @@ export function useActiveLocalCurrencyComponents() { return useMemo( () => getFiatCurrencyComponents(activeLocale, activeLocalCurrency), - [activeLocalCurrency, activeLocale] + [activeLocalCurrency, activeLocale], ) } diff --git a/apps/web/src/hooks/useActiveLocale.ts b/apps/web/src/hooks/useActiveLocale.ts index 2dec888a73b..1416f816d57 100644 --- a/apps/web/src/hooks/useActiveLocale.ts +++ b/apps/web/src/hooks/useActiveLocale.ts @@ -14,7 +14,8 @@ export function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | un } const lowerMaybeSupportedLocale = maybeSupportedLocale.toLowerCase() return SUPPORTED_LOCALES.find( - (locale) => locale.toLowerCase() === lowerMaybeSupportedLocale || locale.split('-')[0] === lowerMaybeSupportedLocale + (locale) => + locale.toLowerCase() === lowerMaybeSupportedLocale || locale.split('-')[0] === lowerMaybeSupportedLocale, ) } diff --git a/apps/web/src/hooks/useApproveCallback.ts b/apps/web/src/hooks/useApproveCallback.ts index 47d2d2f3bdd..e897dbc6733 100644 --- a/apps/web/src/hooks/useApproveCallback.ts +++ b/apps/web/src/hooks/useApproveCallback.ts @@ -26,7 +26,7 @@ function useGetAndTrackApproval(getApproval: ReturnType[1]) // returns a variable indicating the state of the approval and a function which approves if necessary or early returns export function useApproveCallback( amountToApprove?: CurrencyAmount, - spender?: string + spender?: string, ): [ApprovalState, () => Promise] { const [approval, getApproval] = useApproval(amountToApprove, spender, useHasPendingApproval) return [approval, useGetAndTrackApproval(getApproval)] diff --git a/apps/web/src/hooks/useArgentWalletContract.ts b/apps/web/src/hooks/useArgentWalletContract.ts index 30562d5d645..b4a1e4b8ab1 100644 --- a/apps/web/src/hooks/useArgentWalletContract.ts +++ b/apps/web/src/hooks/useArgentWalletContract.ts @@ -10,6 +10,6 @@ export function useArgentWalletContract(): ArgentWalletContract | null { return useContract( isArgentWallet ? account.address : undefined, ArgentWalletContractABI, - true + true, ) as ArgentWalletContract } diff --git a/apps/web/src/hooks/useAutoRouterSupported.tsx b/apps/web/src/hooks/useAutoRouterSupported.tsx deleted file mode 100644 index 22000faa97e..00000000000 --- a/apps/web/src/hooks/useAutoRouterSupported.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { useAccount } from 'hooks/useAccount' - -export default function useAutoRouterSupported(): boolean { - const { chainId } = useAccount() - return !!chainId -} diff --git a/apps/web/src/hooks/useAutoSlippageTolerance.ts b/apps/web/src/hooks/useAutoSlippageTolerance.ts index c7ae2842dd3..6fe8d73fb2f 100644 --- a/apps/web/src/hooks/useAutoSlippageTolerance.ts +++ b/apps/web/src/hooks/useAutoSlippageTolerance.ts @@ -94,7 +94,7 @@ export default function useClassicAutoSlippageTolerance(trade?: ClassicTrade): P ? JSBI.multiply(nativeGasPrice, JSBI.BigInt(gasEstimate)) : undefined const gasCostUSD = useUSDPrice( - nativeCurrency && nativeGasCost ? CurrencyAmount.fromRawAmount(nativeCurrency, nativeGasCost) : undefined + nativeCurrency && nativeGasCost ? CurrencyAmount.fromRawAmount(nativeCurrency, nativeGasCost) : undefined, ) const gasCostStablecoinAmount = useStablecoinAmountFromFiatValue(gasCostUSD.data) diff --git a/apps/web/src/hooks/useColor.ts b/apps/web/src/hooks/useColor.ts index 3da854e427c..76f9b20eb90 100644 --- a/apps/web/src/hooks/useColor.ts +++ b/apps/web/src/hooks/useColor.ts @@ -19,7 +19,7 @@ export function useSrcColor(src?: string, currencyName?: string, backgroundColor const extractSrc = useMemo( () => (src?.includes('coingecko') ? 'https://corsproxy.io/?' + encodeURIComponent(src) : src), - [src] + [src], ) return useExtractedTokenColor(extractSrc, currencyName, backgroundColor ?? theme.surface1, theme.accent1) diff --git a/apps/web/src/hooks/useConfirmModalState.ts b/apps/web/src/hooks/useConfirmModalState.ts index 917f27cec17..5e325964f5b 100644 --- a/apps/web/src/hooks/useConfirmModalState.ts +++ b/apps/web/src/hooks/useConfirmModalState.ts @@ -90,7 +90,7 @@ export function useConfirmModalState({ formatCurrencyAmount({ amount: trade.inputAmount, type: NumberType.SwapTradeAmount, - }) + }), ) const wrapConfirmed = useIsTransactionConfirmed(wrapTxHash) const prevWrapConfirmed = usePrevious(wrapConfirmed) @@ -103,7 +103,7 @@ export function useConfirmModalState({ logger.warn('useConfirmModalState', 'catchUserReject', 'Failed to wrap', { error: e, trade }) setApprovalError(errorType) }, - [trade] + [trade], ) const performStep = useCallback( @@ -166,7 +166,7 @@ export function useConfirmModalState({ trace, catchUserReject, onSwap, - ] + ], ) const startSwapFlow = useCallback(() => { @@ -176,7 +176,7 @@ export function useConfirmModalState({ }, [generateRequiredSteps, performStep]) const previousSetupApprovalNeeded = usePrevious( - allowance.state === AllowanceState.REQUIRED ? allowance.needsSetupApproval : undefined + allowance.state === AllowanceState.REQUIRED ? allowance.needsSetupApproval : undefined, ) useEffect(() => { @@ -200,7 +200,7 @@ export function useConfirmModalState({ }, [allowance, performStep, previousSetupApprovalNeeded]) const previousRevocationPending = usePrevious( - allowance.state === AllowanceState.REQUIRED && allowance.isRevocationPending + allowance.state === AllowanceState.REQUIRED && allowance.isRevocationPending, ) useEffect(() => { if (allowance.state === AllowanceState.REQUIRED && previousRevocationPending && !allowance.isRevocationPending) { diff --git a/apps/web/src/hooks/useContract.ts b/apps/web/src/hooks/useContract.ts index e5ef97af501..a8572243a1d 100644 --- a/apps/web/src/hooks/useContract.ts +++ b/apps/web/src/hooks/useContract.ts @@ -39,7 +39,7 @@ import { NonfungiblePositionManager, UniswapInterfaceMulticall } from 'uniswap/s import { V3Migrator } from 'uniswap/src/abis/types/v3/V3Migrator' import WETH_ABI from 'uniswap/src/abis/weth.json' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' -import { UniverseChainId } from 'uniswap/src/types/chains' +import { InterfaceChainId, UniverseChainId } from 'uniswap/src/types/chains' import { getContract } from 'utilities/src/contracts/getContract' import { logger } from 'utilities/src/logger/logger' @@ -53,10 +53,11 @@ const { abi: V2MigratorABI } = V3MigratorJson export function useContract( addressOrAddressMap: string | { [chainId: number]: string } | undefined, ABI: any, - withSignerIfPossible = true + withSignerIfPossible = true, + chainId?: InterfaceChainId, ): T | null { const account = useAccount() - const provider = useEthersProvider() + const provider = useEthersProvider({ chainId: chainId ?? account.chainId }) return useMemo(() => { if (!addressOrAddressMap || !ABI || !provider || !account.chainId) { @@ -66,7 +67,7 @@ export function useContract( if (typeof addressOrAddressMap === 'string') { address = addressOrAddressMap } else { - address = addressOrAddressMap[account.chainId] + address = addressOrAddressMap[chainId ?? account.chainId] } if (!address) { return null @@ -82,7 +83,7 @@ export function useContract( }) return null } - }, [addressOrAddressMap, ABI, provider, account.chainId, account.address, withSignerIfPossible]) as T + }, [addressOrAddressMap, ABI, provider, chainId, account.chainId, account.address, withSignerIfPossible]) as T } function useMainnetContract(address: string | undefined, ABI: any): T | null { @@ -116,12 +117,12 @@ export function useTokenContract(tokenAddress?: string, withSignerIfPossible?: b return useContract(tokenAddress, ERC20_ABI, withSignerIfPossible) } -export function useWETHContract(withSignerIfPossible?: boolean) { - const { chainId } = useAccount() +export function useWETHContract(withSignerIfPossible?: boolean, chainId?: InterfaceChainId) { return useContract( chainId ? WRAPPED_NATIVE_CURRENCY[chainId]?.address : undefined, WETH_ABI, - withSignerIfPossible + withSignerIfPossible, + chainId, ) } @@ -169,7 +170,7 @@ export function useInterfaceMulticall() { export function useMainnetInterfaceMulticall() { return useMainnetContract( MULTICALL_ADDRESSES[UniverseChainId.Mainnet], - MulticallABI + MulticallABI, ) as UniswapInterfaceMulticall } @@ -178,7 +179,7 @@ export function useV3NFTPositionManagerContract(withSignerIfPossible?: boolean): const contract = useContract( NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, NFTPositionManagerABI, - withSignerIfPossible + withSignerIfPossible, ) useEffect(() => { if (contract && account.isConnected) { diff --git a/apps/web/src/hooks/useCurrentBlockTimestamp.ts b/apps/web/src/hooks/useCurrentBlockTimestamp.ts index f5ab326ae94..4172d3161fb 100644 --- a/apps/web/src/hooks/useCurrentBlockTimestamp.ts +++ b/apps/web/src/hooks/useCurrentBlockTimestamp.ts @@ -11,7 +11,7 @@ export default function useCurrentBlockTimestamp(options?: ListenerOptions): Big multicall, 'getCurrentBlockTimestamp', undefined, - options + options, )?.result?.[0]?.toString() return useMemo(() => (typeof resultStr === 'string' ? BigNumber.from(resultStr) : undefined), [resultStr]) } diff --git a/apps/web/src/hooks/useDebouncedChangeHandler.tsx b/apps/web/src/hooks/useDebouncedChangeHandler.tsx index 21a71f9b903..411c5898518 100644 --- a/apps/web/src/hooks/useDebouncedChangeHandler.tsx +++ b/apps/web/src/hooks/useDebouncedChangeHandler.tsx @@ -9,7 +9,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' export default function useDebouncedChangeHandler( value: T, onChange: (newValue: T) => void, - debouncedMs = 100 + debouncedMs = 100, ): [T, (value: T) => void] { const [inner, setInner] = useState(() => value) const timer = useRef>() @@ -25,7 +25,7 @@ export default function useDebouncedChangeHandler( timer.current = undefined }, debouncedMs) }, - [debouncedMs, onChange] + [debouncedMs, onChange], ) useEffect(() => { diff --git a/apps/web/src/hooks/useDebouncedTrade.ts b/apps/web/src/hooks/useDebouncedTrade.ts index 594d3d3562c..b286f78cdc3 100644 --- a/apps/web/src/hooks/useDebouncedTrade.ts +++ b/apps/web/src/hooks/useDebouncedTrade.ts @@ -2,7 +2,6 @@ import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core' import { routingPreferencesAtom } from 'components/Settings/MultipleRoutingOptions' import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens' import { useAccount } from 'hooks/useAccount' -import useAutoRouterSupported from 'hooks/useAutoRouterSupported' import useDebounce from 'hooks/useDebounce' import { useAtomValue } from 'jotai/utils' import { useMemo } from 'react' @@ -24,7 +23,7 @@ export function useDebouncedTrade( routerPreferenceOverride?: RouterPreference.X, account?: string, inputTax?: Percent, - outputTax?: Percent + outputTax?: Percent, ): { state: TradeState trade?: InterfaceTrade @@ -38,7 +37,7 @@ export function useDebouncedTrade( routerPreferenceOverride?: RouterPreference.API, account?: string, inputTax?: Percent, - outputTax?: Percent + outputTax?: Percent, ): { state: TradeState trade?: ClassicTrade @@ -60,7 +59,7 @@ export function useDebouncedTrade( routerPreferenceOverride?: RouterPreference, account?: string, inputTax?: Percent, - outputTax?: Percent + outputTax?: Percent, ): { state: TradeState trade?: InterfaceTrade @@ -68,11 +67,10 @@ export function useDebouncedTrade( swapQuoteLatency?: number } { const { chainId } = useAccount() - const autoRouterSupported = useAutoRouterSupported() const inputs = useMemo<[CurrencyAmount | undefined, Currency | undefined]>( () => [amountSpecified, otherCurrency], - [amountSpecified, otherCurrency] + [amountSpecified, otherCurrency], ) const isDebouncing = useDebounce(inputs, DEBOUNCE_TIME) !== inputs @@ -85,7 +83,7 @@ export function useDebouncedTrade( const weth = WRAPPED_NATIVE_CURRENCY[chainId] return Boolean( (amountSpecified.currency.isNative && weth?.equals(otherCurrency)) || - (otherCurrency.isNative && weth?.equals(amountSpecified.currency)) + (otherCurrency.isNative && weth?.equals(amountSpecified.currency)), ) }, [amountSpecified, chainId, otherCurrency]) @@ -94,7 +92,7 @@ export function useDebouncedTrade( const multipleRouteOptionsRoutingPreference = useAtomValue(routingPreferencesAtom) const routingPreference = multipleRouteOptionsEnabled ? multipleRouteOptionsRoutingPreference : undefined - const skipBothFetches = !autoRouterSupported || isWrap + const skipBothFetches = isWrap const skipRoutingFetch = skipBothFetches || isDebouncing const skipPreviewTradeFetch = skipBothFetches || isPreviewTradeDebouncing @@ -105,7 +103,7 @@ export function useDebouncedTrade( amountSpecified, otherCurrency, inputTax, - outputTax + outputTax, ) const routingApiTradeResult = useRoutingAPITrade( skipRoutingFetch, @@ -116,7 +114,7 @@ export function useDebouncedTrade( account, routingPreference?.protocols, inputTax, - outputTax + outputTax, ) return previewTradeResult.currentTrade && !routingApiTradeResult.currentTrade diff --git a/apps/web/src/hooks/useENS.ts b/apps/web/src/hooks/useENS.ts index 7af1f79c131..7514349b613 100644 --- a/apps/web/src/hooks/useENS.ts +++ b/apps/web/src/hooks/useENS.ts @@ -22,6 +22,6 @@ export default function useENS(nameOrAddress?: string | null): { address: validated ? validated : lookup.address, name: reverseLookup.ENSName ? reverseLookup.ENSName : !validated && lookup.address ? nameOrAddress || null : null, }), - [lookup.address, lookup.loading, nameOrAddress, reverseLookup.ENSName, reverseLookup.loading, validated] + [lookup.address, lookup.loading, nameOrAddress, reverseLookup.ENSName, reverseLookup.loading, validated], ) } diff --git a/apps/web/src/hooks/useENSAddress.ts b/apps/web/src/hooks/useENSAddress.ts index 861ea36ca68..45ae042828a 100644 --- a/apps/web/src/hooks/useENSAddress.ts +++ b/apps/web/src/hooks/useENSAddress.ts @@ -15,7 +15,7 @@ export default function useENSAddress(ensName?: string | null): { loading: boole const resolverAddressCall = useMainnetSingleCallResult(registrarContract, 'resolver', ensNodeArgument, NEVER_RELOAD) const resolverAddress = resolverAddressCall.result?.[0] const resolverContract = useENSResolverContract( - resolverAddress && !isZero(resolverAddress) ? resolverAddress : undefined + resolverAddress && !isZero(resolverAddress) ? resolverAddress : undefined, ) const addressCall = useMainnetSingleCallResult(resolverContract, 'addr', ensNodeArgument, NEVER_RELOAD) const address = addressCall.result?.[0] @@ -26,6 +26,6 @@ export default function useENSAddress(ensName?: string | null): { loading: boole address: changed ? null : address ?? null, loading: changed || resolverAddressCall.loading || addressCall.loading, }), - [addressCall.loading, address, changed, resolverAddressCall.loading] + [addressCall.loading, address, changed, resolverAddressCall.loading], ) } diff --git a/apps/web/src/hooks/useENSAvatar.ts b/apps/web/src/hooks/useENSAvatar.ts index 399d009ca04..6dc4076ce62 100644 --- a/apps/web/src/hooks/useENSAvatar.ts +++ b/apps/web/src/hooks/useENSAvatar.ts @@ -23,7 +23,7 @@ import { safeNamehash } from 'utils/safeNamehash' */ export default function useENSAvatar( address?: string, - enforceOwnership = true + enforceOwnership = true, ): { avatar: string | null; loading: boolean } { const debouncedAddress = useDebounce(address, 200) const node = useMemo(() => { @@ -49,7 +49,7 @@ export default function useENSAvatar( avatar: changed ? null : http ?? null, loading: changed || addressAvatar.loading || nameAvatar.loading || nftAvatar.loading, }), - [addressAvatar.loading, changed, http, nameAvatar.loading, nftAvatar.loading] + [addressAvatar.loading, changed, http, nameAvatar.loading, nftAvatar.loading], ) } @@ -60,7 +60,7 @@ function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } const resolverAddress = useMainnetSingleCallResult(registrarContract, 'resolver', nodeArgument, NEVER_RELOAD) const resolverAddressResult = resolverAddress.result?.[0] const resolverContract = useENSResolverContract( - resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined + resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined, ) const avatar = useMainnetSingleCallResult(resolverContract, 'text', textArgument, NEVER_RELOAD) @@ -69,14 +69,14 @@ function useAvatarFromNode(node?: string): { avatar?: string; loading: boolean } avatar: avatar.result?.[0], loading: avatar.loading, }), - [avatar.loading, avatar.result] + [avatar.loading, avatar.result], ) } function useAvatarFromNFT( nftUri = '', enforceOwnership: boolean, - address?: string + address?: string, ): { avatar?: string; loading: boolean } { const parts = nftUri.toLowerCase().split(':') const protocol = parts[0] @@ -91,7 +91,7 @@ function useAvatarFromNFT( isERC1155 ? contractAddress : undefined, address, isERC1155 ? id : undefined, - enforceOwnership + enforceOwnership, ) const uri = erc721.uri || erc1155.uri const http = uri && uriToHttpUrls(uri)[0] @@ -116,14 +116,14 @@ function useAvatarFromNFT( return useMemo( () => ({ avatar, loading: erc721.loading || erc1155.loading || loading }), - [avatar, erc1155.loading, erc721.loading, loading] + [avatar, erc1155.loading, erc721.loading, loading], ) } function useERC721Uri( contractAddress: string | undefined, id: string | undefined, - enforceOwnership: boolean + enforceOwnership: boolean, ): { uri?: string; loading: boolean } { const idArgument = useMemo(() => [id], [id]) const account = useAccount() @@ -135,7 +135,7 @@ function useERC721Uri( uri: !enforceOwnership || account.address === owner.result?.[0] ? uri.result?.[0] : undefined, loading: owner.loading || uri.loading, }), - [account.address, enforceOwnership, owner.loading, owner.result, uri.loading, uri.result] + [account.address, enforceOwnership, owner.loading, owner.result, uri.loading, uri.result], ) } @@ -143,7 +143,7 @@ function useERC1155Uri( contractAddress: string | undefined, ownerAddress: string | undefined, id: string | undefined, - enforceOwnership: boolean + enforceOwnership: boolean, ): { uri?: string; loading: boolean } { const idArgument = useMemo(() => [id], [id]) const accountArgument = useMemo(() => [ownerAddress, id], [ownerAddress, id]) diff --git a/apps/web/src/hooks/useENSName.ts b/apps/web/src/hooks/useENSName.ts index f0fb1fef05e..d8433713936 100644 --- a/apps/web/src/hooks/useENSName.ts +++ b/apps/web/src/hooks/useENSName.ts @@ -23,7 +23,7 @@ export default function useENSName(address?: string): { ENSName: string | null; const resolverAddress = useMainnetSingleCallResult(registrarContract, 'resolver', ensNodeArgument, NEVER_RELOAD) const resolverAddressResult = resolverAddress.result?.[0] const resolverContract = useENSResolverContract( - resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined + resolverAddressResult && !isZero(resolverAddressResult) ? resolverAddressResult : undefined, ) const nameCallRes = useMainnetSingleCallResult(resolverContract, 'name', ensNodeArgument, NEVER_RELOAD) const name = nameCallRes.result?.[0] @@ -41,6 +41,6 @@ export default function useENSName(address?: string): { ENSName: string | null; ENSName: changed ? null : checkedName, loading, }), - [changed, checkedName, loading] + [changed, checkedName, loading], ) } diff --git a/apps/web/src/hooks/useERC20Permit.ts b/apps/web/src/hooks/useERC20Permit.ts index e318b42b8fe..cbbf56cf667 100644 --- a/apps/web/src/hooks/useERC20Permit.ts +++ b/apps/web/src/hooks/useERC20Permit.ts @@ -109,7 +109,7 @@ export function useERC20Permit( currencyAmount: CurrencyAmount | null | undefined, spender: string | null | undefined, transactionDeadline: BigNumber | undefined, - overridePermitInfo: PermitInfo | undefined | null + overridePermitInfo: PermitInfo | undefined | null, ): { signatureData: SignatureData | null state: UseERC20PermitState diff --git a/apps/web/src/hooks/useEthersProvider.ts b/apps/web/src/hooks/useEthersProvider.ts index 86a3d25fba5..82bd2bdd432 100644 --- a/apps/web/src/hooks/useEthersProvider.ts +++ b/apps/web/src/hooks/useEthersProvider.ts @@ -20,8 +20,8 @@ function clientToProvider(client?: Client, chainId ensAddress, } : chainId - ? { chainId, name: 'Unsupported' } - : undefined + ? { chainId, name: 'Unsupported' } + : undefined if (!network) { return undefined } diff --git a/apps/web/src/hooks/useFeeTierDistribution.ts b/apps/web/src/hooks/useFeeTierDistribution.ts index acaef256a35..e7c5b138cb0 100644 --- a/apps/web/src/hooks/useFeeTierDistribution.ts +++ b/apps/web/src/hooks/useFeeTierDistribution.ts @@ -21,7 +21,7 @@ interface FeeTierDistribution { export function useFeeTierDistribution( currencyA: Currency | undefined, - currencyB: Currency | undefined + currencyB: Currency | undefined, ): FeeTierDistribution { const { isLoading, error, distributions } = usePoolTVL(currencyA?.wrapped, currencyB?.wrapped) @@ -122,7 +122,7 @@ function usePoolTVL(token0: Token | undefined, token1: Token | undefined) { [FeeAmount.LOW]: [undefined, undefined], [FeeAmount.MEDIUM]: [undefined, undefined], [FeeAmount.HIGH]: [undefined, undefined], - } as Record + } as Record, ) // sum total tvl for token0 and token1 @@ -132,7 +132,7 @@ function usePoolTVL(token0: Token | undefined, token1: Token | undefined) { acc[1] += value[1] ?? 0 return acc }, - [0, 0] + [0, 0], ) // returns undefined if both tvl0 and tvl1 are undefined (pool not created) @@ -144,20 +144,20 @@ function usePoolTVL(token0: Token | undefined, token1: Token | undefined) { tvlByFeeTier[FeeAmount.LOWEST][0], sumToken0Tvl, tvlByFeeTier[FeeAmount.LOWEST][1], - sumToken1Tvl + sumToken1Tvl, ), [FeeAmount.LOW]: mean(tvlByFeeTier[FeeAmount.LOW][0], sumToken0Tvl, tvlByFeeTier[FeeAmount.LOW][1], sumToken1Tvl), [FeeAmount.MEDIUM]: mean( tvlByFeeTier[FeeAmount.MEDIUM][0], sumToken0Tvl, tvlByFeeTier[FeeAmount.MEDIUM][1], - sumToken1Tvl + sumToken1Tvl, ), [FeeAmount.HIGH]: mean( tvlByFeeTier[FeeAmount.HIGH][0], sumToken0Tvl, tvlByFeeTier[FeeAmount.HIGH][1], - sumToken1Tvl + sumToken1Tvl, ), } diff --git a/apps/web/src/hooks/useFetchListCallback.ts b/apps/web/src/hooks/useFetchListCallback.ts index 9cf1ff7dcd5..f9f993fbc03 100644 --- a/apps/web/src/hooks/useFetchListCallback.ts +++ b/apps/web/src/hooks/useFetchListCallback.ts @@ -19,7 +19,7 @@ export function useFetchListCallback(): (listUrl: string, skipValidation?: boole return getTokenList( listUrl, (ensName: string) => resolveENSContentHash(ensName, RPC_PROVIDERS[UniverseChainId.Mainnet]), - skipValidation + skipValidation, ) .then((tokenList) => { dispatch(fetchTokenList.fulfilled({ url: listUrl, tokenList, requestId })) @@ -31,6 +31,6 @@ export function useFetchListCallback(): (listUrl: string, skipValidation?: boole throw error }) }, - [dispatch] + [dispatch], ) } diff --git a/apps/web/src/hooks/useFilterPossiblyMaliciousPositions.ts b/apps/web/src/hooks/useFilterPossiblyMaliciousPositions.ts index b170c884645..06e69b8567b 100644 --- a/apps/web/src/hooks/useFilterPossiblyMaliciousPositions.ts +++ b/apps/web/src/hooks/useFilterPossiblyMaliciousPositions.ts @@ -17,7 +17,7 @@ import { hasURL } from 'utils/urlChecks' function getUniqueAddressesFromPositions(positions: PositionDetails[]): string[] { return Array.from( - new Set(positions.reduce((acc, position) => acc.concat(position.token0, position.token1), [])) + new Set(positions.reduce((acc, position) => acc.concat(position.token0, position.token1), [])), ) } diff --git a/apps/web/src/hooks/useGroupedRecentTransfers.ts b/apps/web/src/hooks/useGroupedRecentTransfers.ts index 859a87e18f1..4e28dbdd785 100644 --- a/apps/web/src/hooks/useGroupedRecentTransfers.ts +++ b/apps/web/src/hooks/useGroupedRecentTransfers.ts @@ -6,18 +6,21 @@ export function useGroupedRecentTransfers(account?: string) { return useMemo( () => ({ - transfers: recentTransfers?.reduce((acc, transfer) => { - const address = transfer.recipient - if (acc[address]) { - acc[address]++ - } else { - acc[address] = 1 - } - return acc - }, {} as { [address: string]: number }), + transfers: recentTransfers?.reduce( + (acc, transfer) => { + const address = transfer.recipient + if (acc[address]) { + acc[address]++ + } else { + acc[address] = 1 + } + return acc + }, + {} as { [address: string]: number }, + ), loading, }), - [loading, recentTransfers] + [loading, recentTransfers], ) } diff --git a/apps/web/src/hooks/useIsPoolOutOfSync.ts b/apps/web/src/hooks/useIsPoolOutOfSync.ts index 0cdb6db4e4d..f8542e365a3 100644 --- a/apps/web/src/hooks/useIsPoolOutOfSync.ts +++ b/apps/web/src/hooks/useIsPoolOutOfSync.ts @@ -14,7 +14,7 @@ function useMarketPrice(baseCurrency?: Currency, quoteCurrency?: Currency) { baseCurrency ? CurrencyAmount.fromRawAmount(baseCurrency, JSBI.BigInt(parseUnits('1', baseCurrency?.decimals))) : undefined, - baseCurrency + baseCurrency, ) const baseCurrencyStableCoinAmount = useStablecoinAmountFromFiatValue(baseCurrencyUSDPrice.data) @@ -22,7 +22,7 @@ function useMarketPrice(baseCurrency?: Currency, quoteCurrency?: Currency) { quoteCurrency ? CurrencyAmount.fromRawAmount(quoteCurrency, JSBI.BigInt(parseUnits('1', quoteCurrency?.decimals))) : undefined, - quoteCurrency + quoteCurrency, ) const quoteCurrencyStableCoinAmount = useStablecoinAmountFromFiatValue(quoteCurrencyUSDPrice.data) @@ -32,7 +32,7 @@ function useMarketPrice(baseCurrency?: Currency, quoteCurrency?: Currency) { const marketPrice = new Fraction( baseCurrencyStableCoinAmount.multiply(DECIMAL_SCALAR).toFixed(0), - quoteCurrencyStableCoinAmount.multiply(DECIMAL_SCALAR).toFixed(0) + quoteCurrencyStableCoinAmount.multiply(DECIMAL_SCALAR).toFixed(0), ) return marketPrice @@ -60,11 +60,11 @@ export function useIsPoolOutOfSync(poolPrice?: Price) { .quote( CurrencyAmount.fromRawAmount( poolPrice.baseCurrency?.wrapped, - JSBI.BigInt(parseUnits('1', poolPrice.baseCurrency?.decimals)) - ) + JSBI.BigInt(parseUnits('1', poolPrice.baseCurrency?.decimals)), + ), ) .multiply(DECIMAL_SCALAR) - .toFixed(0) + .toFixed(0), ) const difference = JSBI.lessThan(scaledMarketPrice, scaledPoolPrice) diff --git a/apps/web/src/hooks/useIsTickAtLimit.ts b/apps/web/src/hooks/useIsTickAtLimit.ts index 930f7087b02..a1e22690af1 100644 --- a/apps/web/src/hooks/useIsTickAtLimit.ts +++ b/apps/web/src/hooks/useIsTickAtLimit.ts @@ -5,7 +5,7 @@ import { Bound } from 'state/mint/v3/actions' export default function useIsTickAtLimit( feeAmount: FeeAmount | undefined, tickLower: number | undefined, - tickUpper: number | undefined + tickUpper: number | undefined, ) { return useMemo( () => ({ @@ -18,6 +18,6 @@ export default function useIsTickAtLimit( ? tickUpper === nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[feeAmount as FeeAmount]) : undefined, }), - [feeAmount, tickLower, tickUpper] + [feeAmount, tickLower, tickUpper], ) } diff --git a/apps/web/src/hooks/useLocalCurrencyLinkProps.ts b/apps/web/src/hooks/useLocalCurrencyLinkProps.ts index eb760fdcabc..c98d08bace7 100644 --- a/apps/web/src/hooks/useLocalCurrencyLinkProps.ts +++ b/apps/web/src/hooks/useLocalCurrencyLinkProps.ts @@ -35,6 +35,6 @@ export function useLocalCurrencyLinkProps(localCurrency?: SupportedLocalCurrency }) }, }, - [localCurrency, location, qs, updateActiveLocalCurrency, activeLocalCurrency] + [localCurrency, location, qs, updateActiveLocalCurrency, activeLocalCurrency], ) } diff --git a/apps/web/src/hooks/useLocationLinkProps.ts b/apps/web/src/hooks/useLocationLinkProps.ts index c1bcd213e95..3e0a29dd416 100644 --- a/apps/web/src/hooks/useLocationLinkProps.ts +++ b/apps/web/src/hooks/useLocationLinkProps.ts @@ -22,6 +22,6 @@ export function useLocationLinkProps(locale: SupportedLocale | null): { search: stringify({ ...qs, lng: locale }), }, }, - [location, qs, locale] + [location, qs, locale], ) } diff --git a/apps/web/src/hooks/useMachineTime.ts b/apps/web/src/hooks/useMachineTime.ts index 40bd86e9fb3..00c9fc2de8d 100644 --- a/apps/web/src/hooks/useMachineTime.ts +++ b/apps/web/src/hooks/useMachineTime.ts @@ -8,7 +8,7 @@ const useMachineTimeMs = (updateInterval: number): number => { useCallback(() => { setNow(Date.now()) }, []), - updateInterval + updateInterval, ) return now } diff --git a/apps/web/src/hooks/useOnClickOutside.ts b/apps/web/src/hooks/useOnClickOutside.ts index 7998812db07..fe7ae800109 100644 --- a/apps/web/src/hooks/useOnClickOutside.ts +++ b/apps/web/src/hooks/useOnClickOutside.ts @@ -20,7 +20,7 @@ function nodeContainsClick(node: RefObject export function useOnClickOutside( node: RefObject, handler: undefined | (() => void), - ignoredNodes: Array> = [] + ignoredNodes: Array> = [], ) { const handlerRef = useRef void)>(handler) diff --git a/apps/web/src/hooks/usePermit2Allowance.ts b/apps/web/src/hooks/usePermit2Allowance.ts index 33cf252e60c..1c67d540a7a 100644 --- a/apps/web/src/hooks/usePermit2Allowance.ts +++ b/apps/web/src/hooks/usePermit2Allowance.ts @@ -47,7 +47,7 @@ export type Allowance = export default function usePermit2Allowance( amount?: CurrencyAmount, spender?: string, - tradeFillType?: TradeFillType + tradeFillType?: TradeFillType, ): Allowance { const account = useAccount() const token = amount?.currency @@ -55,7 +55,7 @@ export default function usePermit2Allowance( const { tokenAllowance, isSyncing: isApprovalSyncing } = useTokenAllowance( token, account.address, - permit2AddressForChain + permit2AddressForChain, ) const updateTokenAllowance = useUpdateTokenAllowance(amount, permit2AddressForChain) const revokeTokenAllowance = useRevokeTokenAllowance(token, permit2AddressForChain) @@ -94,7 +94,7 @@ export default function usePermit2Allowance( const [now, setNow] = useState(Date.now() + AVERAGE_L1_BLOCK_TIME) useInterval( useCallback(() => setNow((Date.now() + AVERAGE_L1_BLOCK_TIME) / 1000), []), - AVERAGE_L1_BLOCK_TIME + AVERAGE_L1_BLOCK_TIME, ) const [signature, setSignature] = useState() diff --git a/apps/web/src/hooks/usePermitAllowance.ts b/apps/web/src/hooks/usePermitAllowance.ts index 888aba6fc9f..0079c2d33db 100644 --- a/apps/web/src/hooks/usePermitAllowance.ts +++ b/apps/web/src/hooks/usePermitAllowance.ts @@ -34,13 +34,13 @@ export function usePermitAllowance(token?: Token, owner?: string, spender?: stri const rawAmount = result?.amount.toString() // convert to a string before using in a hook, to avoid spurious rerenders const allowance = useMemo( () => (token && rawAmount ? CurrencyAmount.fromRawAmount(token, rawAmount) : undefined), - [token, rawAmount] + [token, rawAmount], ) useEffect(() => setBlocksPerFetch(allowance?.equalTo(0) ? 1 : undefined), [allowance]) return useMemo( () => ({ permitAllowance: allowance, expiration: result?.expiration, nonce: result?.nonce }), - [allowance, result?.expiration, result?.nonce] + [allowance, result?.expiration, result?.nonce], ) } @@ -56,7 +56,7 @@ export function useUpdatePermitAllowance( token: Token | undefined, spender: string | undefined, nonce: number | undefined, - onPermitSignature: (signature: PermitSignature) => void + onPermitSignature: (signature: PermitSignature) => void, ) { const account = useAccount() const signer = useEthersSigner() @@ -97,7 +97,7 @@ export function useUpdatePermitAllowance( const { domain, types, values } = AllowanceTransfer.getPermitData( permit, permit2Address(token?.chainId), - account.chainId + account.chainId, ) const signature = await trace.child({ name: 'Sign', op: 'wallet.sign' }, async (walletTrace) => { try { @@ -124,6 +124,6 @@ export function useUpdatePermitAllowance( } } }), - [account.chainId, account.status, nonce, onPermitSignature, signer, spender, token] + [account.chainId, account.status, nonce, onPermitSignature, signer, spender, token], ) } diff --git a/apps/web/src/hooks/usePoolTickData.ts b/apps/web/src/hooks/usePoolTickData.ts index 7b4203d23e9..417f8d0be79 100644 --- a/apps/web/src/hooks/usePoolTickData.ts +++ b/apps/web/src/hooks/usePoolTickData.ts @@ -32,7 +32,7 @@ function useTicksFromSubgraph( currencyB: Currency | undefined, feeAmount: FeeAmount | undefined, skip = 0, - chainId: InterfaceChainId + chainId: InterfaceChainId, ) { const poolAddress = currencyA && currencyB && feeAmount @@ -41,7 +41,7 @@ function useTicksFromSubgraph( currencyB?.wrapped, feeAmount, undefined, - chainId ? V3_CORE_FACTORY_ADDRESSES[chainId] : undefined + chainId ? V3_CORE_FACTORY_ADDRESSES[chainId] : undefined, ) : undefined const supportedChainId = useSupportedChainId(chainId) @@ -63,7 +63,7 @@ function useAllV3Ticks( currencyA: Currency | undefined, currencyB: Currency | undefined, feeAmount: FeeAmount | undefined, - chainId: InterfaceChainId + chainId: InterfaceChainId, ): { isLoading: boolean error: unknown @@ -94,7 +94,7 @@ export function usePoolActiveLiquidity( currencyA: Currency | undefined, currencyB: Currency | undefined, feeAmount: FeeAmount | undefined, - chainId?: InterfaceChainId + chainId?: InterfaceChainId, ): { isLoading: boolean error: any diff --git a/apps/web/src/hooks/usePools.ts b/apps/web/src/hooks/usePools.ts index 62878372d90..7ddf29e4b79 100644 --- a/apps/web/src/hooks/usePools.ts +++ b/apps/web/src/hooks/usePools.ts @@ -30,7 +30,7 @@ export class PoolCache { tokenA: Token, tokenB: Token, fee: FeeAmount, - chainId: InterfaceChainId + chainId: InterfaceChainId, ): string { if (this.addresses.length > this.MAX_ENTRIES) { this.addresses = this.addresses.slice(0, this.MAX_ENTRIES / 2) @@ -64,7 +64,7 @@ export class PoolCache { fee: FeeAmount, sqrtPriceX96: BigintIsh, liquidity: BigintIsh, - tick: number + tick: number, ): Pool { if (this.pools.length > this.MAX_ENTRIES) { this.pools = this.pools.slice(0, this.MAX_ENTRIES / 2) @@ -77,7 +77,7 @@ export class PoolCache { pool.fee === fee && JSBI.EQ(pool.sqrtRatioX96, sqrtPriceX96) && JSBI.EQ(pool.liquidity, liquidity) && - pool.tickCurrent === tick + pool.tickCurrent === tick, ) if (found) { return found @@ -97,7 +97,7 @@ export enum PoolState { } export function usePools( - poolKeys: [Currency | undefined, Currency | undefined, FeeAmount | undefined][] + poolKeys: [Currency | undefined, Currency | undefined, FeeAmount | undefined][], ): [PoolState, Pool | null][] { const { chainId } = useAccount() @@ -188,11 +188,11 @@ export function usePools( export function usePool( currencyA: Currency | undefined, currencyB: Currency | undefined, - feeAmount: FeeAmount | undefined + feeAmount: FeeAmount | undefined, ): [PoolState, Pool | null] { const poolKeys: [Currency | undefined, Currency | undefined, FeeAmount | undefined][] = useMemo( () => [[currencyA, currencyB, feeAmount]], - [currencyA, currencyB, feeAmount] + [currencyA, currencyB, feeAmount], ) return usePools(poolKeys)[0] @@ -202,7 +202,7 @@ export function usePoolMultichain( tokenA: Token | undefined, tokenB: Token | undefined, fee: number | undefined, - chainId: InterfaceChainId + chainId: InterfaceChainId, ): [PoolState, Pool | null] { const poolData = useRef<[PoolState, Pool | null]>([PoolState.LOADING, null]) const poolAddress = diff --git a/apps/web/src/hooks/usePositionTokenURI.ts b/apps/web/src/hooks/usePositionTokenURI.ts index 67f0376a912..e3216ce9de3 100644 --- a/apps/web/src/hooks/usePositionTokenURI.ts +++ b/apps/web/src/hooks/usePositionTokenURI.ts @@ -31,7 +31,7 @@ export function usePositionTokenURI(tokenId: TokenId | undefined): UsePositionTo const contract = useV3NFTPositionManagerContract() const inputs = useMemo( () => [tokenId instanceof BigNumber ? tokenId.toHexString() : tokenId?.toString(16)], - [tokenId] + [tokenId], ) const { result, error, loading, valid } = useSingleCallResult(contract, 'tokenURI', inputs, { ...NEVER_RELOAD, diff --git a/apps/web/src/hooks/useSelectChain.ts b/apps/web/src/hooks/useSelectChain.ts index f344e7b1c61..96b85147abf 100644 --- a/apps/web/src/hooks/useSelectChain.ts +++ b/apps/web/src/hooks/useSelectChain.ts @@ -18,7 +18,7 @@ export default function useSelectChain() { removePopup({ content: { failedSwitchNetwork: targetChain, type: PopupType.FailedSwitchNetwork }, key: 'failed-network-switch', - }) + }), ) return true } catch (error) { @@ -31,13 +31,13 @@ export default function useSelectChain() { addPopup({ content: { failedSwitchNetwork: targetChain, type: PopupType.FailedSwitchNetwork }, key: 'failed-network-switch', - }) + }), ) } - + // TODO(WEB-3306): This UX could be improved to show an error state. return false } }, - [dispatch, switchChain] + [dispatch, switchChain], ) } diff --git a/apps/web/src/hooks/useSendCallback.ts b/apps/web/src/hooks/useSendCallback.ts index 26c682ef5d3..43a03acf0a0 100644 --- a/apps/web/src/hooks/useSendCallback.ts +++ b/apps/web/src/hooks/useSendCallback.ts @@ -72,7 +72,7 @@ export function useSendCallback({ throw error } } - } + }, ) const sendInfo: SendTransactionInfo = { type: TransactionType.SEND, @@ -101,6 +101,6 @@ export function useSendCallback({ gasFee?.params, supportedTransactionChainId, switchChain, - ] + ], ) } diff --git a/apps/web/src/hooks/useStablecoinPrice.ts b/apps/web/src/hooks/useStablecoinPrice.ts index ae838c61a67..0064c07a02c 100644 --- a/apps/web/src/hooks/useStablecoinPrice.ts +++ b/apps/web/src/hooks/useStablecoinPrice.ts @@ -20,7 +20,7 @@ export default function useStablecoinPrice(currency?: Currency): Price { if (!currency || !stablecoin) { diff --git a/apps/web/src/hooks/useSwapCallback.tsx b/apps/web/src/hooks/useSwapCallback.tsx index 83c0405a2bb..53c7194ccb8 100644 --- a/apps/web/src/hooks/useSwapCallback.tsx +++ b/apps/web/src/hooks/useSwapCallback.tsx @@ -1,10 +1,11 @@ import { Percent, TradeType } from '@uniswap/sdk-core' import { FlatFeeOptions } from '@uniswap/universal-router-sdk' import { FeeOptions } from '@uniswap/v3-sdk' -import { useSupportedChainId } from 'constants/chains' +import { SupportedInterfaceChainId, useSupportedChainId } from 'constants/chains' import { BigNumber } from 'ethers/lib/ethers' import { useAccount } from 'hooks/useAccount' import { PermitSignature } from 'hooks/usePermitAllowance' +import useSelectChain from 'hooks/useSelectChain' import { useUniswapXSwapCallback } from 'hooks/useUniswapXSwapCallback' import { useUniversalRouterSwapCallback } from 'hooks/useUniversalRouter' import { useCallback } from 'react' @@ -46,13 +47,13 @@ export function useSwapCallback( trade: InterfaceTrade | undefined, // trade to execute, required fiatValues: { amountIn?: number; amountOut?: number; feeUsd?: number }, // usd values for amount in and out, and the fee value, logged for analytics allowedSlippage: Percent, // in bips - permitSignature: PermitSignature | undefined + permitSignature: PermitSignature | undefined, ) { const addTransaction = useTransactionAdder() const addOrder = useAddOrder() const account = useAccount() - const { chainId } = useSwapAndLimitContext() - const supportedChainId = useSupportedChainId(chainId) + const supportedConnectedChainId = useSupportedChainId(account.chainId) + const { chainId: swapChainId } = useSwapAndLimitContext() const uniswapXSwapCallback = useUniswapXSwapCallback({ trade: isUniswapXTrade(trade) ? trade : undefined, @@ -67,22 +68,25 @@ export function useSwapCallback( slippageTolerance: allowedSlippage, permit: permitSignature, ...getUniversalRouterFeeFields(trade), - } + }, ) + const selectChain = useSelectChain() const swapCallback = isUniswapXTrade(trade) ? uniswapXSwapCallback : universalRouterSwapCallback return useCallback(async () => { if (!trade) { throw new Error('missing trade') - } - if (!account.address || !account.chainId) { + } else if (!account.isConnected || !account.address) { throw new Error('wallet must be connected to swap') + } else if (!swapChainId) { + throw new Error('missing swap chainId') + } else if (!supportedConnectedChainId || supportedConnectedChainId !== swapChainId) { + const correctChain = await selectChain(swapChainId) + if (!correctChain) { + throw new Error('wallet must be connected to correct chain to swap') + } } - if (!supportedChainId) { - throw new Error('unsupported chain') - } - const result = await swapCallback() const swapInfo: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo = { @@ -111,11 +115,11 @@ export function useSwapCallback( addOrder( account.address, result.response.orderHash, - supportedChainId, + supportedConnectedChainId as SupportedInterfaceChainId, // satisfies type-checker; already checked & switched chain above if !supportedConnectedChainId result.response.deadline, swapInfo as UniswapXOrderDetails['swapInfo'], result.response.encodedOrder, - isUniswapXTrade(trade) ? trade.offchainOrderType : OffchainOrderType.DUTCH_AUCTION // satisfying type-checker; isUniswapXTrade should always be true + isUniswapXTrade(trade) ? trade.offchainOrderType : OffchainOrderType.DUTCH_AUCTION, // satisfying type-checker; isUniswapXTrade should always be true ) break default: @@ -125,12 +129,14 @@ export function useSwapCallback( return result }, [ account.address, - account.chainId, + account.isConnected, addOrder, addTransaction, allowedSlippage, - supportedChainId, + selectChain, + supportedConnectedChainId, swapCallback, + swapChainId, trade, ]) } diff --git a/apps/web/src/hooks/useSwapTaxes.ts b/apps/web/src/hooks/useSwapTaxes.ts index 2ac3365dc8e..2033271abe5 100644 --- a/apps/web/src/hooks/useSwapTaxes.ts +++ b/apps/web/src/hooks/useSwapTaxes.ts @@ -35,9 +35,9 @@ function getFeeOnTransferAddress(chainId?: InterfaceChainId) { } } -function useFeeOnTransferDetectorContract(): FeeOnTransferDetector | null { +function useFeeOnTransferDetectorContract(chainId?: InterfaceChainId): FeeOnTransferDetector | null { const account = useAccount() - const contract = useContract(getFeeOnTransferAddress(account.chainId), FOT_DETECTOR_ABI) + const contract = useContract(getFeeOnTransferAddress(chainId), FOT_DETECTOR_ABI, true, chainId) useEffect(() => { if (contract && account.address) { @@ -45,11 +45,11 @@ function useFeeOnTransferDetectorContract(): FeeOnTransferDetector | null { source: 'useFeeOnTransferDetectorContract', contract: { name: 'FeeOnTransferDetector', - address: getFeeOnTransferAddress(account.chainId), + address: getFeeOnTransferAddress(chainId), }, }) } - }, [account.address, account.chainId, contract]) + }, [account.address, chainId, contract]) return contract } @@ -61,7 +61,7 @@ async function getSwapTaxes( fotDetector: FeeOnTransferDetector, inputTokenAddress: string | undefined, outputTokenAddress: string | undefined, - chainId: InterfaceChainId + chainId: InterfaceChainId, ) { const addresses = [] if (inputTokenAddress && FEE_CACHE[inputTokenAddress] === undefined) { @@ -94,10 +94,11 @@ async function getSwapTaxes( return { inputTax, outputTax } } -export function useSwapTaxes(inputTokenAddress?: string, outputTokenAddress?: string) { - const fotDetector = useFeeOnTransferDetectorContract() +export function useSwapTaxes(inputTokenAddress?: string, outputTokenAddress?: string, tokenChainId?: InterfaceChainId) { + const account = useAccount() + const chainId = tokenChainId ?? account.chainId + const fotDetector = useFeeOnTransferDetectorContract(chainId) const [{ inputTax, outputTax }, setTaxes] = useState({ inputTax: ZERO_PERCENT, outputTax: ZERO_PERCENT }) - const { chainId } = useAccount() useEffect(() => { if (!fotDetector || !chainId) { diff --git a/apps/web/src/hooks/useSwitchChain.ts b/apps/web/src/hooks/useSwitchChain.ts index 6f6ee5f0acd..b31afa0c7b7 100644 --- a/apps/web/src/hooks/useSwitchChain.ts +++ b/apps/web/src/hooks/useSwitchChain.ts @@ -62,9 +62,9 @@ export function useSwitchChain() { resolve() } }, - } + }, ) - }) + }), ) }, [ @@ -76,6 +76,6 @@ export function useSwitchChain() { page, searchParams, setSearchParams, - ] + ], ) } diff --git a/apps/web/src/hooks/useTokenAllowance.ts b/apps/web/src/hooks/useTokenAllowance.ts index 98de6311040..0f05b0c8906 100644 --- a/apps/web/src/hooks/useTokenAllowance.ts +++ b/apps/web/src/hooks/useTokenAllowance.ts @@ -16,7 +16,7 @@ const MAX_ALLOWANCE = MaxUint256.toString() export function useTokenAllowance( token?: Token, owner?: string, - spender?: string + spender?: string, ): { tokenAllowance?: CurrencyAmount isSyncing: boolean @@ -35,7 +35,7 @@ export function useTokenAllowance( const rawAmount = result?.toString() // convert to a string before using in a hook, to avoid spurious rerenders const allowance = useMemo( () => (token && rawAmount ? CurrencyAmount.fromRawAmount(token, rawAmount) : undefined), - [token, rawAmount] + [token, rawAmount], ) useEffect(() => setBlocksPerFetch(allowance?.equalTo(0) ? 1 : undefined), [allowance]) @@ -44,7 +44,7 @@ export function useTokenAllowance( export function useUpdateTokenAllowance( amount: CurrencyAmount | undefined, - spender: string + spender: string, ): () => Promise<{ response: ContractTransaction; info: ApproveTransactionInfo }> { const contract = useTokenContract(amount?.currency.address) const analyticsTrace = useTrace() @@ -102,13 +102,13 @@ export function useUpdateTokenAllowance( } } }), - [amount, contract, spender, analyticsTrace] + [amount, contract, spender, analyticsTrace], ) } export function useRevokeTokenAllowance( token: Token | undefined, - spender: string + spender: string, ): () => Promise<{ response: ContractTransaction; info: ApproveTransactionInfo }> { const amount = useMemo(() => (token ? CurrencyAmount.fromRawAmount(token, 0) : undefined), [token]) diff --git a/apps/web/src/hooks/useTokenBalances.test.ts b/apps/web/src/hooks/useTokenBalances.test.ts index 27aacd26877..c9d98fe1753 100644 --- a/apps/web/src/hooks/useTokenBalances.test.ts +++ b/apps/web/src/hooks/useTokenBalances.test.ts @@ -4,7 +4,7 @@ import { useTokenBalancesQuery } from 'graphql/data/apollo/TokenBalancesProvider import { useTokenBalances } from 'hooks/useTokenBalances' import { mocked } from 'test-utils/mocked' import { renderHook } from 'test-utils/render' -import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { Chain, useQuickTokenBalancesWebQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' jest.mock('@web3-react/core', () => ({ useWeb3React: jest.fn(() => ({ account: '0x123', chainId: 1 })), @@ -15,9 +15,16 @@ jest.mock('graphql/data/apollo/TokenBalancesProvider', () => ({ useTokenBalancesQuery: jest.fn(() => ({ data: {}, loading: false })), })) +jest.mock('uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks', () => ({ + ...jest.requireActual('uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'), + useQuickTokenBalancesWebQuery: jest.fn(() => ({ data: {}, loading: false })), +})) + describe('useTokenBalances', () => { it('should return empty balances when loading', () => { mocked(useTokenBalancesQuery).mockReturnValueOnce({ data: undefined, loading: true } as any) + mocked(useQuickTokenBalancesWebQuery).mockReturnValueOnce({ data: undefined, loading: true } as any) + const { loading, balanceList, balanceMap } = renderHook(() => useTokenBalances()).result.current expect(balanceMap).toEqual({}) expect(loading).toEqual(true) @@ -26,6 +33,7 @@ describe('useTokenBalances', () => { it('should return empty balances when user is not connected', () => { mocked(useWeb3React).mockReturnValueOnce({ account: undefined, chainId: undefined } as any) mocked(useTokenBalancesQuery).mockReturnValueOnce({ data: undefined, loading: false } as any) + mocked(useQuickTokenBalancesWebQuery).mockReturnValueOnce({ data: undefined, loading: false } as any) const { loading, balanceList, balanceMap } = renderHook(() => useTokenBalances()).result.current expect(balanceMap).toEqual({}) expect(loading).toEqual(false) diff --git a/apps/web/src/hooks/useTokenBalances.ts b/apps/web/src/hooks/useTokenBalances.ts index e090bf9323c..a52959cd26f 100644 --- a/apps/web/src/hooks/useTokenBalances.ts +++ b/apps/web/src/hooks/useTokenBalances.ts @@ -1,7 +1,15 @@ import { useTokenBalancesQuery } from 'graphql/data/apollo/TokenBalancesProvider' +import { GQL_MAINNET_CHAINS_MUTABLE } from 'graphql/data/util' +import { useAccount } from 'hooks/useAccount' import { TokenBalances } from 'lib/hooks/useTokenList/sorting' import { useMemo } from 'react' -import { PortfolioTokenBalancePartsFragment } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { + PortfolioTokenBalancePartsFragment, + QuickTokenBalancePartsFragment, + useQuickTokenBalancesWebQuery, +} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { FeatureFlags } from 'uniswap/src/features/gating/flags' +import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { currencyKeyFromGraphQL } from 'utils/currencyKey' /** @@ -9,10 +17,25 @@ import { currencyKeyFromGraphQL } from 'utils/currencyKey' */ export function useTokenBalances({ cacheOnly }: { cacheOnly?: boolean } = {}): { balanceMap: TokenBalances - balanceList: readonly (PortfolioTokenBalancePartsFragment | undefined)[] + balanceList: readonly (QuickTokenBalancePartsFragment | PortfolioTokenBalancePartsFragment | undefined)[] loading: boolean } { - const { data, loading } = useTokenBalancesQuery({ cacheOnly }) + const account = useAccount() + const multichainUXEnabled = useFeatureFlag(FeatureFlags.MultichainUX) + + // Quick result is always available at pageload, but never refetched when stale + const quickQueryResult = useQuickTokenBalancesWebQuery({ + variables: { + ownerAddress: account.address ?? '', + chains: GQL_MAINNET_CHAINS_MUTABLE, + }, + skip: !account.address || !multichainUXEnabled, + fetchPolicy: 'cache-first', + }) + // Full query result is not available at pageload, but is refetched when needed in UI + const fullQueryResult = useTokenBalancesQuery({ cacheOnly }) + const { data, loading } = fullQueryResult.data ? fullQueryResult : quickQueryResult + return useMemo(() => { const balanceList = data?.portfolios?.[0]?.tokenBalances ?? [] const balanceMap = diff --git a/apps/web/src/hooks/useTotalSupply.ts b/apps/web/src/hooks/useTotalSupply.ts index 6a3d4dc7ce2..e51b63de35b 100644 --- a/apps/web/src/hooks/useTotalSupply.ts +++ b/apps/web/src/hooks/useTotalSupply.ts @@ -12,6 +12,6 @@ export function useTotalSupply(token?: Currency): CurrencyAmount | undefi return useMemo( () => (token?.isToken && totalSupplyStr ? CurrencyAmount.fromRawAmount(token, totalSupplyStr) : undefined), - [token, totalSupplyStr] + [token, totalSupplyStr], ) } diff --git a/apps/web/src/hooks/useTransactionGasFee.ts b/apps/web/src/hooks/useTransactionGasFee.ts index 4e76c605c85..7bab1638618 100644 --- a/apps/web/src/hooks/useTransactionGasFee.ts +++ b/apps/web/src/hooks/useTransactionGasFee.ts @@ -70,7 +70,7 @@ export enum GasSpeed { export function useTransactionGasFee( tx?: TransactionRequest, speed: GasSpeed = GasSpeed.Urgent, - skip: boolean = !tx + skip: boolean = !tx, ): GasFeeResult { const gasFeeFetcher = useGasFeeQuery(tx, skip) const { data, isLoading } = useAsyncData(gasFeeFetcher) diff --git a/apps/web/src/hooks/useUSDPrice.ts b/apps/web/src/hooks/useUSDPrice.ts index f4683c12ba3..aa6adfd5e96 100644 --- a/apps/web/src/hooks/useUSDPrice.ts +++ b/apps/web/src/hooks/useUSDPrice.ts @@ -37,7 +37,7 @@ function useETHPrice(currency?: Currency): { TradeType.EXACT_OUTPUT, amountOut, currency, - INTERNAL_ROUTER_PREFERENCE_PRICE + INTERNAL_ROUTER_PREFERENCE_PRICE, ) return useMemo(() => { @@ -69,7 +69,7 @@ function useETHPrice(currency?: Currency): { export function useUSDPrice( currencyAmount?: CurrencyAmount, - prefetchCurrency?: Currency + prefetchCurrency?: Currency, ): { data?: number isLoading: boolean diff --git a/apps/web/src/hooks/useUSDTokenUpdater.ts b/apps/web/src/hooks/useUSDTokenUpdater.ts index 2ab4b553f98..95b95a72519 100644 --- a/apps/web/src/hooks/useUSDTokenUpdater.ts +++ b/apps/web/src/hooks/useUSDTokenUpdater.ts @@ -24,7 +24,7 @@ export function useUSDTokenUpdater(isFiatInput: boolean, exactAmount: string, ex const stablecoinAmount = supportedChainId ? tryParseCurrencyAmount( exactAmountUSD, - getChain({ chainId: supportedChainId }).spotPriceStablecoinAmount.currency + getChain({ chainId: supportedChainId }).spotPriceStablecoinAmount.currency, ) : undefined diff --git a/apps/web/src/hooks/useUniswapWalletOptions.ts b/apps/web/src/hooks/useUniswapWalletOptions.ts index 450a6cdafd3..8cfbe8f7049 100644 --- a/apps/web/src/hooks/useUniswapWalletOptions.ts +++ b/apps/web/src/hooks/useUniswapWalletOptions.ts @@ -4,10 +4,9 @@ import { FeatureFlags } from 'uniswap/src/features/gating/flags' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' export function useUniswapWalletOptions() { - const isBetaLive = useFeatureFlag(FeatureFlags.ExtensionBetaLaunch) - const isGALive = useFeatureFlag(FeatureFlags.ExtensionGeneralLaunch) + const isGALive = useFeatureFlag(FeatureFlags.ExtensionLaunch) const isExtensionInstalled = Boolean(useConnectorWithId(CONNECTION.UNISWAP_EXTENSION_RDNS)) - return (isBetaLive && isExtensionInstalled) || isGALive + return isExtensionInstalled || isGALive } diff --git a/apps/web/src/hooks/useUniswapXSwapCallback.ts b/apps/web/src/hooks/useUniswapXSwapCallback.ts index 462c7fa8723..4bd2b842f09 100644 --- a/apps/web/src/hooks/useUniswapXSwapCallback.ts +++ b/apps/web/src/hooks/useUniswapXSwapCallback.ts @@ -21,7 +21,12 @@ import { InterfaceEventNameLocal } from 'uniswap/src/features/telemetry/constant import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { logger } from 'utilities/src/logger/logger' import { useTrace } from 'utilities/src/telemetry/trace/TraceContext' -import { SignatureExpiredError, UniswapXv2HardQuoteError, UserRejectedRequestError } from 'utils/errors' +import { + SignatureExpiredError, + UniswapXv2HardQuoteError, + UserRejectedRequestError, + WrongChainError, +} from 'utils/errors' import { signTypedData } from 'utils/signing' import { didUserReject, swapErrorToUserReadableMessage } from 'utils/swapErrorToUserReadableMessage' import { getWalletMeta } from 'utils/walletMeta' @@ -94,6 +99,10 @@ export function useUniswapXSwapCallback({ if (!trade) { throw new Error('missing trade') } + const connectedChainId = await provider.getSigner().getChainId() + if (account.chainId !== connectedChainId) { + throw new WrongChainError() + } sendAnalyticsEvent(InterfaceEventNameLocal.UniswapXSignatureRequested, { ...formatSwapSignedAnalyticsEventProperties({ @@ -272,6 +281,7 @@ export function useUniswapXSwapCallback({ [ account.status, account.address, + account.chainId, provider, trade, allowedSlippage, @@ -279,6 +289,6 @@ export function useUniswapXSwapCallback({ portfolioBalanceUsd, analyticsContext, connectorName, - ] + ], ) } diff --git a/apps/web/src/hooks/useUniversalRouter.ts b/apps/web/src/hooks/useUniversalRouter.ts index 9dbb916b4af..c069a4e03e5 100644 --- a/apps/web/src/hooks/useUniversalRouter.ts +++ b/apps/web/src/hooks/useUniversalRouter.ts @@ -52,7 +52,7 @@ interface SwapOptions { export function useUniversalRouterSwapCallback( trade: ClassicTrade | undefined, fiatValues: { amountIn?: number; amountOut?: number; feeUsd?: number }, - options: SwapOptions + options: SwapOptions, ) { const account = useAccount() const provider = useEthersWeb3Provider() @@ -131,7 +131,7 @@ export function useUniversalRouterSwapCallback( throw error } } - } + }, ) sendAnalyticsEvent(SwapEventName.SWAP_SIGNED, { ...formatSwapSignedAnalyticsEventProperties({ @@ -192,6 +192,6 @@ export function useUniversalRouterSwapCallback( connectorName, blockNumber, isAutoSlippage, - ] + ], ) } diff --git a/apps/web/src/hooks/useUnmountingAnimation.ts b/apps/web/src/hooks/useUnmountingAnimation.ts index 22ff6b1cb52..325afb2b2ef 100644 --- a/apps/web/src/hooks/useUnmountingAnimation.ts +++ b/apps/web/src/hooks/useUnmountingAnimation.ts @@ -28,7 +28,7 @@ export function useUnmountingAnimation( node: RefObject, getAnimatingClass: () => string, animatedElements?: RefObject[], - skip = false + skip = false, ) { useEffect(() => { const current = node.current diff --git a/apps/web/src/hooks/useV2LiquidityTokenPermit.ts b/apps/web/src/hooks/useV2LiquidityTokenPermit.ts index 06871d53a98..c45eba83649 100644 --- a/apps/web/src/hooks/useV2LiquidityTokenPermit.ts +++ b/apps/web/src/hooks/useV2LiquidityTokenPermit.ts @@ -10,7 +10,7 @@ const REMOVE_V2_LIQUIDITY_PERMIT_INFO: PermitInfo = { export function useV2LiquidityTokenPermit( liquidityAmount: CurrencyAmount | null | undefined, - spender: string | null | undefined + spender: string | null | undefined, ) { const transactionDeadline = useTransactionDeadline() return useERC20Permit(liquidityAmount, spender, transactionDeadline, REMOVE_V2_LIQUIDITY_PERMIT_INFO) diff --git a/apps/web/src/hooks/useV2Pairs.ts b/apps/web/src/hooks/useV2Pairs.ts index e90e3dfb5f8..0e75b7bfe10 100644 --- a/apps/web/src/hooks/useV2Pairs.ts +++ b/apps/web/src/hooks/useV2Pairs.ts @@ -17,7 +17,7 @@ export enum PairState { export function useV2Pairs(currencies: [Currency | undefined, Currency | undefined][]): [PairState, Pair | null][] { const tokens = useMemo( () => currencies.map(([currencyA, currencyB]) => [currencyA?.wrapped, currencyB?.wrapped]), - [currencies] + [currencies], ) const pairAddresses = useMemo( @@ -31,7 +31,7 @@ export function useV2Pairs(currencies: [Currency | undefined, Currency | undefin ? computePairAddress({ factoryAddress: V2_FACTORY_ADDRESSES[tokenA.chainId], tokenA, tokenB }) : undefined }), - [tokens] + [tokens], ) const results = useMultipleContractSingleData(pairAddresses, PAIR_INTERFACE, 'getReserves') @@ -57,7 +57,7 @@ export function useV2Pairs(currencies: [Currency | undefined, Currency | undefin PairState.EXISTS, new Pair( CurrencyAmount.fromRawAmount(token0, reserve0.toString()), - CurrencyAmount.fromRawAmount(token1, reserve1.toString()) + CurrencyAmount.fromRawAmount(token1, reserve1.toString()), ), ] }) diff --git a/apps/web/src/hooks/useV3PositionFees.ts b/apps/web/src/hooks/useV3PositionFees.ts index 80117fc81a8..e6047e08eae 100644 --- a/apps/web/src/hooks/useV3PositionFees.ts +++ b/apps/web/src/hooks/useV3PositionFees.ts @@ -13,7 +13,7 @@ const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1) export function useV3PositionFees( pool?: Pool, tokenId?: BigNumber, - asWETH = false + asWETH = false, ): [CurrencyAmount, CurrencyAmount] | [undefined, undefined] { const positionManager = useV3NFTPositionManagerContract(false) const owner: string | undefined = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]) @@ -36,7 +36,7 @@ export function useV3PositionFees( amount0Max: MAX_UINT128, amount1Max: MAX_UINT128, }, - { from: owner } // need to simulate the call as the owner + { from: owner }, // need to simulate the call as the owner ) setAmounts([results.amount0, results.amount1]) } catch { diff --git a/apps/web/src/hooks/useWrapCallback.tsx b/apps/web/src/hooks/useWrapCallback.tsx index d97734e5d3d..f0c97a8c0a5 100644 --- a/apps/web/src/hooks/useWrapCallback.tsx +++ b/apps/web/src/hooks/useWrapCallback.tsx @@ -9,6 +9,7 @@ import { formatToDecimal, getTokenAddress } from 'lib/utils/analytics' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { useMemo, useState } from 'react' import { useCurrencyBalance } from 'state/connection/hooks' +import { useSwapAndLimitContext } from 'state/swap/hooks' import { useTransactionAdder } from 'state/transactions/hooks' import { TransactionType } from 'state/transactions/types' import { trace } from 'tracing/trace' @@ -27,7 +28,7 @@ enum WrapInputError { } export function WrapErrorText({ wrapInputError }: { wrapInputError: WrapInputError }) { - const { chainId } = useAccount() + const { chainId } = useSwapAndLimitContext() const native = useNativeCurrency(chainId) const wrapped = native?.wrapped @@ -55,15 +56,16 @@ export function WrapErrorText({ wrapInputError }: { wrapInputError: WrapInputErr export default function useWrapCallback( inputCurrency: Currency | undefined | null, outputCurrency: Currency | undefined | null, - typedValue: string | undefined + typedValue: string | undefined, ): { wrapType: WrapType; execute?: () => Promise; inputError?: WrapInputError } { const account = useAccount() - const wethContract = useWETHContract() + const { chainId } = useSwapAndLimitContext() + const wethContract = useWETHContract(true, chainId) const balance = useCurrencyBalance(account.address, inputCurrency ?? undefined) // we can always parse the amount typed as the input currency, since wrapping is 1:1 const inputAmount = useMemo( () => tryParseCurrencyAmount(typedValue, inputCurrency ?? undefined), - [inputCurrency, typedValue] + [inputCurrency, typedValue], ) const addTransaction = useTransactionAdder() @@ -75,10 +77,10 @@ export default function useWrapCallback( } return useMemo(() => { - if (!wethContract || !account.chainId || !inputCurrency || !outputCurrency) { + if (!wethContract || !chainId || !inputCurrency || !outputCurrency) { return NOT_APPLICABLE } - const weth = WRAPPED_NATIVE_CURRENCY[account.chainId] + const weth = WRAPPED_NATIVE_CURRENCY[chainId] if (!weth) { return NOT_APPLICABLE } @@ -104,7 +106,7 @@ export default function useWrapCallback( trace({ name: 'Wrap', op: 'swap.wrap' }, async (trace) => { const network = await wethContract.provider.getNetwork() if ( - network.chainId !== account.chainId || + network.chainId !== chainId || wethContract.address !== WRAPPED_NATIVE_CURRENCY[network.chainId]?.address ) { sendAnalyticsEvent(InterfaceEventName.WRAP_TOKEN_TXN_INVALIDATED, { @@ -120,13 +122,13 @@ Please file a bug detailing how this happened - https://github.com/Uniswap/inter throw error } const txReceipt = await trace.child({ name: 'Deposit', op: 'wallet.send_transaction' }, () => - wethContract.deposit({ value: `0x${inputAmount.quotient.toString(16)}` }) + wethContract.deposit({ value: `0x${inputAmount.quotient.toString(16)}` }), ) addTransaction(txReceipt, { type: TransactionType.WRAP, unwrapped: false, currencyAmountRaw: inputAmount?.quotient.toString(), - chainId: account.chainId, + chainId, }) sendAnalyticsEvent(InterfaceEventName.WRAP_TOKEN_TXN_SUBMITTED, { ...eventProperties, @@ -138,8 +140,8 @@ Please file a bug detailing how this happened - https://github.com/Uniswap/inter inputError: sufficientBalance ? undefined : hasInputAmount - ? WrapInputError.INSUFFICIENT_NATIVE_BALANCE - : WrapInputError.ENTER_NATIVE_AMOUNT, + ? WrapInputError.INSUFFICIENT_NATIVE_BALANCE + : WrapInputError.ENTER_NATIVE_AMOUNT, } } else if (weth.equals(inputCurrency) && outputCurrency.isNative) { return { @@ -150,13 +152,13 @@ Please file a bug detailing how this happened - https://github.com/Uniswap/inter trace({ name: 'Wrap', op: 'swap.wrap' }, async (trace) => { try { const txReceipt = await trace.child({ name: 'Withdraw', op: 'wallet.send_transaction' }, () => - wethContract.withdraw(`0x${inputAmount.quotient.toString(16)}`) + wethContract.withdraw(`0x${inputAmount.quotient.toString(16)}`), ) addTransaction(txReceipt, { type: TransactionType.WRAP, unwrapped: true, currencyAmountRaw: inputAmount?.quotient.toString(), - chainId: account.chainId, + chainId, }) sendAnalyticsEvent(InterfaceEventName.WRAP_TOKEN_TXN_SUBMITTED, { ...eventProperties, @@ -172,11 +174,11 @@ Please file a bug detailing how this happened - https://github.com/Uniswap/inter inputError: sufficientBalance ? undefined : hasInputAmount - ? WrapInputError.INSUFFICIENT_WRAPPED_BALANCE - : WrapInputError.ENTER_WRAPPED_AMOUNT, + ? WrapInputError.INSUFFICIENT_WRAPPED_BALANCE + : WrapInputError.ENTER_WRAPPED_AMOUNT, } } else { return NOT_APPLICABLE } - }, [wethContract, account.chainId, inputCurrency, outputCurrency, inputAmount, balance, addTransaction]) + }, [wethContract, chainId, inputCurrency, outputCurrency, inputAmount, balance, addTransaction]) } diff --git a/apps/web/src/i18n.tsx b/apps/web/src/i18n.tsx index 6c931a6ce5a..039c79e2bb9 100644 --- a/apps/web/src/i18n.tsx +++ b/apps/web/src/i18n.tsx @@ -22,7 +22,7 @@ i18n return enUsLocale } return import(`./i18n/locales/translations/${language}.json`) - }) + }), ) .on('failedLoading', (language, namespace, msg) => { logger.error(new Error(`Error loading language ${language} ${namespace}: ${msg}`), { diff --git a/apps/web/src/i18n/Plural.tsx b/apps/web/src/i18n/Plural.tsx index cff8d9bbef1..6e47ab93b06 100644 --- a/apps/web/src/i18n/Plural.tsx +++ b/apps/web/src/i18n/Plural.tsx @@ -1,8 +1,9 @@ import { Translation } from 'react-i18next' +import { isTestEnv } from 'utilities/src/environment' export function Plural({ value, one, other }: { value: number; one: string; other: string }) { const children = value === 1 ? one : other - if (process.env.NODE_ENV === 'test') { + if (isTestEnv()) { return <>{children} } // ensures it re-renders when language changes diff --git a/apps/web/src/i18n/locales/source/en-US.json b/apps/web/src/i18n/locales/source/en-US.json index ed7e6c87766..99a42edbba6 100644 --- a/apps/web/src/i18n/locales/source/en-US.json +++ b/apps/web/src/i18n/locales/source/en-US.json @@ -73,7 +73,6 @@ "common.amountDeposited.label": "{{amount}} Deposited", "common.amountInput.placeholder": "Input amount", "common.analytics": "Analytics", - "common.and": "and", "common.app": "App", "common.approval.cancelled": "Approval cancelled", "common.approval.failed": "Approval failed", @@ -188,6 +187,7 @@ "common.downloadUniswap": "Download Uniswap", "common.downloadUniswapApp": "Download the Uniswap app", "common.edit.button": "Edit", + "common.endAdornment": "and", "common.error.label": "Error", "common.error.request": "Sorry, an error occured while processing your request. If you request support, be sure to copy the details of this error.", "common.error.somethingWrong": "Something went wrong!", @@ -381,7 +381,7 @@ "common.revokedApproval": "Revoked Approval", "common.revoking.approval": "Revoking approval", "common.samePrice": "Same price", - "common.scanQRDownload": "Scan the QR code with your phone to download the Uniswap app", + "common.scanQRDownload": "Scan the QR code with your phone to download", "common.search.label": "Search", "common.searchResults": "Search results", "common.searchTokens": "Search tokens", @@ -588,10 +588,20 @@ "fee.tier": "Fee tier", "fee.tierExact": "{{fee}} fee tier", "fiatOnRamp.notAvailable.error": "Crypto purchases are not available in your region. ", + "fiatOnRamp.checkoutWith": "Checkout with", + "fiatOnRamp.chooseProvider.description": "You’ll continue to the provider’s portal to see the fees associated with your transaction.", + "fiatOnRamp.connection.message": "Connecting you to {{serviceProvider}}", + "fiatOnRamp.connection.quote": "Buying {{amount}} worth of {{currencySymbol}}", + "fiatOnRamp.disclaimer": "By continuing, you acknowledge that you’ll be subject to the Terms of Service and Privacy Policy with {{serviceProvider}}, as applicable.", + "fiatOnRamp.error.min": "Minimum {{amount}}", + "fiatOnRamp.error.max": "Maximum {{amount}}", + "fiatOnRamp.continueInTab": "Go to the {{serviceProvider}} tab to continue. It’s safe to close this modal now.", + "fiatOnRamp.completeTransactionHeader": "Complete transaction with {{serviceProvider}}", + "fiatOnRamp.quote.type.other": "Other options", "for {{address}}": "for {{address}}", "for": "for", - "hero.anytime": "anytime,", - "hero.anywhere": "anywhere.", + "globalPreferences.title": "Global preferences", + "hero.swap.title": "Swap anytime,
anywhere.", "hero.scroll": "Scroll to learn more", "hero.subtitle": "The largest onchain marketplace. Buy and sell crypto on Ethereum and 7+ other chains.", "landing.buildNextGen": "Build the next generation of open applications and tools.", @@ -667,6 +677,7 @@ "nft.blockedOpenSea": "Blocked on OpenSea", "nft.buyTransferNFTToStart": "Buy or transfer NFTs to this wallet to get started.", "nft.buyTransferTokensToStart": "Buy or transfer tokens to this wallet to get started.", + "nft.chainSupportComingSoon": "{{chainName}} support coming soon", "nft.collectionOnUni": "NFT collection on Uniswap", "nft.collections": "NFT collections", "nft.collectonOnAddress": "NFT collection on Uniswap - {{address}}", @@ -711,6 +722,7 @@ "nfts.my": "My NFTs", "nfts.noneYet": "No NFTs yet", "nfts.sell": "Sell NFTs", + "nfts.viewAndSell": "View and sell NFTs", "notFound.oops": "Oops, take me back to Swap", "notice.uk.label": "UK disclaimer:", "notice.uk": "This web application is provided as a tool for users to interact with the Uniswap Protocol on their own initiative, with no endorsement or recommendation of cryptocurrency trading activities. In doing so, Uniswap is not recommending that users or potential users engage in cryptoasset trading activity, and users or potential users of the web application should not regard this webpage or its contents as involving any form of recommendation, invitation or inducement to deal in cryptoassets.", @@ -899,8 +911,8 @@ "swap.fail.message": "Try adjusting slippage to a higher value.", "swap.fail.uniswapX": "Swap couldn’t be completed with UniswapX. Try your swap again to route it through the classic Uniswap API.", "swap.failed.label": "Your swap could not be executed. Please check your network connection and your slippage settings.", - "swap.fees.experience": "Fees are applied to ensure the best experience with Uniswap, and have already been factored into this quote.", - "swap.fees.noFee": "Fees are applied to ensure the best experience with Uniswap. There is no fee associated with this swap.", + "swap.fees.experience": "This fee is applied on select token pairs to ensure the best experience with Uniswap. It is paid in the output token and has already been factored into the quote.", + "swap.fees.noFee": "Fees are applied on select token pairs to ensure the best experience with Uniswap. There is no fee associated with this swap.", "swap.fetchingBestPrice": "Fetching best price...", "swap.fetchingPrice": "Fetching price...", "swap.finalizingQuote": "Finalizing quote...", @@ -917,7 +929,7 @@ "swap.limitSubmitted": "Limit submitted", "swap.marketPrice.outsideRange.label": "The market price is outside your specified price range. Single-asset deposit only.", "swap.maxPriceSlip.revert": "The maximum amount you are guaranteed to spend. If the price slips any further, your transaction will revert.", - "swap.minPriceSlip.revert": "The minimum amount you are guaranteed to receive. If the price slips any further, your transaction will revert.", + "swap.minPriceSlip.revert": "If the price moves so that you will receive less than {{ amount }}, your transaction will revert.", "swap.namedFee": "{{name}} fee", "swap.networkCost.paidIn": "Network cost is paid in {{sym}} on the {{chainName}} network in order to transact.", "swap.orderRouting": "Order routing", @@ -938,6 +950,7 @@ "swap.slippage.amt": "{{amt}} slippage", "swap.slippage.exactIn.revert": "If the price moves so that you will receive less than {{amount}}, your transaction will be reverted. This is the minimum amount you are guaranteed to receive.", "swap.slippage.exactOut.revert": "If the price moves so that you will pay more than {{amount}}, your transaction will be reverted. This is the maximum amount you are guaranteed to pay.", + "swap.slippage.tooltip": "The maximum price movement before your transaction will revert.", "swap.slippageBelow.warning": "Slippage below {{amt}} may result in a failed transaction", "swap.submitted": "Swap submitted", "swap.success": "Swap success!", diff --git a/apps/web/src/i18n/useTranslation.tsx b/apps/web/src/i18n/useTranslation.tsx index 27b6cb41a4c..5a8fdb77639 100644 --- a/apps/web/src/i18n/useTranslation.tsx +++ b/apps/web/src/i18n/useTranslation.tsx @@ -1,8 +1,9 @@ import i18n, { t } from 'i18next' import { useTranslation as useTranslationOG } from 'react-i18next' +import { isTestEnv } from 'utilities/src/environment' export function useTranslation() { - if (process.env.NODE_ENV === 'test') { + if (isTestEnv()) { return { i18n, t } } // eslint-disable-next-line react-hooks/rules-of-hooks diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx index 469c22ca9b4..a476222fe3e 100644 --- a/apps/web/src/index.tsx +++ b/apps/web/src/index.tsx @@ -1,11 +1,16 @@ -/* eslint-disable prettier/prettier */ // Ordering is intentional and must be preserved: styling, polyfilling, tracing, and then functionality. +// prettier-ignore import '@reach/dialog/styles.css' +// prettier-ignore import 'inter-ui' +// prettier-ignore import 'polyfills' +// prettier-ignore import 'tracing' -import 'i18n' // ensure translations load before things -/* eslint-enable prettier/prettier */ +// ensure translations load before things +// prettier-ignore +import 'i18n' +import 'setupRive' import { getDeviceId } from '@amplitude/analytics-browser' import { ApolloProvider } from '@apollo/client' @@ -37,7 +42,8 @@ import { SystemThemeUpdater, ThemeColorMetaUpdater } from 'theme/components/Them import { TamaguiProvider } from 'theme/tamaguiProvider' import { DUMMY_STATSIG_SDK_KEY } from 'uniswap/src/features/gating/constants' import { UnitagUpdaterContextProvider } from 'uniswap/src/features/unitags/context' -import { getEnvName, isBrowserRouterEnabled } from 'utils/env' +import { getEnvName } from 'utilities/src/environment' +import { isBrowserRouterEnabled } from 'utils/env' import { unregister as unregisterServiceWorker } from 'utils/serviceWorker' import { getCanonicalUrl } from 'utils/urlRoutes' @@ -81,7 +87,7 @@ function StatsigProvider({ children }: PropsWithChildren) { userID: getDeviceId(), customIDs: { address: account.address ?? '' }, }), - [account.address] + [account.address], ) return ( - + , ) // TODO(EXT-1229): We had to remove `React.StrictMode` because it's not diff --git a/apps/web/src/lib/hooks/routing/clientSideSmartOrderRouter.ts b/apps/web/src/lib/hooks/routing/clientSideSmartOrderRouter.ts index 24ffd7a5067..8187ca40eab 100644 --- a/apps/web/src/lib/hooks/routing/clientSideSmartOrderRouter.ts +++ b/apps/web/src/lib/hooks/routing/clientSideSmartOrderRouter.ts @@ -41,7 +41,7 @@ async function getQuote( amount: BigintIsh }, router: AlphaRouter, - routerConfig: Partial + routerConfig: Partial, ): Promise { const tokenInIsNative = Object.values(SwapRouterNativeAssets).includes(tokenIn.address as SwapRouterNativeAssets) const tokenOutIsNative = Object.values(SwapRouterNativeAssets).includes(tokenOut.address as SwapRouterNativeAssets) @@ -81,7 +81,7 @@ export async function getClientSideQuote( tradeType, }: GetQuoteArgs, router: AlphaRouter, - config: Partial + config: Partial, ) { return getQuote( { @@ -101,6 +101,6 @@ export async function getClientSideQuote( amount, }, router, - config + config, ) } diff --git a/apps/web/src/lib/hooks/routing/useRoutingAPIArguments.ts b/apps/web/src/lib/hooks/routing/useRoutingAPIArguments.ts index f6697deaaf8..90109e8e1e8 100644 --- a/apps/web/src/lib/hooks/routing/useRoutingAPIArguments.ts +++ b/apps/web/src/lib/hooks/routing/useRoutingAPIArguments.ts @@ -89,6 +89,6 @@ export function useRoutingAPIArguments({ priceImprovementBps, forceOpenOrders, deadlineBufferSecs, - ] + ], ) } diff --git a/apps/web/src/lib/hooks/useApproval.ts b/apps/web/src/lib/hooks/useApproval.ts index 3536686efeb..37c76468bb5 100644 --- a/apps/web/src/lib/hooks/useApproval.ts +++ b/apps/web/src/lib/hooks/useApproval.ts @@ -21,7 +21,7 @@ export enum ApprovalState { function useApprovalStateForSpender( amountToApprove: CurrencyAmount | undefined, spender: string | undefined, - useIsPendingApproval: (token?: Token, spender?: string) => boolean + useIsPendingApproval: (token?: Token, spender?: string) => boolean, ): ApprovalState { const account = useAccount() const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined @@ -53,13 +53,13 @@ function useApprovalStateForSpender( export function useApproval( amountToApprove: CurrencyAmount | undefined, spender: string | undefined, - useIsPendingApproval: (token?: Token, spender?: string) => boolean + useIsPendingApproval: (token?: Token, spender?: string) => boolean, ): [ ApprovalState, () => Promise< | { response: TransactionResponse; tokenAddress: string; spenderAddress: string; amount: CurrencyAmount } | undefined - > + >, ] { const { chainId } = useAccount() const token = amountToApprove?.currency?.isToken ? amountToApprove.currency : undefined diff --git a/apps/web/src/lib/hooks/useBlockNumber.tsx b/apps/web/src/lib/hooks/useBlockNumber.tsx index 906b518051c..6d78c4e6f97 100644 --- a/apps/web/src/lib/hooks/useBlockNumber.tsx +++ b/apps/web/src/lib/hooks/useBlockNumber.tsx @@ -102,7 +102,7 @@ export function BlockNumberProvider({ children }: PropsWithChildren) { block: activeBlock, mainnetBlock, }), - [activeBlock, account.chainId, mainnetBlock, onChainBlock] + [activeBlock, account.chainId, mainnetBlock, onChainBlock], ) return {children} } diff --git a/apps/web/src/lib/hooks/useCurrencyBalance.ts b/apps/web/src/lib/hooks/useCurrencyBalance.ts index 790bfa6af48..74288ad0b1e 100644 --- a/apps/web/src/lib/hooks/useCurrencyBalance.ts +++ b/apps/web/src/lib/hooks/useCurrencyBalance.ts @@ -30,7 +30,7 @@ function useNativeCurrencyBalances(uncheckedAddresses?: (string | undefined)[]): .sort() .map((addr) => [addr]) : [], - [uncheckedAddresses] + [uncheckedAddresses], ) const results = useSingleContractMultipleData(multicallContract, 'getEthBalance', validAddressInputs) @@ -44,7 +44,7 @@ function useNativeCurrencyBalances(uncheckedAddresses?: (string | undefined)[]): } return memo }, {}), - [validAddressInputs, chainId, results] + [validAddressInputs, chainId, results], ) } @@ -57,7 +57,7 @@ const tokenBalancesGasRequirement = { gasRequired: 185_000 } export function useRpcTokenBalancesWithLoadingIndicator( address?: string, tokens?: (Token | undefined)[], - skip?: boolean + skip?: boolean, ): [{ [tokenAddress: string]: CurrencyAmount | undefined }, boolean] { const { chainId } = useAccount() const validatedTokens: Token[] = useMemo( @@ -65,7 +65,7 @@ export function useRpcTokenBalancesWithLoadingIndicator( skip ? [] : tokens?.filter((t?: Token): t is Token => isAddress(t?.address) !== false && t?.chainId === chainId) ?? [], - [chainId, tokens, skip] + [chainId, tokens, skip], ) const validatedTokenAddresses = useMemo(() => validatedTokens.map((vt) => vt.address), [validatedTokens]) @@ -74,7 +74,7 @@ export function useRpcTokenBalancesWithLoadingIndicator( ERC20Interface, 'balanceOf', useMemo(() => [address], [address]), - tokenBalancesGasRequirement + tokenBalancesGasRequirement, ) const anyLoading: boolean = useMemo(() => balances.some((callState) => callState.loading), [balances]) @@ -93,24 +93,24 @@ export function useRpcTokenBalancesWithLoadingIndicator( : {}, anyLoading, ], - [address, validatedTokens, anyLoading, balances] + [address, validatedTokens, anyLoading, balances], ) } function useRpcTokenBalances( address?: string, - tokens?: (Token | undefined)[] + tokens?: (Token | undefined)[], ): { [tokenAddress: string]: CurrencyAmount | undefined } { return useRpcTokenBalancesWithLoadingIndicator(address, tokens)[0] } function useRpcCurrencyBalances( account?: string, - currencies?: (Currency | undefined)[] + currencies?: (Currency | undefined)[], ): (CurrencyAmount | undefined)[] { const tokens = useMemo( () => currencies?.filter((currency): currency is Token => currency?.isToken ?? false) ?? [], - [currencies] + [currencies], ) const { chainId } = useAccount() @@ -132,7 +132,7 @@ function useRpcCurrencyBalances( } return undefined }) ?? [], - [account, chainId, currencies, ethBalance, tokenBalances] + [account, chainId, currencies, ethBalance, tokenBalances], ) } @@ -143,7 +143,7 @@ function useRpcCurrencyBalances( */ function useGqlCurrencyBalances( account?: string, - currencies?: (Currency | undefined)[] + currencies?: (Currency | undefined)[], ): (CurrencyAmount | undefined)[] { const { balanceMap } = useTokenBalances({ cacheOnly: true }) @@ -173,14 +173,13 @@ function useGqlCurrencyBalances( /** * Returns balances for tokens on currently-connected chainId via RPC. * Falls back to graphql TokenBalances if user is not connected to chain, a.k.a !isSynced. - * If no chainId is provided we default to rpc balances. */ export function useCurrencyBalances( account?: string, currencies?: (Currency | undefined)[], - chainId?: number ): (CurrencyAmount | undefined)[] { const { chainId: providerChainId } = useAccount() + const chainId = useMemo(() => currencies?.[0]?.chainId, [currencies]) const isSynced = !chainId || chainId === providerChainId const gqlCurrencyBalances = useGqlCurrencyBalances(account, currencies) @@ -203,11 +202,9 @@ export function useTokenBalance(account?: string, token?: Token): CurrencyAmount export default function useCurrencyBalance( account?: string, currency?: Currency, - chainId?: number ): CurrencyAmount | undefined { return useCurrencyBalances( account, useMemo(() => [currency], [currency]), - chainId )[0] } diff --git a/apps/web/src/lib/hooks/useNativeCurrency.ts b/apps/web/src/lib/hooks/useNativeCurrency.ts index 494ba6e2fd6..7b5e576dc64 100644 --- a/apps/web/src/lib/hooks/useNativeCurrency.ts +++ b/apps/web/src/lib/hooks/useNativeCurrency.ts @@ -10,6 +10,6 @@ export default function useNativeCurrency(chainId: InterfaceChainId | null | und ? nativeOnChain(chainId) : // display mainnet when not connected nativeOnChain(UniverseChainId.Mainnet), - [chainId] + [chainId], ) } diff --git a/apps/web/src/lib/hooks/useTokenList/fetchTokenList.ts b/apps/web/src/lib/hooks/useTokenList/fetchTokenList.ts index 6edb00ee1e6..c00a750d069 100644 --- a/apps/web/src/lib/hooks/useTokenList/fetchTokenList.ts +++ b/apps/web/src/lib/hooks/useTokenList/fetchTokenList.ts @@ -15,7 +15,7 @@ const listCache = new Map() export default async function fetchTokenList( listUrl: string, resolveENSContentHash: (ensName: string) => Promise, - skipValidation?: boolean + skipValidation?: boolean, ): Promise { const cached = listCache?.get(listUrl) // avoid spurious re-fetches if (cached) { @@ -78,7 +78,7 @@ export default async function fetchTokenList( 'fetchTokenList', 'fetchTokenList', `failed to parse and validate list response: ${listUrl} (${url})`, - error + error, ) continue } diff --git a/apps/web/src/lib/hooks/useTokenList/sorting.ts b/apps/web/src/lib/hooks/useTokenList/sorting.ts index 45e43baa950..8b100aa308d 100644 --- a/apps/web/src/lib/hooks/useTokenList/sorting.ts +++ b/apps/web/src/lib/hooks/useTokenList/sorting.ts @@ -43,7 +43,7 @@ export function getSortedPortfolioTokens( portfolioTokenBalances: readonly (PortfolioTokenBalancePartsFragment | undefined)[] | undefined, balances: TokenBalances, chainId: InterfaceChainId | undefined, - splitOptions?: SplitOptions + splitOptions?: SplitOptions, ): Token[] { const validVisiblePortfolioTokens = splitHiddenTokens(portfolioTokenBalances ?? [], splitOptions) .visibleTokens.map((tokenBalance) => { @@ -66,7 +66,7 @@ export function getSortedPortfolioTokens( address, tokenBalance.token?.decimals, tokenBalance.token?.symbol, - tokenBalance.token?.project?.name ?? tokenBalance.token?.name + tokenBalance.token?.project?.name ?? tokenBalance.token?.name, ) return portfolioToken diff --git a/apps/web/src/lib/state/multicall.tsx b/apps/web/src/lib/state/multicall.tsx index 02980a23304..39bf756ca87 100644 --- a/apps/web/src/lib/state/multicall.tsx +++ b/apps/web/src/lib/state/multicall.tsx @@ -20,7 +20,7 @@ export function MulticallUpdater() { const contract = useInterfaceMulticall() const listenerOptions: ListenerOptions = useMemo( () => ({ blocksPerFetch: supportedChain ? UNIVERSE_CHAIN_INFO[supportedChain].blockPerMainnetEpochForChainId : 1 }), - [supportedChain] + [supportedChain], ) const latestMainnetBlockNumber = useMainnetBlockNumber() diff --git a/apps/web/src/lib/utils/analytics.ts b/apps/web/src/lib/utils/analytics.ts index 8c095ba404c..95aee9d0d3c 100644 --- a/apps/web/src/lib/utils/analytics.ts +++ b/apps/web/src/lib/utils/analytics.ts @@ -13,7 +13,7 @@ export const getDurationUntilTimestampSeconds = (futureTimestampInSecondsSinceEp export const formatToDecimal = ( intialNumberObject: Percent | CurrencyAmount, - decimalPlace: number + decimalPlace: number, ): number => parseFloat(intialNumberObject.toFixed(decimalPlace)) export const getTokenAddress = (currency: Currency) => (currency.isNative ? NATIVE_CHAIN_ID : currency.address) @@ -24,7 +24,7 @@ export const formatPercentNumber = (percent: Percent): number => parseFloat(perc export const getPriceUpdateBasisPoints = ( prevPrice: Price, - newPrice: Price + newPrice: Price, ): number => { const changeFraction = newPrice.subtract(prevPrice).divide(prevPrice) const changePercentage = new Percent(changeFraction.numerator, changeFraction.denominator) @@ -44,7 +44,7 @@ function getEstimatedNetworkFee(trade: InterfaceTrade) { export function formatCommonPropertiesForTrade( trade: InterfaceTrade, allowedSlippage: Percent, - outputFeeFiatValue?: number + outputFeeFiatValue?: number, ) { return { routing: trade.fillType, @@ -112,7 +112,7 @@ export const formatSwapQuoteReceivedEventProperties = ( trade: InterfaceTrade, allowedSlippage: Percent, swapQuoteLatencyMs: number | undefined, - outputFeeFiatValue: number | undefined + outputFeeFiatValue: number | undefined, ) => { return { ...formatCommonPropertiesForTrade(trade, allowedSlippage, outputFeeFiatValue), diff --git a/apps/web/src/lib/utils/contenthashToUri.test.skip.ts b/apps/web/src/lib/utils/contenthashToUri.test.skip.ts index 10643c62938..3c2c20d2803 100644 --- a/apps/web/src/lib/utils/contenthashToUri.test.skip.ts +++ b/apps/web/src/lib/utils/contenthashToUri.test.skip.ts @@ -6,7 +6,7 @@ import contenthashToUri, { hexToUint8Array } from 'lib/utils/contenthashToUri' describe('#contenthashToUri', () => { it('1inch.tokens.eth contenthash', () => { expect(contenthashToUri('0xe3010170122013e051d1cfff20606de36845d4fe28deb9861a319a5bc8596fa4e610e8803918')).toEqual( - 'ipfs://QmPgEqyV3m8SB52BS2j2mJpu9zGprhj2BGCHtRiiw2fdM1' + 'ipfs://QmPgEqyV3m8SB52BS2j2mJpu9zGprhj2BGCHtRiiw2fdM1', ) }) it('uniswap.eth contenthash', () => { diff --git a/apps/web/src/lib/utils/searchBar.ts b/apps/web/src/lib/utils/searchBar.ts index 702e181c5a1..0a8425f67a8 100644 --- a/apps/web/src/lib/utils/searchBar.ts +++ b/apps/web/src/lib/utils/searchBar.ts @@ -12,7 +12,7 @@ import { GenieCollection } from 'nft/types' export function organizeSearchResults( isNFTPage: boolean, tokenResults: SearchToken[], - collectionResults: GenieCollection[] + collectionResults: GenieCollection[], ): [SearchToken[], GenieCollection[]] { const reducedTokens = tokenResults?.slice(0, isNFTPage ? 3 : collectionResults.length < 3 ? 8 - collectionResults.length : 5) ?? [] diff --git a/apps/web/src/lib/utils/tryParseCurrencyAmount.ts b/apps/web/src/lib/utils/tryParseCurrencyAmount.ts index 12d5a3a93fd..1c457548db4 100644 --- a/apps/web/src/lib/utils/tryParseCurrencyAmount.ts +++ b/apps/web/src/lib/utils/tryParseCurrencyAmount.ts @@ -18,7 +18,7 @@ function truncateValue(value: string, decimals: number): string { */ export default function tryParseCurrencyAmount( value?: string, - currency?: T + currency?: T, ): CurrencyAmount | undefined { if (!value || !currency || isNaN(parseFloat(value))) { return undefined diff --git a/apps/web/src/nft/components/bag/Bag.tsx b/apps/web/src/nft/components/bag/Bag.tsx index a3dad0e41ef..e6b686f06bd 100644 --- a/apps/web/src/nft/components/bag/Bag.tsx +++ b/apps/web/src/nft/components/bag/Bag.tsx @@ -136,14 +136,14 @@ const Bag = () => { const isSellingAssets = sellAssets.length > 0 const shouldRenderEmptyState = Boolean( - (!isProfilePage && !isBuyingAssets && bagStatus === BagStatus.ADDING_TO_BAG) || (isProfilePage && !isSellingAssets) + (!isProfilePage && !isBuyingAssets && bagStatus === BagStatus.ADDING_TO_BAG) || (isProfilePage && !isSellingAssets), ) const eventProperties = useMemo( () => ({ ...formatAssetEventProperties(itemsInBag.map((item) => item.asset)), }), - [itemsInBag] + [itemsInBag], ) if (!bagExpanded || !isNFTPage) { diff --git a/apps/web/src/nft/components/bag/BagFooter.test.tsx b/apps/web/src/nft/components/bag/BagFooter.test.tsx index 7c902942ba7..0b287bacd1f 100644 --- a/apps/web/src/nft/components/bag/BagFooter.test.tsx +++ b/apps/web/src/nft/components/bag/BagFooter.test.tsx @@ -60,7 +60,7 @@ describe('BagFooter.tsx', () => { }) as ReturnType mocked(useBagTotalEthPrice).mockReturnValue(BigNumber.from(12)) mocked(useCurrencyBalance).mockReturnValue( - CurrencyAmount.fromRawAmount(nativeOnChain(UniverseChainId.Mainnet), 100) + CurrencyAmount.fromRawAmount(nativeOnChain(UniverseChainId.Mainnet), 100), ) mocked(usePermit2Allowance).mockReturnValue({ diff --git a/apps/web/src/nft/components/bag/BagFooter.tsx b/apps/web/src/nft/components/bag/BagFooter.tsx index d8eebd7e87b..2b612f8918e 100644 --- a/apps/web/src/nft/components/bag/BagFooter.tsx +++ b/apps/web/src/nft/components/bag/BagFooter.tsx @@ -288,7 +288,7 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) = const defaultCurrency = useCurrency('ETH') const inputCurrencyBalance = useTokenBalance( account.address, - !!inputCurrency && inputCurrency.isToken ? inputCurrency : undefined + !!inputCurrency && inputCurrency.isToken ? inputCurrency : undefined, ) const { isLocked: bagIsLocked, @@ -322,7 +322,7 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) = const allowance = usePermit2Allowance( maximumAmountIn, getURAddress(isSupportedChain ? account.chainId : undefined, universalRouterAddress), - TradeFillType.Classic + TradeFillType.Classic, ) const loadingAllowance = allowance.state === AllowanceState.LOADING || universalRouterAddressIsLoading usePayWithAnyTokenSwap(trade, allowance, allowedSlippage) @@ -451,7 +451,7 @@ export const BagFooter = ({ setModalIsOpen, eventProperties }: BagFooterProps) = theme, fetchAssets, usingPayWithAnyToken, - priceImpact + priceImpact, ) } diff --git a/apps/web/src/nft/components/bag/BagRow.tsx b/apps/web/src/nft/components/bag/BagRow.tsx index c53ca26820d..08683c30b64 100644 --- a/apps/web/src/nft/components/bag/BagRow.tsx +++ b/apps/web/src/nft/components/bag/BagRow.tsx @@ -112,7 +112,7 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is e.stopPropagation() removeAsset([asset]) }, - [asset, removeAsset] + [asset, removeAsset], ) return ( @@ -185,7 +185,7 @@ export const PriceChangeBagRow = ({ asset, usdPrice, markAssetAsReviewed, top, i const toKeep = false markAssetAsReviewed(asset, toKeep) }, - [asset, markAssetAsReviewed] + [asset, markAssetAsReviewed], ) const handleKeep = useCallback( (e: MouseEvent) => { @@ -194,7 +194,7 @@ export const PriceChangeBagRow = ({ asset, usdPrice, markAssetAsReviewed, top, i const toKeep = true markAssetAsReviewed(asset, toKeep) }, - [asset, markAssetAsReviewed] + [asset, markAssetAsReviewed], ) return ( diff --git a/apps/web/src/nft/components/bag/ButtonStates.tsx b/apps/web/src/nft/components/bag/ButtonStates.tsx index bec721d5ba0..f0082ee1440 100644 --- a/apps/web/src/nft/components/bag/ButtonStates.tsx +++ b/apps/web/src/nft/components/bag/ButtonStates.tsx @@ -39,7 +39,7 @@ export function getBuyButtonStateData( theme: DefaultTheme, handleClickOverride?: (() => void) | (() => Promise), usingPayWithAnyToken?: boolean, - priceImpact?: PriceImpact + priceImpact?: PriceImpact, ): BuyButtonStateData { const defaultBuyButtonState: BuyButtonStateData = { handleClick: () => undefined, diff --git a/apps/web/src/nft/components/bag/MobileHoverBag.tsx b/apps/web/src/nft/components/bag/MobileHoverBag.tsx index 01627b67b61..56ac9e1c5ef 100644 --- a/apps/web/src/nft/components/bag/MobileHoverBag.tsx +++ b/apps/web/src/nft/components/bag/MobileHoverBag.tsx @@ -37,8 +37,8 @@ export const MobileHoverBag = () => { index === 0 ? 'translate(-50%, -50%) rotate(-4.42deg)' : index === 1 - ? 'translate(-50%, -50%) rotate(-14.01deg)' - : 'translate(-50%, -50%) rotate(10.24deg)', + ? 'translate(-50%, -50%) rotate(-14.01deg)' + : 'translate(-50%, -50%) rotate(10.24deg)', zIndex: index, }} /> diff --git a/apps/web/src/nft/components/card/index.tsx b/apps/web/src/nft/components/card/index.tsx index 14ff0f68821..f3e97a3a5f9 100644 --- a/apps/web/src/nft/components/card/index.tsx +++ b/apps/web/src/nft/components/card/index.tsx @@ -113,7 +113,7 @@ export const NftCard = ({ uniformAspectRatio, setUniformAspectRatio, renderedHeight, - setRenderedHeight + setRenderedHeight, )} {!hideDetails && ( diff --git a/apps/web/src/nft/components/card/utils.tsx b/apps/web/src/nft/components/card/utils.tsx index 71bb137ea56..6b39286f3e5 100644 --- a/apps/web/src/nft/components/card/utils.tsx +++ b/apps/web/src/nft/components/card/utils.tsx @@ -55,7 +55,7 @@ export function getNftDisplayComponent( uniformAspectRatio?: UniformAspectRatio, setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void, renderedHeight?: number, - setRenderedHeight?: (renderedHeight: number | undefined) => void + setRenderedHeight?: (renderedHeight: number | undefined) => void, ) { switch (getAssetMediaType(asset)) { case AssetMediaType.Image: @@ -129,7 +129,7 @@ export function useSelectAsset({ return isSelected ? unselectAsset?.() : selectAsset?.() }, - [selectAsset, isDisabled, onClick, unselectAsset, isSelected] + [selectAsset, isDisabled, onClick, unselectAsset, isSelected], ) } @@ -159,7 +159,7 @@ export const handleUniformAspectRatio = ( e: React.SyntheticEvent, setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void, renderedHeight?: number, - setRenderedHeight?: (renderedHeight: number | undefined) => void + setRenderedHeight?: (renderedHeight: number | undefined) => void, ) => { if (uniformAspectRatio !== UniformAspectRatios.square && setUniformAspectRatio) { const height = e.currentTarget.clientHeight @@ -187,7 +187,7 @@ export const handleUniformAspectRatio = ( export function getHeightFromAspectRatio( uniformAspectRatio: UniformAspectRatio, - renderedHeight?: number + renderedHeight?: number, ): number | undefined { return uniformAspectRatio === UniformAspectRatios.square || uniformAspectRatio === UniformAspectRatios.unset ? undefined @@ -196,7 +196,7 @@ export function getHeightFromAspectRatio( export function getMediaAspectRatio( uniformAspectRatio?: UniformAspectRatio, - setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void + setUniformAspectRatio?: (uniformAspectRatio: UniformAspectRatio) => void, ): string { return uniformAspectRatio === UniformAspectRatios.square || !setUniformAspectRatio ? '1' : 'auto' } diff --git a/apps/web/src/nft/components/collection/Activity.tsx b/apps/web/src/nft/components/collection/Activity.tsx index 28740d71c80..27b0f8069b0 100644 --- a/apps/web/src/nft/components/collection/Activity.tsx +++ b/apps/web/src/nft/components/collection/Activity.tsx @@ -75,7 +75,7 @@ export const Activity = ({ contractAddress, rarityVerified, collectionName, chai .filter((key) => activeFilters[key]), address: contractAddress, }, - 25 + 25, ) const isLoadingMore = hasNextActivity && nftActivity?.length @@ -97,7 +97,7 @@ export const Activity = ({ contractAddress, rarityVerified, collectionName, chai ) }, - [activeFilters] + [activeFilters], ) return ( @@ -161,7 +161,7 @@ export const Activity = ({ contractAddress, rarityVerified, collectionName, chai ethPriceInUSD={ethPriceInUSD} /> - ) + ), )} diff --git a/apps/web/src/nft/components/collection/ActivityCells.test.tsx b/apps/web/src/nft/components/collection/ActivityCells.test.tsx index bf00e071870..c57b6c59410 100644 --- a/apps/web/src/nft/components/collection/ActivityCells.test.tsx +++ b/apps/web/src/nft/components/collection/ActivityCells.test.tsx @@ -16,7 +16,7 @@ describe('BuyCell', () => { toggleCart={() => undefined} isMobile={false} ethPriceInUSD={0} - /> + />, ) expect(await screen.findByText(/Add to Bag/i)).toBeInTheDocument() }) diff --git a/apps/web/src/nft/components/collection/ActivityCells.tsx b/apps/web/src/nft/components/collection/ActivityCells.tsx index 3e2cff29f29..ab9ea411a35 100644 --- a/apps/web/src/nft/components/collection/ActivityCells.tsx +++ b/apps/web/src/nft/components/collection/ActivityCells.tsx @@ -116,7 +116,7 @@ export const BuyCell = ({ }: BuyCellProps) => { const asset = useMemo( () => buildActivityAsset(event, collectionName, ethPriceInUSD), - [event, collectionName, ethPriceInUSD] + [event, collectionName, ethPriceInUSD], ) const isSelected = useMemo(() => { return itemsInBag.some((item) => asset.tokenId === item.asset.tokenId && asset.address === item.asset.address) @@ -193,7 +193,7 @@ export const PriceCell = ({ marketplace, price }: { marketplace?: Markets | stri const { formatNumberOrString } = useFormatter() const formattedPrice = useMemo( () => (price ? formatNumberOrString({ input: parseFloat(price?.toString()), type: NumberType.NFTToken }) : null), - [formatNumberOrString, price] + [formatNumberOrString, price], ) return ( @@ -269,7 +269,7 @@ export const EventCell = ({ const { formatNumberOrString } = useFormatter() const formattedPrice = useMemo( () => (price ? formatNumberOrString({ input: parseFloat(price?.toString()), type: NumberType.NFTToken }) : null), - [formatNumberOrString, price] + [formatNumberOrString, price], ) return ( diff --git a/apps/web/src/nft/components/collection/CollectionAsset.test.tsx b/apps/web/src/nft/components/collection/CollectionAsset.test.tsx index 0e67d7c1407..b8a87802463 100644 --- a/apps/web/src/nft/components/collection/CollectionAsset.test.tsx +++ b/apps/web/src/nft/components/collection/CollectionAsset.test.tsx @@ -16,7 +16,7 @@ describe('NftCard', () => { uniformAspectRatio={UniformAspectRatios.square} setUniformAspectRatio={() => undefined} setRenderedHeight={() => undefined} - /> + />, ) expect(asFragment()).toMatchSnapshot() }) diff --git a/apps/web/src/nft/components/collection/CollectionAsset.tsx b/apps/web/src/nft/components/collection/CollectionAsset.tsx index 5a0935c5ef6..6920aff4560 100644 --- a/apps/web/src/nft/components/collection/CollectionAsset.tsx +++ b/apps/web/src/nft/components/collection/CollectionAsset.tsx @@ -43,7 +43,7 @@ export const CollectionAsset = ({ const { isSelected } = useMemo(() => { const matchingItems = itemsInBag.filter( - (item) => asset.tokenId === item.asset.tokenId && asset.address === item.asset.address + (item) => asset.tokenId === item.asset.tokenId && asset.address === item.asset.address, ) const isSelected = matchingItems.length > 0 diff --git a/apps/web/src/nft/components/collection/CollectionNfts.tsx b/apps/web/src/nft/components/collection/CollectionNfts.tsx index 7f11ac4fcd6..70437efe3cb 100644 --- a/apps/web/src/nft/components/collection/CollectionNfts.tsx +++ b/apps/web/src/nft/components/collection/CollectionNfts.tsx @@ -285,7 +285,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie const getPoolPosition = useCallback( (asset: GenieAsset) => { const assetInBag = itemsInBag.some( - (item) => asset.tokenId === item.asset.tokenId && asset.address === item.asset.address + (item) => asset.tokenId === item.asset.tokenId && asset.address === item.asset.address, ) if (asset.marketplace === Markets.Sudoswap) { @@ -303,14 +303,14 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie .findIndex((item) => item.asset.tokenId === asset.tokenId) : itemsInBag.filter((item) => isInSameMarketplaceCollection(asset, item.asset)).length }, - [itemsInBag] + [itemsInBag], ) const calculatePrice = useCallback( (asset: GenieAsset) => { return calcPoolPrice(asset, getPoolPosition(asset)) }, - [getPoolPosition] + [getPoolPosition], ) const collectionAssets = useMemo(() => { @@ -324,7 +324,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie (asset) => asset.marketplace && isPooledMarket(asset.marketplace) && - (asset.priceInfo.ETHPrice = calculatePrice(asset) ?? '0') + (asset.priceInfo.ETHPrice = calculatePrice(asset) ?? '0'), ) if (sortBy === SortBy.HighToLow || sortBy === SortBy.LowToHigh) { @@ -371,7 +371,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie const sortDropDownOptions: DropDownOption[] = useMemo( () => getSortDropdownOptions(setSortBy, hasRarity), - [hasRarity, setSortBy] + [hasRarity, setSortBy], ) useEffect(() => { diff --git a/apps/web/src/nft/components/collection/Filters.tsx b/apps/web/src/nft/components/collection/Filters.tsx index 7e0d76cd8b4..f9442e77172 100644 --- a/apps/web/src/nft/components/collection/Filters.tsx +++ b/apps/web/src/nft/components/collection/Filters.tsx @@ -30,7 +30,7 @@ export const Filters = ({ traitsByGroup }: { traitsByGroup: Record getSortDropdownOptions(setSortBy, hasRarity ?? false), - [hasRarity, setSortBy] + [hasRarity, setSortBy], ) return ( diff --git a/apps/web/src/nft/components/collection/MarketplaceSelect.tsx b/apps/web/src/nft/components/collection/MarketplaceSelect.tsx index d9c0f8e4cd4..13f8a01e344 100644 --- a/apps/web/src/nft/components/collection/MarketplaceSelect.tsx +++ b/apps/web/src/nft/components/collection/MarketplaceSelect.tsx @@ -192,7 +192,7 @@ export const MarketplaceSelect = () => { {...{ addMarket, removeMarket, isMarketSelected: selectedMarkets.includes(value) }} /> )), - [addMarket, marketCount, removeMarket, selectedMarkets] + [addMarket, marketCount, removeMarket, selectedMarkets], ) const onClick: React.MouseEventHandler = (e) => { diff --git a/apps/web/src/nft/components/collection/Sweep.tsx b/apps/web/src/nft/components/collection/Sweep.tsx index cb8614de991..72277a4df12 100644 --- a/apps/web/src/nft/components/collection/Sweep.tsx +++ b/apps/web/src/nft/components/collection/Sweep.tsx @@ -21,7 +21,8 @@ const SweepContainer = styled.div` border-radius: 12px; background-color: ${({ theme }) => theme.surface1}; justify-content: space-between; - background: linear-gradient(${({ theme }) => theme.surface1}, ${({ theme }) => theme.surface1}) padding-box, + background: + linear-gradient(${({ theme }) => theme.surface1}, ${({ theme }) => theme.surface1}) padding-box, linear-gradient(to right, #4673fa, #9646fa) border-box; border: 2px solid transparent; ` @@ -125,8 +126,8 @@ const NftHolder = styled.div<{ index: number; src?: string }>` index === 0 ? 'translate(-50%, -50%) rotate(-4.42deg)' : index === 1 - ? 'translate(-50%, -50%) rotate(-14.01deg)' - : 'translate(-50%, -50%) rotate(10.24deg)'}; + ? 'translate(-50%, -50%) rotate(-14.01deg)' + : 'translate(-50%, -50%) rotate(10.24deg)'}; z-index: ${({ index }) => 3 - index}; ` @@ -206,7 +207,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => { } const sudoSwapAssetsInJointCollections = jointCollections.filter( - (sweepAsset) => sweepAsset.marketplace === Markets.Sudoswap && !sweepAsset.susFlag + (sweepAsset) => sweepAsset.marketplace === Markets.Sudoswap && !sweepAsset.susFlag, ) jointCollections.forEach((asset) => { @@ -216,7 +217,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => { asset, sudoSwapAssetsInJointCollections .filter((sweepAsset) => isInSameSudoSwapPool(asset, sweepAsset)) - .findIndex((sweepAsset) => sweepAsset.tokenId === asset.tokenId) + .findIndex((sweepAsset) => sweepAsset.tokenId === asset.tokenId), ) asset.priceInfo.ETHPrice = poolPrice ?? '0' } else { @@ -237,7 +238,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => { }) let validAssets = jointCollections.filter( - (asset) => BigNumber.from(asset.priceInfo.ETHPrice).gt(0) && !asset.susFlag + (asset) => BigNumber.from(asset.priceInfo.ETHPrice).gt(0) && !asset.susFlag, ) validAssets = validAssets.slice( @@ -246,15 +247,15 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => { collectionAssets?.length ?? 0, sudoSwapAssets?.length ?? 0, nftxAssets?.length ?? 0, - nft20Assets?.length ?? 0 - ) + nft20Assets?.length ?? 0, + ), ) return { sortedAssets: validAssets, sortedAssetsTotalEth: validAssets.reduce( (total, asset) => total.add(BigNumber.from(asset.priceInfo.ETHPrice)), - BigNumber.from(0) + BigNumber.from(0), ), } }, [collectionAssets, sudoSwapAssets, nftxAssets, nft20Assets]) @@ -266,7 +267,7 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => { const sweepEthPrice = sweepItemsInBag.reduce( (total, asset) => total.add(BigNumber.from(asset.priceInfo.ETHPrice)), - BigNumber.from(0) + BigNumber.from(0), ) return { sweepItemsInBag, sweepEthPrice } @@ -459,7 +460,7 @@ function useSweepFetcherParams( contractAddress: string, market: Markets.Sudoswap | Markets.NFTX | Markets.NFT20 | 'others', minPrice: string, - maxPrice: string + maxPrice: string, ): SweepFetcherParams { const traits = useCollectionFilters((state) => state.traits) const markets = useCollectionFilters((state) => state.markets) diff --git a/apps/web/src/nft/components/collection/TraitSelect.tsx b/apps/web/src/nft/components/collection/TraitSelect.tsx index 2c307369896..eb8c0f3ca22 100644 --- a/apps/web/src/nft/components/collection/TraitSelect.tsx +++ b/apps/web/src/nft/components/collection/TraitSelect.tsx @@ -126,7 +126,7 @@ export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: st const searchedTraits = useMemo( () => traits.filter((t) => t.trait_value?.toString().toLowerCase().includes(debouncedSearch.toLowerCase())), - [debouncedSearch, traits] + [debouncedSearch, traits], ) const Row = useCallback( @@ -135,11 +135,11 @@ export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: st const isTraitSelected = selectedTraits.find( ({ trait_type, trait_value }) => - trait_type === trait.trait_type && String(trait_value) === String(trait.trait_value) + trait_type === trait.trait_type && String(trait_value) === String(trait.trait_value), ) return }, - [selectedTraits, addTrait, removeTrait] + [selectedTraits, addTrait, removeTrait], ) const itemKey = useCallback((index: number, data: Trait[]) => { @@ -161,7 +161,9 @@ export const TraitSelect = ({ traits, type, index }: { traits: Trait[]; type: st /> {({ height }: { height: number }) => ( diff --git a/apps/web/src/nft/components/collection/TransactionCompleteModal.tsx b/apps/web/src/nft/components/collection/TransactionCompleteModal.tsx index 82fc384ec71..eb9383fa402 100644 --- a/apps/web/src/nft/components/collection/TransactionCompleteModal.tsx +++ b/apps/web/src/nft/components/collection/TransactionCompleteModal.tsx @@ -88,7 +88,7 @@ const TxCompleteModal = () => { 'newwindow', `left=${(window.screen.width - TWITTER_WIDTH) / 2}, top=${ (window.screen.height - TWITTER_HEIGHT) / 2 - }, width=${TWITTER_WIDTH}, height=${TWITTER_HEIGHT}` + }, width=${TWITTER_WIDTH}, height=${TWITTER_HEIGHT}`, ) } @@ -135,7 +135,7 @@ const TxCompleteModal = () => { 1 && styles.successAssetImageGrid + nftsPurchased.length > 1 && styles.successAssetImageGrid, )} style={{ maxHeight: `${getSuccessfulImageSize(nftsPurchased.length, isMobile)}px`, diff --git a/apps/web/src/nft/components/collection/UnavailableCollectionPage.test.tsx b/apps/web/src/nft/components/collection/UnavailableCollectionPage.test.tsx index 81bac0a2e3f..e5e4db09d77 100644 --- a/apps/web/src/nft/components/collection/UnavailableCollectionPage.test.tsx +++ b/apps/web/src/nft/components/collection/UnavailableCollectionPage.test.tsx @@ -24,7 +24,7 @@ describe('Blocked Collection', () => { render() expect(screen.getByText('Learn why')).toHaveAttribute( 'href', - 'https://support.uniswap.org/hc/en-us/articles/18783694078989-Unsupported-Token-Policy' + 'https://support.uniswap.org/hc/en-us/articles/18783694078989-Unsupported-Token-Policy', ) expect(screen.getByText('Return to NFT Explore')).toHaveAttribute('href', '/nfts') }) diff --git a/apps/web/src/nft/components/common/SortDropdown/index.tsx b/apps/web/src/nft/components/common/SortDropdown/index.tsx index 8a9db0b5073..71ecbef5a6d 100644 --- a/apps/web/src/nft/components/common/SortDropdown/index.tsx +++ b/apps/web/src/nft/components/common/SortDropdown/index.tsx @@ -46,7 +46,7 @@ export const SortDropdown = ({ const reversable = useMemo( () => dropDownOptions[selectedIndex].reverseOnClick || dropDownOptions[selectedIndex].reverseIndex, - [selectedIndex, dropDownOptions] + [selectedIndex, dropDownOptions], ) const width = isCollectionStatsLoading ? 220 : inFilters ? 'full' : mini ? 'min' : maxWidth ? maxWidth : '300px' diff --git a/apps/web/src/nft/components/details/AssetDetails.tsx b/apps/web/src/nft/components/details/AssetDetails.tsx index 540977867f6..ede1ac3b056 100644 --- a/apps/web/src/nft/components/details/AssetDetails.tsx +++ b/apps/web/src/nft/components/details/AssetDetails.tsx @@ -249,11 +249,11 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => { asset.rarity ? { rarityProvider: asset?.rarity?.providers?.find( - ({ provider: _provider }) => _provider === asset.rarity?.primaryProvider + ({ provider: _provider }) => _provider === asset.rarity?.primaryProvider, ), } : {}, - [asset.rarity] + [asset.rarity], ) const assetMediaType = useMemo(() => { @@ -276,7 +276,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => { tokenId: token_id, }, 1, - 'no-cache' + 'no-cache', ) const weiPrice = gqlPriceData?.[0]?.price @@ -300,7 +300,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => { ) }, - [activeFilters] + [activeFilters], ) const { @@ -317,7 +317,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => { address: contractAddress, tokenId: token_id, }, - 25 + 25, ) const rarity = asset?.rarity?.providers?.[0] diff --git a/apps/web/src/nft/components/details/AssetPriceDetails.tsx b/apps/web/src/nft/components/details/AssetPriceDetails.tsx index 982cbba8e13..82f71fe2daf 100644 --- a/apps/web/src/nft/components/details/AssetPriceDetails.tsx +++ b/apps/web/src/nft/components/details/AssetPriceDetails.tsx @@ -217,7 +217,7 @@ const OwnerContainer = ({ asset }: { asset: WalletAsset }) => { const USDPrice = useMemo( () => (ethUsdPrice && asset.floor_sell_order_price ? ethUsdPrice * asset.floor_sell_order_price : undefined), - [ethUsdPrice, asset.floor_sell_order_price] + [ethUsdPrice, asset.floor_sell_order_price], ) const trace = useTrace() @@ -327,13 +327,13 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps) const USDPrice = useUsdPriceofNftAsset(asset) const assetsFilter = [{ address: asset.address, tokenId: asset.tokenId }] - const { walletAssets: ownerAssets } = useNftBalance(account.address ?? '', [], assetsFilter, 1) + const { walletAssets: ownerAssets } = useNftBalance({ ownerAddress: account.address ?? '', assetsFilter, first: 1 }) const walletAsset: WalletAsset | undefined = useMemo(() => ownerAssets?.[0], [ownerAssets]) const { assetInBag } = useMemo(() => { return { assetInBag: itemsInBag.some( - (item) => asset.tokenId === item.asset.tokenId && asset.address === item.asset.address + (item) => asset.tokenId === item.asset.tokenId && asset.address === item.asset.address, ), } }, [asset, itemsInBag]) @@ -344,7 +344,7 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps) 'newwindow', `left=${(window.screen.width - TWITTER_WIDTH) / 2}, top=${ (window.screen.height - TWITTER_HEIGHT) / 2 - }, width=${TWITTER_WIDTH}, height=${TWITTER_HEIGHT}` + }, width=${TWITTER_WIDTH}, height=${TWITTER_HEIGHT}`, ) } diff --git a/apps/web/src/nft/components/details/TraitsContainer.tsx b/apps/web/src/nft/components/details/TraitsContainer.tsx index 73769b95e21..248d7d42873 100644 --- a/apps/web/src/nft/components/details/TraitsContainer.tsx +++ b/apps/web/src/nft/components/details/TraitsContainer.tsx @@ -60,7 +60,7 @@ const GridItem = ({ trait, collectionAddress }: { trait: Trait; collectionAddres { traits: [`("${trait_type}","${trait_value}")`] }, { arrayFormat: 'comma', - } + }, ) return ( diff --git a/apps/web/src/nft/components/explore/Banner.tsx b/apps/web/src/nft/components/explore/Banner.tsx index 75c44578a40..9472023395f 100644 --- a/apps/web/src/nft/components/explore/Banner.tsx +++ b/apps/web/src/nft/components/explore/Banner.tsx @@ -91,7 +91,7 @@ const Banner = () => { const { data: trendingCollections } = useTrendingCollections( TRENDING_COLLECTION_SIZE + EXCLUDED_COLLECTIONS.length, - HistoryDuration.Day + HistoryDuration.Day, ) const collections = useMemo(() => { @@ -108,7 +108,7 @@ const Banner = () => { } setActiveCollectionIdx((idx) => calculateCardIndex(idx + direction, collections.length)) }, - [collections] + [collections], ) return ( diff --git a/apps/web/src/nft/components/explore/Carousel.tsx b/apps/web/src/nft/components/explore/Carousel.tsx index cd06ec400eb..2bf34669e0f 100644 --- a/apps/web/src/nft/components/explore/Carousel.tsx +++ b/apps/web/src/nft/components/explore/Carousel.tsx @@ -66,7 +66,7 @@ export const Carousel = ({ children, activeIndex, toggleNextSlide }: CarouselPro const idx = useCallback((x: number, l = children.length) => calculateCardIndex(x, l), [children]) const getPos = useCallback( (i: number, firstVis: number, firstVisIdx: number) => calculateFirstCardIndex(i, firstVis, firstVisIdx, idx), - [idx] + [idx], ) const [springs, set] = useSprings(children.length, (i) => ({ x: (i < children.length - 1 ? i : -1) * MAX_CARD_WIDTH, @@ -89,7 +89,7 @@ export const Carousel = ({ children, activeIndex, toggleNextSlide }: CarouselPro }) prev.current = [firstVis, firstVisIdx] }, - [idx, getPos, set, children.length] + [idx, getPos, set, children.length], ) const direction = useRef(0) @@ -103,7 +103,7 @@ export const Carousel = ({ children, activeIndex, toggleNextSlide }: CarouselPro direction.current = next toggleNextSlide(next) }, - [toggleNextSlide] + [toggleNextSlide], ) useEffect(() => { diff --git a/apps/web/src/nft/components/explore/CarouselCard.tsx b/apps/web/src/nft/components/explore/CarouselCard.tsx index 571aeb8d31d..073dc65ddc6 100644 --- a/apps/web/src/nft/components/explore/CarouselCard.tsx +++ b/apps/web/src/nft/components/explore/CarouselCard.tsx @@ -270,7 +270,7 @@ export const CarouselCard = ({ collection, onClick }: CarouselCardProps) => { {MARKETS_TO_CHECK.map((market) => { const marketplace = gqlCollection.marketplaceCount?.find( - (marketplace) => marketplace.marketplace === market + (marketplace) => marketplace.marketplace === market, ) if (!marketplace) { return null diff --git a/apps/web/src/nft/components/explore/CollectionTable.tsx b/apps/web/src/nft/components/explore/CollectionTable.tsx index a1ffe31194d..f9ea14f6294 100644 --- a/apps/web/src/nft/components/explore/CollectionTable.tsx +++ b/apps/web/src/nft/components/explore/CollectionTable.tsx @@ -156,7 +156,7 @@ const CollectionTable = ({ data, timePeriod }: { data: CollectionTableColumn[]; }, }, ], - [floorChangeSort, floorSort, volumeChangeSort, volumeSort, timePeriod] + [floorChangeSort, floorSort, volumeChangeSort, volumeSort, timePeriod], ) return ( diff --git a/apps/web/src/nft/components/explore/Table.tsx b/apps/web/src/nft/components/explore/Table.tsx index 11066407d49..4a19361dfc8 100644 --- a/apps/web/src/nft/components/explore/Table.tsx +++ b/apps/web/src/nft/components/explore/Table.tsx @@ -120,7 +120,7 @@ export function Table>({ }, ...props, }, - useSortBy + useSortBy, ) const navigate = useNavigate() diff --git a/apps/web/src/nft/components/explore/TrendingCollections.tsx b/apps/web/src/nft/components/explore/TrendingCollections.tsx index 36c27f98b66..88bec093584 100644 --- a/apps/web/src/nft/components/explore/TrendingCollections.tsx +++ b/apps/web/src/nft/components/explore/TrendingCollections.tsx @@ -91,7 +91,7 @@ const TrendingCollections = () => { const { data: trendingCollections, loading: trendingCollectionsAreLoading } = useTrendingCollections( 100, - convertTimePeriodToHistoryDuration(timePeriod) + convertTimePeriodToHistoryDuration(timePeriod), ) const ethUsdPrice = useNativeUsdPrice() diff --git a/apps/web/src/nft/components/profile/list/ListPage.tsx b/apps/web/src/nft/components/profile/list/ListPage.tsx index 2944bb75e44..80376116a86 100644 --- a/apps/web/src/nft/components/profile/list/ListPage.tsx +++ b/apps/web/src/nft/components/profile/list/ListPage.tsx @@ -195,14 +195,14 @@ export const ListPage = () => { setGlobalMarketplaces, sellAssets, issues, - }) + }), ) const { listings, collectionsRequiringApproval, setCollectionStatusAndCallback } = useNFTList( ({ listings, collectionsRequiringApproval, setCollectionStatusAndCallback }) => ({ listings, collectionsRequiringApproval, setCollectionStatusAndCallback, - }) + }), ) const totalEthListingValue = useMemo(() => getTotalEthValue(sellAssets), [sellAssets]) diff --git a/apps/web/src/nft/components/profile/list/ListingButton.tsx b/apps/web/src/nft/components/profile/list/ListingButton.tsx index 02395849bf3..865968d7c69 100644 --- a/apps/web/src/nft/components/profile/list/ListingButton.tsx +++ b/apps/web/src/nft/components/profile/list/ListingButton.tsx @@ -36,7 +36,7 @@ export const ListingButton = ({ onClick }: { onClick: () => void }) => { toggleShowResolveIssues, issues, setIssues, - }) + }), ) const [showWarning, setShowWarning] = useState(false) const isMobile = useIsMobile() diff --git a/apps/web/src/nft/components/profile/list/MarketplaceRow.tsx b/apps/web/src/nft/components/profile/list/MarketplaceRow.tsx index 3ae0cb8e58c..2b2b1c3666c 100644 --- a/apps/web/src/nft/components/profile/list/MarketplaceRow.tsx +++ b/apps/web/src/nft/components/profile/list/MarketplaceRow.tsx @@ -133,8 +133,8 @@ export const MarketplaceRow = ({ const [listPrice, setListPrice] = useState( () => asset.newListings?.find((listing) => - expandMarketplaceRows ? listing.marketplace.name === selectedMarkets?.[0].name : !!listing.price - )?.price + expandMarketplaceRows ? listing.marketplace.name === selectedMarkets?.[0].name : !!listing.price, + )?.price, ) const [globalOverride, setGlobalOverride] = useState(false) @@ -147,7 +147,7 @@ export const MarketplaceRow = ({ setAssetListPrice(asset, price, marketplace) } }, - [asset, selectedMarkets, setAssetListPrice, setGlobalPrice, showGlobalPrice] + [asset, selectedMarkets, setAssetListPrice, setGlobalPrice, showGlobalPrice], ) const fees = useMemo(() => { @@ -171,7 +171,7 @@ export const MarketplaceRow = ({ setGlobalOverride, listPrice, globalPrice, - globalPriceMethod + globalPriceMethod, ) // When in Same Price Mode and not overriding, update local price when global price changes diff --git a/apps/web/src/nft/components/profile/list/Modal/BelowFloorWarningModal.tsx b/apps/web/src/nft/components/profile/list/Modal/BelowFloorWarningModal.tsx index 89162961f3b..2942938c428 100644 --- a/apps/web/src/nft/components/profile/list/Modal/BelowFloorWarningModal.tsx +++ b/apps/web/src/nft/components/profile/list/Modal/BelowFloorWarningModal.tsx @@ -107,7 +107,7 @@ export const BelowFloorWarningModal = ({ value={listingsBelowFloor.length !== 1 ? 2 : 1} one={t('nft.oneListedDelta', { delta: formatDelta( - (1 - (listingsBelowFloor[0][1].price ?? 0) / (listingsBelowFloor[0][0].floorPrice ?? 0)) * 100 + (1 - (listingsBelowFloor[0][1].price ?? 0) / (listingsBelowFloor[0][0].floorPrice ?? 0)) * 100, ), })} other={t('nft.listedSignificantly', { diff --git a/apps/web/src/nft/components/profile/list/Modal/ListModal.tsx b/apps/web/src/nft/components/profile/list/Modal/ListModal.tsx index fccdd5449a5..7025d4a580a 100644 --- a/apps/web/src/nft/components/profile/list/Modal/ListModal.tsx +++ b/apps/web/src/nft/components/profile/list/Modal/ListModal.tsx @@ -67,13 +67,13 @@ export const ListModal = ({ overlayClick }: { overlayClick: () => void }) => { getLooksRareNonce, collectionsRequiringApproval, listings, - }) + }), ) const totalEthListingValue = useMemo(() => getTotalEthValue(sellAssets), [sellAssets]) const [openSection, toggleOpenSection] = useReducer( (s) => (s === Section.APPROVE ? Section.SIGN : Section.APPROVE), - Section.APPROVE + Section.APPROVE, ) const nativeCurrency = useNativeCurrency(account.chainId) const parsedAmount = tryParseCurrencyAmount(totalEthListingValue.toString(), nativeCurrency) @@ -85,12 +85,12 @@ export const ListModal = ({ overlayClick }: { overlayClick: () => void }) => { const allCollectionsApproved = useMemo( () => collectionsRequiringApproval.every((collection) => collection.status === ListingStatus.APPROVED), - [collectionsRequiringApproval] + [collectionsRequiringApproval], ) const allListingsApproved = useMemo( () => listings.every((listing) => listing.status === ListingStatus.APPROVED), - [listings] + [listings], ) const signListings = async () => { diff --git a/apps/web/src/nft/components/profile/list/NFTListingsGrid.tsx b/apps/web/src/nft/components/profile/list/NFTListingsGrid.tsx index da4ad47dd67..35c688765be 100644 --- a/apps/web/src/nft/components/profile/list/NFTListingsGrid.tsx +++ b/apps/web/src/nft/components/profile/list/NFTListingsGrid.tsx @@ -186,7 +186,7 @@ export const NFTListingsGrid = ({ selectedMarkets }: { selectedMarkets: ListingM }, }, ], - [globalPriceMethod] + [globalPriceMethod], ) let prompt diff --git a/apps/web/src/nft/components/profile/list/PriceTextInput.tsx b/apps/web/src/nft/components/profile/list/PriceTextInput.tsx index 5a80399d801..374bdd03035 100644 --- a/apps/web/src/nft/components/profile/list/PriceTextInput.tsx +++ b/apps/web/src/nft/components/profile/list/PriceTextInput.tsx @@ -117,10 +117,10 @@ export const PriceTextInput = ({ (warningType === WarningType.BELOW_FLOOR && percentBelowFloor >= 20) ? colors.red400 : warningType === WarningType.BELOW_FLOOR - ? theme.deprecated_accentWarning - : isGlobalPrice || !!listPrice - ? theme.accent1 - : theme.neutral2 + ? theme.deprecated_accentWarning + : isGlobalPrice || !!listPrice + ? theme.accent1 + : theme.neutral2 const setPrice = (event: React.ChangeEvent) => { if (!listPrice && event.target.value.includes('.') && parseFloat(event.target.value) === 0) { diff --git a/apps/web/src/nft/components/profile/list/SelectMarketplacesDropdown.tsx b/apps/web/src/nft/components/profile/list/SelectMarketplacesDropdown.tsx index ae36998e553..ddc0592b4ce 100644 --- a/apps/web/src/nft/components/profile/list/SelectMarketplacesDropdown.tsx +++ b/apps/web/src/nft/components/profile/list/SelectMarketplacesDropdown.tsx @@ -141,7 +141,7 @@ export const SelectMarketplacesDropdown = ({ const [isOpen, toggleIsOpen] = useReducer((s) => !s, false) const dropdownDisplayText = useMemo( () => (selectedMarkets.length === 1 ? selectedMarkets[0].name : 'Multiple'), - [selectedMarkets] + [selectedMarkets], ) const ref = useRef(null) useOnClickOutside(ref, () => isOpen && toggleIsOpen()) diff --git a/apps/web/src/nft/components/profile/list/SetDurationModal.tsx b/apps/web/src/nft/components/profile/list/SetDurationModal.tsx index 34db2a61bca..8c7ac2994a0 100644 --- a/apps/web/src/nft/components/profile/list/SetDurationModal.tsx +++ b/apps/web/src/nft/components/profile/list/SetDurationModal.tsx @@ -138,7 +138,7 @@ export const SetDurationModal = () => { }, }, ], - [duration] + [duration], ) let prompt diff --git a/apps/web/src/nft/components/profile/list/utils.ts b/apps/web/src/nft/components/profile/list/utils.ts index b2a5af55411..0bc7a3264b5 100644 --- a/apps/web/src/nft/components/profile/list/utils.ts +++ b/apps/web/src/nft/components/profile/list/utils.ts @@ -20,8 +20,8 @@ export async function approveCollectionRow( setCollectionStatusAndCallback: ( collection: CollectionRow, status: ListingStatus, - callback?: () => Promise - ) => void + callback?: () => Promise, + ) => void, ) { const callback = () => approveCollectionRow(collectionRow, signer, setCollectionStatusAndCallback) setCollectionStatusAndCallback(collectionRow, ListingStatus.SIGNING, callback) @@ -31,21 +31,21 @@ export async function approveCollectionRow( marketplace.name === 'OpenSea' ? OPENSEA_CROSS_CHAIN_CONDUIT : marketplace.name === 'LooksRare' - ? collectionRow.nftStandard === NftStandard.Erc721 - ? LOOKSRARE_MARKETPLACE_CONTRACT_721 - : LOOKSRARE_MARKETPLACE_CONTRACT_1155 - : marketplace.name === 'X2Y2' - ? collectionRow.nftStandard === NftStandard.Erc721 - ? X2Y2_TRANSFER_CONTRACT_721 - : X2Y2_TRANSFER_CONTRACT_1155 - : addresses.TRANSFER_MANAGER_ERC721 + ? collectionRow.nftStandard === NftStandard.Erc721 + ? LOOKSRARE_MARKETPLACE_CONTRACT_721 + : LOOKSRARE_MARKETPLACE_CONTRACT_1155 + : marketplace.name === 'X2Y2' + ? collectionRow.nftStandard === NftStandard.Erc721 + ? X2Y2_TRANSFER_CONTRACT_721 + : X2Y2_TRANSFER_CONTRACT_1155 + : addresses.TRANSFER_MANAGER_ERC721 !!collectionAddress && (await approveCollection( spender, collectionAddress, signer, (newStatus: ListingStatus) => setCollectionStatusAndCallback(collectionRow, newStatus, callback), - nftStandard + nftStandard, )) } @@ -55,7 +55,7 @@ export async function signListingRow( provider: Web3Provider, getLooksRareNonce: () => number, setLooksRareNonce: (nonce: number) => void, - setListingStatusAndCallback: (listing: ListingRow, status: ListingStatus, callback?: () => Promise) => void + setListingStatusAndCallback: (listing: ListingRow, status: ListingStatus, callback?: () => Promise) => void, ) { const looksRareNonce = getLooksRareNonce() const callback = () => { @@ -64,7 +64,7 @@ export async function signListingRow( setListingStatusAndCallback(listing, ListingStatus.SIGNING, callback) const { asset, marketplace } = listing const res = await signListing(marketplace, asset, signer, provider, looksRareNonce, (newStatus: ListingStatus) => - setListingStatusAndCallback(listing, newStatus, callback) + setListingStatusAndCallback(listing, newStatus, callback), ) res && listing.marketplace.name === 'LooksRare' && setLooksRareNonce(looksRareNonce + 1) } @@ -103,7 +103,7 @@ const getListings = (sellAssets: WalletAsset[]): [CollectionRow[], ListingRow[]] !newCollectionsToApprove.some( (collectionRow: CollectionRow) => collectionRow.collectionAddress === asset.asset_contract.address && - collectionRow.marketplace.name === marketplace.name + collectionRow.marketplace.name === marketplace.name, ) ) { const newCollectionRow = { @@ -132,7 +132,7 @@ export function useSubscribeListingState() { ({ setListings, setCollectionsRequiringApproval }) => ({ setListings, setCollectionsRequiringApproval, - }) + }), ) useEffect(() => { const [newCollectionsToApprove, newListings] = getListings(sellAssets) @@ -146,7 +146,7 @@ export function useHandleGlobalPriceToggle( setListPrice: Dispatch, setPrice: (price?: number) => void, listPrice?: number, - globalPrice?: number + globalPrice?: number, ) { useEffect(() => { let price: number | undefined @@ -170,7 +170,7 @@ export function useSyncPriceWithGlobalMethod( setGlobalOverride: Dispatch, listPrice?: number, globalPrice?: number, - globalPriceMethod?: SetPriceMethod + globalPriceMethod?: SetPriceMethod, ) { useEffect(() => { if (globalPriceMethod === SetPriceMethod.FLOOR_PRICE) { @@ -192,7 +192,7 @@ export function useUpdateInputAndWarnings( setWarningType: Dispatch, inputRef: React.MutableRefObject, asset: WalletAsset, - listPrice?: number + listPrice?: number, ) { useEffect(() => { setWarningType(WarningType.NONE) diff --git a/apps/web/src/nft/components/profile/view/EmptyWalletContent.test.tsx b/apps/web/src/nft/components/profile/view/EmptyWalletContent.test.tsx index f2a67771da0..403350dd44b 100644 --- a/apps/web/src/nft/components/profile/view/EmptyWalletContent.test.tsx +++ b/apps/web/src/nft/components/profile/view/EmptyWalletContent.test.tsx @@ -9,7 +9,7 @@ describe('EmptyWalletContent.tsx', () => { -
+
, ) expect(asFragment()).toMatchSnapshot() }) diff --git a/apps/web/src/nft/components/profile/view/FilterSidebar.tsx b/apps/web/src/nft/components/profile/view/FilterSidebar.tsx index 1c0fb0d5fd3..566157aacb4 100644 --- a/apps/web/src/nft/components/profile/view/FilterSidebar.tsx +++ b/apps/web/src/nft/components/profile/view/FilterSidebar.tsx @@ -102,7 +102,7 @@ export const FilterSidebar = ({ const hideSearch = useMemo( () => (walletCollections && walletCollections?.length >= WALLET_COLLECTIONS_PAGINATION_LIMIT) || isFetchingNextPage, - [walletCollections, isFetchingNextPage] + [walletCollections, isFetchingNextPage], ) return ( @@ -172,7 +172,7 @@ const CollectionSelect = ({ useEffect(() => { if (collectionSearchText) { const filtered = collections.filter((collection) => - collection.name?.toLowerCase().includes(collectionSearchText.toLowerCase()) + collection.name?.toLowerCase().includes(collectionSearchText.toLowerCase()), ) setDisplayCollections(filtered) } else { @@ -198,7 +198,7 @@ const CollectionSelect = ({ // Every row is loaded except for our loading indicator row. const isItemLoaded = useCallback( (index: number) => !hasNextPage || index < displayCollections.length, - [displayCollections.length, hasNextPage] + [displayCollections.length, hasNextPage], ) const CollectionFilterRow = useCallback( @@ -217,7 +217,7 @@ const CollectionSelect = ({ /> ) }, - [displayCollections, isFetchingNextPage, itemKey, collectionFilters, setCollectionFilters] + [displayCollections, isFetchingNextPage, itemKey, collectionFilters, setCollectionFilters], ) return ( @@ -306,7 +306,7 @@ const CollectionItem = ({ (address: string) => { return collectionFilters.some((collection) => collection === address) }, - [collectionFilters] + [collectionFilters], ) const handleCheckbox = () => { setCheckboxSelected(!isCheckboxSelected) diff --git a/apps/web/src/nft/components/profile/view/ProfilePage.tsx b/apps/web/src/nft/components/profile/view/ProfilePage.tsx index 2dec1e18e2a..e8a69cc6812 100644 --- a/apps/web/src/nft/components/profile/view/ProfilePage.tsx +++ b/apps/web/src/nft/components/profile/view/ProfilePage.tsx @@ -74,7 +74,7 @@ export const ProfilePage = () => { const ownerCollections = useMemo( () => (isSuccess ? ownerCollectionsData?.pages.map((page) => page.data).flat() : null), - [isSuccess, ownerCollectionsData] + [isSuccess, ownerCollectionsData], ) useEffect(() => { @@ -176,7 +176,11 @@ const ProfilePageNfts = ({ loading, hasNext, loadMore, - } = useNftBalance(account.address ?? '', collectionFilters, [], DEFAULT_WALLET_ASSET_QUERY_AMOUNT) + } = useNftBalance({ + ownerAddress: account.address ?? '', + collectionFilters, + first: DEFAULT_WALLET_ASSET_QUERY_AMOUNT, + }) const { gridX } = useSpring({ gridX: isFiltersExpanded ? FILTER_SIDEBAR_WIDTH : -PADDING, @@ -202,7 +206,7 @@ const ProfilePageNfts = ({ position={isMobile && isBagExpanded ? 'fixed' : 'static'} style={{ transform: gridX.to( - (x) => `translate(${Number(x) - (!isMobile && isFiltersExpanded ? FILTER_SIDEBAR_WIDTH : -PADDING)}px)` + (x) => `translate(${Number(x) - (!isMobile && isFiltersExpanded ? FILTER_SIDEBAR_WIDTH : -PADDING)}px)`, ), }} paddingY="20" diff --git a/apps/web/src/nft/components/profile/view/ViewMyNftsAsset.test.tsx b/apps/web/src/nft/components/profile/view/ViewMyNftsAsset.test.tsx index 49e31660768..3cced7b92e3 100644 --- a/apps/web/src/nft/components/profile/view/ViewMyNftsAsset.test.tsx +++ b/apps/web/src/nft/components/profile/view/ViewMyNftsAsset.test.tsx @@ -12,7 +12,7 @@ describe('NftCard', () => { mediaShouldBePlaying={false} setCurrentTokenPlayingMedia={() => undefined} hideDetails={false} - /> + />, ) expect(asFragment()).toMatchSnapshot() }) diff --git a/apps/web/src/nft/components/profile/view/ViewMyNftsAsset.tsx b/apps/web/src/nft/components/profile/view/ViewMyNftsAsset.tsx index 7c3856d779c..008efb69e0e 100644 --- a/apps/web/src/nft/components/profile/view/ViewMyNftsAsset.tsx +++ b/apps/web/src/nft/components/profile/view/ViewMyNftsAsset.tsx @@ -34,7 +34,7 @@ export const ViewMyNftsAsset = ({ const isSelected = useMemo(() => { return sellAssets.some( - (item) => item.tokenId === asset.tokenId && item.asset_contract.address === asset.asset_contract.address + (item) => item.tokenId === asset.tokenId && item.asset_contract.address === asset.asset_contract.address, ) }, [asset, sellAssets]) @@ -55,7 +55,7 @@ export const ViewMyNftsAsset = ({ if ( !cartExpanded && !sellAssets.find( - (x) => x.tokenId === asset.tokenId && x.asset_contract.address === asset.asset_contract.address + (x) => x.tokenId === asset.tokenId && x.asset_contract.address === asset.asset_contract.address, ) && !isMobile ) { diff --git a/apps/web/src/nft/css/cssObjectFromTheme.ts b/apps/web/src/nft/css/cssObjectFromTheme.ts index 3c4a7176d6d..1fec769491d 100644 --- a/apps/web/src/nft/css/cssObjectFromTheme.ts +++ b/apps/web/src/nft/css/cssObjectFromTheme.ts @@ -5,7 +5,7 @@ const resolveTheme = (theme: Theme | (() => Theme)) => (typeof theme === 'functi export function cssObjectFromTheme( theme: Theme | (() => Theme), - { extends: baseTheme }: { extends?: Theme | (() => Theme) } = {} + { extends: baseTheme }: { extends?: Theme | (() => Theme) } = {}, ) { const resolvedThemeVars = { ...assignInlineVars(themeVars, resolveTheme(theme)), @@ -18,7 +18,7 @@ export function cssObjectFromTheme( const resolvedBaseThemeVars = assignInlineVars(themeVars, resolveTheme(baseTheme)) const filteredVars = Object.fromEntries( - Object.entries(resolvedThemeVars).filter(([varName, value]) => value !== resolvedBaseThemeVars[varName]) + Object.entries(resolvedThemeVars).filter(([varName, value]) => value !== resolvedBaseThemeVars[varName]), ) return filteredVars diff --git a/apps/web/src/nft/hooks/useBag.ts b/apps/web/src/nft/hooks/useBag.ts index cf195c45e79..9781bd82ec1 100644 --- a/apps/web/src/nft/hooks/useBag.ts +++ b/apps/web/src/nft/hooks/useBag.ts @@ -82,7 +82,7 @@ export const useBag = createWithEqualityFn()( let index = -1 if (asset.tokenType !== NftStandard.Erc1155) { index = itemsInBag.findIndex( - (n) => n.asset.tokenId === asset.tokenId && n.asset.address === asset.address + (n) => n.asset.tokenId === asset.tokenId && n.asset.address === asset.address, ) } if (index !== -1) { @@ -123,8 +123,8 @@ export const useBag = createWithEqualityFn()( !assets.some((asset) => asset.id ? asset.id === item.asset.id - : asset.tokenId === item.asset.tokenId && asset.address === item.asset.address - ) + : asset.tokenId === item.asset.tokenId && asset.address === item.asset.address, + ), ) return { itemsInBag: itemsCopy, @@ -138,7 +138,7 @@ export const useBag = createWithEqualityFn()( return { itemsInBag: get().itemsInBag } } const itemsInBagCopy = itemsInBag.map((item) => - item.asset.address === contractAddress && item.inSweep ? { ...item, inSweep: false } : item + item.asset.address === contractAddress && item.inSweep ? { ...item, inSweep: false } : item, ) if (itemsInBag.length === 0) { return { @@ -166,7 +166,7 @@ export const useBag = createWithEqualityFn()( } }), }), - { name: 'useBag' } + { name: 'useBag' }, ), - shallow + shallow, ) diff --git a/apps/web/src/nft/hooks/useBagTotalEthPrice.ts b/apps/web/src/nft/hooks/useBagTotalEthPrice.ts index bef39aa023f..3ee2500021c 100644 --- a/apps/web/src/nft/hooks/useBagTotalEthPrice.ts +++ b/apps/web/src/nft/hooks/useBagTotalEthPrice.ts @@ -16,11 +16,11 @@ export function useBagTotalEthPrice(): BigNumber { item.status !== BagItemStatus.UNAVAILABLE ? total.add( BigNumber.from( - item.asset.updatedPriceInfo ? item.asset.updatedPriceInfo.ETHPrice : item.asset.priceInfo.ETHPrice - ) + item.asset.updatedPriceInfo ? item.asset.updatedPriceInfo.ETHPrice : item.asset.priceInfo.ETHPrice, + ), ) : total, - BigNumber.from(0) + BigNumber.from(0), ) return totalEthPrice diff --git a/apps/web/src/nft/hooks/useCollectionFilters.ts b/apps/web/src/nft/hooks/useCollectionFilters.ts index 6f93df2cbc3..278bd146215 100644 --- a/apps/web/src/nft/hooks/useCollectionFilters.ts +++ b/apps/web/src/nft/hooks/useCollectionFilters.ts @@ -107,6 +107,6 @@ export const useCollectionFilters = create()( toggleShowFullTraitName: ({ shouldShow, trait_value, trait_type }) => set(() => ({ showFullTraitName: { shouldShow, trait_value, trait_type } })), }), - { name: 'useCollectionTraits' } - ) + { name: 'useCollectionTraits' }, + ), ) diff --git a/apps/web/src/nft/hooks/useDerivedPayWithAnyTokenSwapInfo.ts b/apps/web/src/nft/hooks/useDerivedPayWithAnyTokenSwapInfo.ts index 8290cdf9e59..aa04e6b25e3 100644 --- a/apps/web/src/nft/hooks/useDerivedPayWithAnyTokenSwapInfo.ts +++ b/apps/web/src/nft/hooks/useDerivedPayWithAnyTokenSwapInfo.ts @@ -7,7 +7,7 @@ import { isClassicTrade } from 'state/routing/utils' export default function useDerivedPayWithAnyTokenSwapInfo( inputCurrency?: Currency, - parsedOutputAmount?: CurrencyAmount + parsedOutputAmount?: CurrencyAmount, ): { state: TradeState trade?: ClassicTrade @@ -18,7 +18,7 @@ export default function useDerivedPayWithAnyTokenSwapInfo( TradeType.EXACT_OUTPUT, parsedOutputAmount, inputCurrency ?? undefined, - RouterPreference.API + RouterPreference.API, ) const allowedSlippage = useAutoSlippageTolerance(isClassicTrade(trade) ? trade : undefined) diff --git a/apps/web/src/nft/hooks/useFetchAssets.ts b/apps/web/src/nft/hooks/useFetchAssets.ts index ddf3df610db..429309a5ef3 100644 --- a/apps/web/src/nft/hooks/useFetchAssets.ts +++ b/apps/web/src/nft/hooks/useFetchAssets.ts @@ -37,7 +37,7 @@ export function useFetchAssets(): () => Promise { isLocked, setLocked, setItemsInBag, - }) + }), ) const tokenTradeInput = useTokenInput((state) => state.tokenTradeInput) const itemsInBag = useMemo(() => recalculateBagUsingPooledAssets(uncheckedItemsInBag), [uncheckedItemsInBag]) diff --git a/apps/web/src/nft/hooks/useFiltersExpanded.ts b/apps/web/src/nft/hooks/useFiltersExpanded.ts index 4dc41954f78..96c695aa527 100644 --- a/apps/web/src/nft/hooks/useFiltersExpanded.ts +++ b/apps/web/src/nft/hooks/useFiltersExpanded.ts @@ -16,10 +16,10 @@ const useFiltersExpandedStore = create()( isExpanded: expanded, })), }), - { name: 'useFiltersExpanded' } + { name: 'useFiltersExpanded' }, ), - { name: 'useFiltersExpanded' } - ) + { name: 'useFiltersExpanded' }, + ), ) export const useFiltersExpanded = (): [boolean, (expanded: boolean) => void] => { diff --git a/apps/web/src/nft/hooks/useIsCollectionLoading.ts b/apps/web/src/nft/hooks/useIsCollectionLoading.ts index d7d311a26f5..fb785af5064 100644 --- a/apps/web/src/nft/hooks/useIsCollectionLoading.ts +++ b/apps/web/src/nft/hooks/useIsCollectionLoading.ts @@ -22,6 +22,6 @@ export const useIsCollectionLoading = create()( return { isCollectionStatsLoading } }), }), - { name: 'useIsCollectionLoading' } - ) + { name: 'useIsCollectionLoading' }, + ), ) diff --git a/apps/web/src/nft/hooks/useNFTList.ts b/apps/web/src/nft/hooks/useNFTList.ts index 8cba41add95..1d8c58562d6 100644 --- a/apps/web/src/nft/hooks/useNFTList.ts +++ b/apps/web/src/nft/hooks/useNFTList.ts @@ -15,7 +15,7 @@ interface NFTListState { setCollectionStatusAndCallback: ( collection: CollectionRow, status: ListingStatus, - callback?: () => Promise + callback?: () => Promise, ) => void } @@ -39,7 +39,7 @@ export const useNFTList = createWithEqualityFn()( oldListing.asset.asset_contract.address === listing.asset.asset_contract.address && oldListing.asset.tokenId === listing.asset.tokenId && oldListing.marketplace.name === listing.marketplace.name && - oldListing.price === listing.price + oldListing.price === listing.price, ) const oldStatus = oldListing?.status const oldCallback = oldListing?.callback @@ -71,7 +71,7 @@ export const useNFTList = createWithEqualityFn()( const oldCollection = get().collectionsRequiringApproval.find( (oldCollection) => oldCollection.collectionAddress === collection.collectionAddress && - oldCollection.marketplace.name === collection.marketplace.name + oldCollection.marketplace.name === collection.marketplace.name, ) const oldStatus = oldCollection?.status const oldCallback = oldCollection?.callback @@ -104,7 +104,7 @@ export const useNFTList = createWithEqualityFn()( (oldListing) => oldListing.name === listing.name && oldListing.price === listing.price && - oldListing.marketplace.name === listing.marketplace.name + oldListing.marketplace.name === listing.marketplace.name, ) if (oldListingIndex > -1) { const updatedListing = { @@ -123,7 +123,7 @@ export const useNFTList = createWithEqualityFn()( const collectionsCopy = [...collectionsRequiringApproval] const oldCollectionIndex = collectionsCopy.findIndex( (oldCollection) => - oldCollection.name === collection.name && oldCollection.marketplace.name === collection.marketplace.name + oldCollection.name === collection.name && oldCollection.marketplace.name === collection.marketplace.name, ) if (oldCollectionIndex > -1) { const updatedCollection = { @@ -138,5 +138,5 @@ export const useNFTList = createWithEqualityFn()( } }), })), - shallow + shallow, ) diff --git a/apps/web/src/nft/hooks/usePayWithAnyTokenSwap.ts b/apps/web/src/nft/hooks/usePayWithAnyTokenSwap.ts index 6e1b29ab906..c13eaf1ef7a 100644 --- a/apps/web/src/nft/hooks/usePayWithAnyTokenSwap.ts +++ b/apps/web/src/nft/hooks/usePayWithAnyTokenSwap.ts @@ -14,7 +14,7 @@ import { export default function usePayWithAnyTokenSwap( trade?: InterfaceTrade | undefined, allowance?: Allowance, - allowedSlippage?: Percent + allowedSlippage?: Percent, ) { const setTokenTradeInput = useTokenInput((state) => state.setTokenTradeInput) const hasRoutes = isClassicTrade(trade) && trade.routes diff --git a/apps/web/src/nft/hooks/usePriceImpact.ts b/apps/web/src/nft/hooks/usePriceImpact.ts index adac5587ca4..b9191021a28 100644 --- a/apps/web/src/nft/hooks/usePriceImpact.ts +++ b/apps/web/src/nft/hooks/usePriceImpact.ts @@ -25,8 +25,8 @@ export function usePriceImpact(trade?: ClassicTrade): PriceImpact | undefined { priceImpactWarning === 'error' ? theme.critical : priceImpactWarning === 'warning' - ? theme.deprecated_accentWarning - : undefined + ? theme.deprecated_accentWarning + : undefined return marketPriceImpact && priceImpactWarning && warningColor ? { diff --git a/apps/web/src/nft/hooks/usePriceRange.ts b/apps/web/src/nft/hooks/usePriceRange.ts index 284c77db4bd..0114ea5fc20 100644 --- a/apps/web/src/nft/hooks/usePriceRange.ts +++ b/apps/web/src/nft/hooks/usePriceRange.ts @@ -32,6 +32,6 @@ export const usePriceRange = create()( }) }, }), - { name: 'usePriceRange' } - ) + { name: 'usePriceRange' }, + ), ) diff --git a/apps/web/src/nft/hooks/useProfilePageState.ts b/apps/web/src/nft/hooks/useProfilePageState.ts index eac95027ca0..1c05d88dbe8 100644 --- a/apps/web/src/nft/hooks/useProfilePageState.ts +++ b/apps/web/src/nft/hooks/useProfilePageState.ts @@ -20,7 +20,7 @@ export const useProfilePageState = createWithEqualityFn()( state: newState, })), }), - { name: 'useProfilePageState' } + { name: 'useProfilePageState' }, ), - shallow + shallow, ) diff --git a/apps/web/src/nft/hooks/usePurchaseAssets.ts b/apps/web/src/nft/hooks/usePurchaseAssets.ts index 9b64bc4c460..16f029d5ad2 100644 --- a/apps/web/src/nft/hooks/usePurchaseAssets.ts +++ b/apps/web/src/nft/hooks/usePurchaseAssets.ts @@ -8,7 +8,7 @@ import { useCallback } from 'react' export function usePurchaseAssets(): ( routingData: RouteResponse, assetsToBuy: UpdatedGenieAsset[], - purchasingWithErc20?: boolean + purchasingWithErc20?: boolean, ) => Promise { const signer = useEthersSigner() const sendTransaction = useSendTransaction((state) => state.sendTransaction) @@ -39,6 +39,6 @@ export function usePurchaseAssets(): ( resetBag() } }, - [signer, resetBag, sendTransaction, setBagExpanded, setBagLocked, setTransactionResponse] + [signer, resetBag, sendTransaction, setBagExpanded, setBagLocked, setTransactionResponse], ) } diff --git a/apps/web/src/nft/hooks/useSellAsset.ts b/apps/web/src/nft/hooks/useSellAsset.ts index 9c3b30aa817..f147b705a70 100644 --- a/apps/web/src/nft/hooks/useSellAsset.ts +++ b/apps/web/src/nft/hooks/useSellAsset.ts @@ -37,15 +37,15 @@ export const useSellAsset = create()( return { sellAssets: [] } } else { sellAssets.find( - (x) => asset.tokenId === x.tokenId && x.asset_contract.address === asset.asset_contract.address + (x) => asset.tokenId === x.tokenId && x.asset_contract.address === asset.asset_contract.address, ) } const assetsCopy = [...sellAssets] assetsCopy.splice( sellAssets.findIndex( - (n) => n.tokenId === asset.tokenId && n.asset_contract.address === asset.asset_contract.address + (n) => n.tokenId === asset.tokenId && n.asset_contract.address === asset.asset_contract.address, ), - 1 + 1, ) return { sellAssets: assetsCopy } }) @@ -66,7 +66,7 @@ export const useSellAsset = create()( const assetsCopy = [...sellAssets] if (marketplace) { const listingIndex = asset.newListings?.findIndex( - (listing) => listing.marketplace.name === marketplace.name + (listing) => listing.marketplace.name === marketplace.name, ) if (asset.newListings && listingIndex != null && listingIndex > -1) { asset.newListings[listingIndex] = { price, marketplace, overrideFloorPrice: false } @@ -80,7 +80,7 @@ export const useSellAsset = create()( asset.marketAgnosticPrice = price } const index = sellAssets.findIndex( - (n) => n.tokenId === asset.tokenId && n.asset_contract.address === asset.asset_contract.address + (n) => n.tokenId === asset.tokenId && n.asset_contract.address === asset.asset_contract.address, ) assetsCopy[index] = asset return { sellAssets: assetsCopy } @@ -94,7 +94,7 @@ export const useSellAsset = create()( asset.newListings = [] for (const marketplace of marketplaces) { const listingIndex = asset.newListings.findIndex( - (listing) => listing.marketplace.name === marketplace.name + (listing) => listing.marketplace.name === marketplace.name, ) const newListing = { price: asset.marketAgnosticPrice, @@ -134,6 +134,6 @@ export const useSellAsset = create()( issues, })), }), - { name: 'useSelectAsset' } - ) + { name: 'useSelectAsset' }, + ), ) diff --git a/apps/web/src/nft/hooks/useSendTransaction.ts b/apps/web/src/nft/hooks/useSendTransaction.ts index 6e022dcb93b..558a025de70 100644 --- a/apps/web/src/nft/hooks/useSendTransaction.ts +++ b/apps/web/src/nft/hooks/useSendTransaction.ts @@ -24,7 +24,7 @@ interface TxState { signer: JsonRpcSigner, selectedAssets: UpdatedGenieAsset[], transactionData: RouteResponse, - purchasedWithErc20: boolean + purchasedWithErc20: boolean, ) => Promise } @@ -92,15 +92,15 @@ export const useSendTransaction = create()( } }, }), - { name: 'useSendTransactionState' } - ) + { name: 'useSendTransactionState' }, + ), ) const findNFTsPurchased = ( txReceipt: ContractReceipt, signerAddress: string, toBuy: GenieAsset[], - txRoute: RoutingItem[] + txRoute: RoutingItem[], ): UpdatedGenieAsset[] => { if (!txReceipt.logs) { return [] @@ -113,7 +113,7 @@ const findNFTsPurchased = ( const transferErc721BuyEvents = txReceipt.logs.filter( (x) => x.topics[0] === erc721Interface.getEventTopic('Transfer') && - hexStripZeros(x.topics[2]).toLowerCase() === signerAddress.toLowerCase() + hexStripZeros(x.topics[2]).toLowerCase() === signerAddress.toLowerCase(), ) const transferredErc721 = transferErc721BuyEvents.map((x) => ({ @@ -123,7 +123,7 @@ const findNFTsPurchased = ( const transferErc1155BuyEvents = txReceipt.logs.filter( (x) => x.topics[0] === erc1155Interface.getEventTopic('TransferSingle') && - hexStripZeros(x.topics[3]).toLowerCase() === signerAddress.toLowerCase() + hexStripZeros(x.topics[3]).toLowerCase() === signerAddress.toLowerCase(), ) const transferredErc1155 = transferErc1155BuyEvents.map((x) => ({ @@ -135,7 +135,7 @@ const findNFTsPurchased = ( const transferCryptopunkEvents = txReceipt.logs.filter( (x) => x.topics[0] === cryptopunksMarketInterface.getEventTopic('PunkTransfer') && - hexStripZeros(x.topics[2]).toLowerCase() === signerAddress.toLowerCase() + hexStripZeros(x.topics[2]).toLowerCase() === signerAddress.toLowerCase(), ) const transferredCryptopunks = transferCryptopunkEvents.map((x) => ({ address: x.address, @@ -148,7 +148,7 @@ const findNFTsPurchased = ( return allTransferred.some( (purchasedNft) => assetToBuy.address.toLowerCase() === purchasedNft.address.toLowerCase() && - parseInt(assetToBuy.tokenId).toString() === purchasedNft.tokenId + parseInt(assetToBuy.tokenId).toString() === purchasedNft.tokenId, ) }) @@ -159,7 +159,7 @@ const findNFTsNotPurchased = (toBuy: GenieAsset[], nftsPurchased: UpdatedGenieAs const nftsNotPurchased: Array = [] toBuy.forEach((selectedAsset) => { const purchasedNft = nftsPurchased.find( - (x) => x.address.toLowerCase() === selectedAsset.address.toLowerCase() && x.tokenId === selectedAsset.tokenId + (x) => x.address.toLowerCase() === selectedAsset.address.toLowerCase() && x.tokenId === selectedAsset.tokenId, ) if (!purchasedNft) { nftsNotPurchased.push(selectedAsset) diff --git a/apps/web/src/nft/hooks/useTokenInput.ts b/apps/web/src/nft/hooks/useTokenInput.ts index 5a2bdbb264b..c55fef31a1f 100644 --- a/apps/web/src/nft/hooks/useTokenInput.ts +++ b/apps/web/src/nft/hooks/useTokenInput.ts @@ -21,7 +21,7 @@ export const useTokenInput = createWithEqualityFn()( clearInputCurrency: () => set(() => ({ inputCurrency: undefined })), setTokenTradeInput: (tokenTradeInput) => set(() => ({ tokenTradeInput })), }), - { name: 'useTokenInput' } + { name: 'useTokenInput' }, ), - shallow + shallow, ) diff --git a/apps/web/src/nft/hooks/useTraitsOpen.ts b/apps/web/src/nft/hooks/useTraitsOpen.ts index 48503d6684c..4b7edbe029a 100644 --- a/apps/web/src/nft/hooks/useTraitsOpen.ts +++ b/apps/web/src/nft/hooks/useTraitsOpen.ts @@ -24,6 +24,6 @@ export const useTraitsOpen = create()( set(({ traitsOpen }) => ({ traitsOpen: { ...traitsOpen, [index]: isOpen } })) }, }), - { name: 'useTraitsOpen' } - ) + { name: 'useTraitsOpen' }, + ), ) diff --git a/apps/web/src/nft/hooks/useTransactionResponse.ts b/apps/web/src/nft/hooks/useTransactionResponse.ts index 15137808c4b..26acd3adc82 100644 --- a/apps/web/src/nft/hooks/useTransactionResponse.ts +++ b/apps/web/src/nft/hooks/useTransactionResponse.ts @@ -18,6 +18,6 @@ export const useTransactionResponse = create()( transactionResponse: txResponse, })), }), - { name: 'useTransactionResponse' } - ) + { name: 'useTransactionResponse' }, + ), ) diff --git a/apps/web/src/nft/hooks/useWalletCollections.ts b/apps/web/src/nft/hooks/useWalletCollections.ts index de8166b8c76..8bf8105a47a 100644 --- a/apps/web/src/nft/hooks/useWalletCollections.ts +++ b/apps/web/src/nft/hooks/useWalletCollections.ts @@ -58,8 +58,8 @@ export const useWalletCollections = create()( return { displayAssets: filterWalletAssets(walletAssets, listFilter) } }), }), - { name: 'useWalletCollections' } - ) + { name: 'useWalletCollections' }, + ), ) const filterWalletAssets = (walletAssets: WalletAsset[], listFilter: string) => { diff --git a/apps/web/src/nft/pages/asset/Asset.tsx b/apps/web/src/nft/pages/asset/Asset.tsx index 792aee44b88..5f3c3972da9 100644 --- a/apps/web/src/nft/pages/asset/Asset.tsx +++ b/apps/web/src/nft/pages/asset/Asset.tsx @@ -55,7 +55,7 @@ const AssetPage = () => { collection.collectionDescription ?? 'View traits, trading activity, descriptions, and other details on your NFTs.', }), - [asset.name, collection.collectionDescription, collection.collectionName, contractAddress, tokenId] + [asset.name, collection.collectionDescription, collection.collectionName, contractAddress, tokenId], ) const metaTags = useDynamicMetatags(metaTagProperties) diff --git a/apps/web/src/nft/types/sell/index.ts b/apps/web/src/nft/types/sell/index.ts index ed5fb993fd1..265dd33bb71 100644 --- a/apps/web/src/nft/types/sell/index.ts +++ b/apps/web/src/nft/types/sell/index.ts @@ -1,5 +1,6 @@ import { GenieCollection, PriceInfo } from 'nft/types/common' import { + Chain, NftMarketplace, NftStandard, OrderStatus, @@ -70,6 +71,7 @@ export interface WalletAsset { marketAgnosticPrice?: number newListings?: Listing[] marketplaces?: ListingMarket[] + chain?: Chain } export interface WalletCollection { diff --git a/apps/web/src/nft/utils/asset.tsx b/apps/web/src/nft/utils/asset.tsx index 6f98f3360bb..9069b54fe8e 100644 --- a/apps/web/src/nft/utils/asset.tsx +++ b/apps/web/src/nft/utils/asset.tsx @@ -19,7 +19,7 @@ import { v4 as uuidv4 } from 'uuid' export function getRarityStatus( rarityStatusCache: Map, id: string, - assets?: (GenieAsset | undefined)[] + assets?: (GenieAsset | undefined)[], ) { if (rarityStatusCache.has(id)) { return rarityStatusCache.get(id) diff --git a/apps/web/src/nft/utils/bag.ts b/apps/web/src/nft/utils/bag.ts index 7587f4063dc..2dd6bacc474 100644 --- a/apps/web/src/nft/utils/bag.ts +++ b/apps/web/src/nft/utils/bag.ts @@ -9,7 +9,7 @@ export function getPurchasableAssets(itemsInBag: BagItem[]): UpdatedGenieAsset[] function createBagFromUpdatedAssets( unavailable: UpdatedGenieAsset[], priceChanged: UpdatedGenieAsset[], - unchanged: UpdatedGenieAsset[] + unchanged: UpdatedGenieAsset[], ): BagItem[] { return [ ...unavailable.map((unavailableAsset) => ({ @@ -31,7 +31,7 @@ function evaluateNextBagState( hasAssets: boolean, shouldReview: boolean, hasAssetsInReview: boolean, - shouldRefetchCalldata: boolean + shouldRefetchCalldata: boolean, ): BagStatus { if (!hasAssets) { return BagStatus.ADDING_TO_BAG @@ -55,7 +55,7 @@ function evaluateNextBagState( export function getNextBagState( wishAssetsToBuy: UpdatedGenieAsset[], route: RoutingItem[], - purchasingWithErc20: boolean + purchasingWithErc20: boolean, ): { newBagItems: BagItem[]; nextBagStatus: BagStatus } { const { hasPriceAdjustment, updatedAssets } = compareAssetsWithTransactionRoute(wishAssetsToBuy, route) const shouldRefetchCalldata = hasPriceAdjustment && purchasingWithErc20 diff --git a/apps/web/src/nft/utils/carousel.ts b/apps/web/src/nft/utils/carousel.ts index 299478276ea..6e6ed7d1e25 100644 --- a/apps/web/src/nft/utils/carousel.ts +++ b/apps/web/src/nft/utils/carousel.ts @@ -6,7 +6,7 @@ export const calculateFirstCardIndex = ( i: number, firstVis: number, firstVisIdx: number, - idx: (x: number, l?: number) => number + idx: (x: number, l?: number) => number, ) => { return idx(i - firstVis + firstVisIdx) } diff --git a/apps/web/src/nft/utils/listNfts.ts b/apps/web/src/nft/utils/listNfts.ts index 61d72029fa7..be8e2773c63 100644 --- a/apps/web/src/nft/utils/listNfts.ts +++ b/apps/web/src/nft/utils/listNfts.ts @@ -48,7 +48,7 @@ const createConsiderationItem = (basisPoints: string, recipient: string): Consid const getConsiderationItems = ( asset: WalletAsset, price: BigNumber, - signerAddress: string + signerAddress: string, ): { sellerFee: ConsiderationInputItem creatorFee?: ConsiderationInputItem @@ -80,7 +80,7 @@ export async function approveCollection( collectionAddress: string, signer: Signer, setStatus: (newStatus: ListingStatus) => void, - nftStandard: NftStandard = NftStandard.Erc721 + nftStandard: NftStandard = NftStandard.Erc721, ): Promise { const contract = new Contract(collectionAddress, nftStandard === NftStandard.Erc721 ? ERC721 : ERC1155, signer) const signerAddress = await signer.getAddress() @@ -114,7 +114,7 @@ export async function signListing( signer: JsonRpcSigner, provider: Web3Provider, looksRareNonce = 0, - setStatus: (newStatus: ListingStatus) => void + setStatus: (newStatus: ListingStatus) => void, ): Promise { const seaport = new Seaport(provider, { conduitKeyToConduit: OPENSEA_KEY_TO_CONDUIT, @@ -135,7 +135,7 @@ export async function signListing( const listingInWei = parseEther(`${listingPrice}`) const { sellerFee, creatorFee, openSeaFee } = getConsiderationItems(asset, listingInWei, signerAddress) const considerationItems = [sellerFee, creatorFee, openSeaFee].filter( - (item): item is ConsiderationInputItem => item !== undefined + (item): item is ConsiderationInputItem => item !== undefined, ) const { executeAllActions } = await seaport.createOrder( @@ -153,7 +153,7 @@ export async function signListing( zone: ZERO_ADDRESS, allowPartialFills: true, }, - signerAddress + signerAddress, ) const order = await executeAllActions() @@ -210,7 +210,7 @@ export async function signListing( signer, SupportedChainId.MAINNET, makerOrder, - LOOKSRARE_MARKETPLACE_CONTRACT_721 + LOOKSRARE_MARKETPLACE_CONTRACT_721, ) setStatus(ListingStatus.PENDING) const payload = { diff --git a/apps/web/src/nft/utils/nftRoute.ts b/apps/web/src/nft/utils/nftRoute.ts index 83ad5a949b7..4f620b7594b 100644 --- a/apps/web/src/nft/utils/nftRoute.ts +++ b/apps/web/src/nft/utils/nftRoute.ts @@ -38,7 +38,7 @@ function buildRoutingItems(routingItems: readonly NftTrade[]): RoutingItem[] { export function buildRouteResponse( routeResponse: NftRouteResponse, - useErc20Token: boolean + useErc20Token: boolean, ): { route: RoutingItem[]; routeResponse: RouteResponse } { const route = routeResponse.route ? buildRoutingItems(routeResponse.route) : [] return { diff --git a/apps/web/src/nft/utils/pooledAssets.ts b/apps/web/src/nft/utils/pooledAssets.ts index 337bdd88a6d..70bf1dd8499 100644 --- a/apps/web/src/nft/utils/pooledAssets.ts +++ b/apps/web/src/nft/utils/pooledAssets.ts @@ -52,7 +52,7 @@ const calcSudoSwapExponentialBondingCurve = (currentPrice: BigNumber, delta: Big const calcSudoSwapXykBondingCurve = ( currentPrice: BigNumber, sudoSwapPool: Pool, - position = 0 + position = 0, ): BigNumber | undefined => { let virtualTokenBalance = BigNumber.from(sudoSwapPool.spotPrice) let virtualNFTBalance = BigNumber.from(sudoSwapPool.delta) @@ -149,7 +149,7 @@ const calcAmmBasedPoolprice = (asset: GenieAsset, position = 0): string => { ethReserves: number } > - )?.poolMetadata?.ethReserves?.toLocaleString('fullwide', { useGrouping: false }) ?? 1 + )?.poolMetadata?.ethReserves?.toLocaleString('fullwide', { useGrouping: false }) ?? 1, ) const tokenReserves = BigNumber.from( ( @@ -159,7 +159,7 @@ const calcAmmBasedPoolprice = (asset: GenieAsset, position = 0): string => { tokenReserves: number } > - )?.poolMetadata?.tokenReserves?.toLocaleString('fullwide', { useGrouping: false }) ?? 1 + )?.poolMetadata?.tokenReserves?.toLocaleString('fullwide', { useGrouping: false }) ?? 1, ) const numerator = ethReserves.mul(amountToBuy).mul(1000) const denominator = tokenReserves.sub(amountToBuy).mul(997) @@ -210,7 +210,7 @@ export const recalculateBagUsingPooledAssets = (uncheckedItemsInBag: BagItem[]) if ( !uncheckedItemsInBag.some((item) => item.asset.marketplace && isPooledMarket(item.asset.marketplace)) || uncheckedItemsInBag.every( - (item) => item.status === BagItemStatus.REVIEWED || item.status === BagItemStatus.REVIEWING_PRICE_CHANGE + (item) => item.status === BagItemStatus.REVIEWED || item.status === BagItemStatus.REVIEWING_PRICE_CHANGE, ) ) { return uncheckedItemsInBag @@ -231,7 +231,7 @@ export const recalculateBagUsingPooledAssets = (uncheckedItemsInBag: BagItem[]) ? calcAvgGroupPoolPrice(asset, itemsInPool.length) : recalculatePooledAssetPrice( asset, - itemsInPool.findIndex((itemInPool) => itemInPool.asset.tokenId === asset.tokenId) + itemsInPool.findIndex((itemInPool) => itemInPool.asset.tokenId === asset.tokenId), ) if (isPriceChangedAsset && item.asset.updatedPriceInfo) { diff --git a/apps/web/src/nft/utils/transactionResponse.ts b/apps/web/src/nft/utils/transactionResponse.ts index f601a174667..776dc335cc3 100644 --- a/apps/web/src/nft/utils/transactionResponse.ts +++ b/apps/web/src/nft/utils/transactionResponse.ts @@ -24,7 +24,7 @@ export const parseTransactionResponse = (transactionResponse: TxResponse | undef totalRefundValue = getTotalNftValue(nftsNotPurchased) totalUSDRefund = totalRefundValue && parseFloat(formatEther(totalRefundValue)) * ethPrice const txFee = BigNumber.from(txReceipt ? txReceipt.gasUsed : 0).mul( - BigNumber.from(txReceipt ? txReceipt.effectiveGasPrice : 0) + BigNumber.from(txReceipt ? txReceipt.effectiveGasPrice : 0), ) txFeeFiat = parseFloat(formatEther(txFee)) * ethPrice } diff --git a/apps/web/src/nft/utils/txRoute/combineItemsWithTxRoute.ts b/apps/web/src/nft/utils/txRoute/combineItemsWithTxRoute.ts index 0f029168bf6..19c93720761 100644 --- a/apps/web/src/nft/utils/txRoute/combineItemsWithTxRoute.ts +++ b/apps/web/src/nft/utils/txRoute/combineItemsWithTxRoute.ts @@ -25,7 +25,7 @@ const getPriceDiff = (oldPrice: string, newPrice: string): { hasPriceDiff: boole const isAveragePriceOfPooledAssets = ( asset: GenieAsset, numberOfAssetsInPool: number, - expectedPrice: string + expectedPrice: string, ): boolean => { return !getPriceDiff(calcAvgGroupPoolPrice(asset, numberOfAssetsInPool), expectedPrice).hasVisiblePriceDiff } @@ -34,7 +34,7 @@ const isAveragedPrice = ( item: UpdatedGenieAsset, items: UpdatedGenieAsset[], route: RoutingItem, - txRoute?: RoutingItem[] + txRoute?: RoutingItem[], ): boolean => { if (!(route && 'priceInfo' in route.assetOut)) { return false @@ -46,7 +46,7 @@ const isAveragedPrice = ( isAveragePriceOfPooledAssets( item, items.filter((routeItem) => itemInRouteAndSamePool(item, routeItem, txRoute)).length, - route.assetOut.priceInfo.basePrice + route.assetOut.priceInfo.basePrice, ) ) } @@ -62,7 +62,7 @@ const itemHasRoute = (item: UpdatedGenieAsset, txRoute?: RoutingItem[]): boolean const itemInRouteAndSamePool = ( item: UpdatedGenieAsset, routeItem: UpdatedGenieAsset, - txRoute?: RoutingItem[] + txRoute?: RoutingItem[], ): boolean => { return ( itemHasRoute(routeItem, txRoute) && @@ -74,7 +74,7 @@ const itemInRouteAndSamePool = ( export const compareAssetsWithTransactionRoute = ( items: UpdatedGenieAsset[], - txRoute?: RoutingItem[] + txRoute?: RoutingItem[], ): { hasPriceAdjustment: boolean; updatedAssets: UpdatedGenieAsset[] } => { let hasPriceAdjustment = false const updatedAssets = items.map((item) => { @@ -92,7 +92,7 @@ export const compareAssetsWithTransactionRoute = ( if (route && 'priceInfo' in route.assetOut) { const { hasPriceDiff, hasVisiblePriceDiff } = getPriceDiff( newPriceInfo.basePrice, - route.assetOut.priceInfo.basePrice + route.assetOut.priceInfo.basePrice, ) newPriceInfo = route.assetOut.priceInfo diff --git a/apps/web/src/nft/utils/updatedAssets.ts b/apps/web/src/nft/utils/updatedAssets.ts index f4e6cb90649..089c3ec3a3b 100644 --- a/apps/web/src/nft/utils/updatedAssets.ts +++ b/apps/web/src/nft/utils/updatedAssets.ts @@ -18,7 +18,7 @@ export const getTotalNftValue = (nfts: UpdatedGenieAsset[]): BigNumber => { nfts.reduce( (ethTotal, nft) => ethTotal.add(BigNumber.from(nft.updatedPriceInfo ? nft.updatedPriceInfo.ETHPrice : nft.priceInfo.ETHPrice)), - BigNumber.from(0) + BigNumber.from(0), ) ) } diff --git a/apps/web/src/nft/utils/x2y2.ts b/apps/web/src/nft/utils/x2y2.ts index 575b0c9bd5a..cd7c78d7a3a 100644 --- a/apps/web/src/nft/utils/x2y2.ts +++ b/apps/web/src/nft/utils/x2y2.ts @@ -111,7 +111,7 @@ export const createSellOrder = ( user: string, deadline: number, items: OfferItem[], - nftStandard: NftStandard = NftStandard.Erc721 + nftStandard: NftStandard = NftStandard.Erc721, ): Order => { const salt = randomSalt() const network = 1 // mainnet diff --git a/apps/web/src/pages/AddLiquidity/index.tsx b/apps/web/src/pages/AddLiquidity/index.tsx index 2d6a6a691d4..ab637b19a9a 100644 --- a/apps/web/src/pages/AddLiquidity/index.tsx +++ b/apps/web/src/pages/AddLiquidity/index.tsx @@ -146,7 +146,7 @@ function AddLiquidity() { // check for existing position if tokenId in url const { position: existingPositionDetails, loading: positionLoading } = useV3PositionFromTokenId( - tokenId ? BigNumber.from(tokenId) : undefined + tokenId ? BigNumber.from(tokenId) : undefined, ) const hasExistingPosition = !!existingPositionDetails && !positionLoading const { position: existingPosition } = useDerivedPositionInfo(existingPositionDetails) @@ -193,7 +193,7 @@ function AddLiquidity() { quoteCurrency ?? undefined, feeAmount, baseCurrency ?? undefined, - existingPosition + existingPosition, ) const { formatPrice } = useFormatter() @@ -234,7 +234,7 @@ function AddLiquidity() { [field]: maxAmountSpend(currencyBalances[field]), } }, - {} + {}, ) const atMaxAmounts: { [field in Field]?: CurrencyAmount } = [Field.CURRENCY_A, Field.CURRENCY_B].reduce( @@ -244,7 +244,7 @@ function AddLiquidity() { [field]: maxAmounts[field]?.equalTo(parsedAmounts[field] ?? '0'), } }, - {} + {}, ) const argentWalletContract = useArgentWalletContract() @@ -254,17 +254,17 @@ function AddLiquidity() { argentWalletContract ? undefined : parsedAmounts[Field.CURRENCY_A], account.status === 'connected' && account.chainId ? NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[account.chainId] - : undefined + : undefined, ) const [approvalB, approveBCallback] = useApproveCallback( argentWalletContract ? undefined : parsedAmounts[Field.CURRENCY_B], account.status === 'connected' && account.chainId ? NONFUNGIBLE_POSITION_MANAGER_ADDRESSES[account.chainId] - : undefined + : undefined, ) const allowedSlippage = useUserSlippageToleranceWithDefault( - outOfRange ? ZERO_PERCENT : DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE + outOfRange ? ZERO_PERCENT : DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE, ) async function onAdd() { @@ -368,7 +368,7 @@ function AddLiquidity() { baseCurrency.wrapped, quoteCurrency.wrapped, feeAmount, - account.chainId + account.chainId, ) : undefined, }) @@ -414,7 +414,7 @@ function AddLiquidity() { } } }, - [account.chainId, account.status] + [account.chainId, account.status], ) const handleCurrencyASelect = useCallback( @@ -426,7 +426,7 @@ function AddLiquidity() { navigate(`/add/${idA}/${idB}`) } }, - [handleCurrencySelect, currencyIdB, navigate] + [handleCurrencySelect, currencyIdB, navigate], ) const handleCurrencyBSelect = useCallback( @@ -438,7 +438,7 @@ function AddLiquidity() { navigate(`/add/${idA}/${idB}`) } }, - [handleCurrencySelect, currencyIdA, navigate] + [handleCurrencySelect, currencyIdA, navigate], ) const handleFeePoolSelect = useCallback( @@ -447,7 +447,7 @@ function AddLiquidity() { onRightRangeInput('') navigate(`/add/${currencyIdA}/${currencyIdB}/${newFeeAmount}`) }, - [currencyIdA, currencyIdB, navigate, onLeftRangeInput, onRightRangeInput] + [currencyIdA, currencyIdB, navigate, onLeftRangeInput, onRightRangeInput], ) const handleDismissConfirmation = useCallback(() => { @@ -634,14 +634,14 @@ function AddLiquidity() { data: usdcValueCurrencyA ? parseFloat(usdcValueCurrencyA.toSignificant()) : undefined, isLoading: false, }), - [usdcValueCurrencyA] + [usdcValueCurrencyA], ) const currencyBFiat = useMemo( () => ({ data: usdcValueCurrencyB ? parseFloat(usdcValueCurrencyB.toSignificant()) : undefined, isLoading: false, }), - [usdcValueCurrencyB] + [usdcValueCurrencyB], ) const owner = useSingleCallResult(tokenId ? positionManager : null, 'ownerOf', [tokenId]).result?.[0] @@ -816,17 +816,17 @@ function AddLiquidity() { handleRateToggle={() => { if (!ticksAtLimit[Bound.LOWER] && !ticksAtLimit[Bound.UPPER]) { onLeftRangeInput( - (invertPrice ? priceLower : priceUpper?.invert())?.toSignificant(6) ?? '' + (invertPrice ? priceLower : priceUpper?.invert())?.toSignificant(6) ?? '', ) onRightRangeInput( - (invertPrice ? priceUpper : priceLower?.invert())?.toSignificant(6) ?? '' + (invertPrice ? priceUpper : priceLower?.invert())?.toSignificant(6) ?? '', ) onFieldAInput(formattedAmounts[Field.CURRENCY_B] ?? '') } navigate( `/add/${currencyIdB as string}/${currencyIdA as string}${ feeAmount ? '/' + feeAmount : '' - }` + }`, ) }} /> diff --git a/apps/web/src/pages/AddLiquidityV2/PoolPriceBar.test.tsx b/apps/web/src/pages/AddLiquidityV2/PoolPriceBar.test.tsx index bbd6a6d1a45..7669261947d 100644 --- a/apps/web/src/pages/AddLiquidityV2/PoolPriceBar.test.tsx +++ b/apps/web/src/pages/AddLiquidityV2/PoolPriceBar.test.tsx @@ -21,7 +21,7 @@ describe('pool price bar', () => { it('handles undefined price', () => { render( - + , ) expect(screen.getByTestId('currency-b-price').textContent).toBe('-') diff --git a/apps/web/src/pages/AddLiquidityV2/index.tsx b/apps/web/src/pages/AddLiquidityV2/index.tsx index 830874796e8..c1b4d064d26 100644 --- a/apps/web/src/pages/AddLiquidityV2/index.tsx +++ b/apps/web/src/pages/AddLiquidityV2/index.tsx @@ -82,7 +82,8 @@ export default function AddLiquidity() { const oneCurrencyIsWETH = Boolean( account.chainId && wrappedNativeCurrency && - ((currencyA && currencyA.equals(wrappedNativeCurrency)) || (currencyB && currencyB.equals(wrappedNativeCurrency))) + ((currencyA && currencyA.equals(wrappedNativeCurrency)) || + (currencyB && currencyB.equals(wrappedNativeCurrency))), ) const accountDrawer = useAccountDrawer() // toggle wallet when disconnected @@ -130,7 +131,7 @@ export default function AddLiquidity() { [field]: maxAmountSpend(currencyBalances[field]), } }, - {} + {}, ) const atMaxAmounts: { [field in Field]?: CurrencyAmount } = [Field.CURRENCY_A, Field.CURRENCY_B].reduce( @@ -140,7 +141,7 @@ export default function AddLiquidity() { [field]: maxAmounts[field]?.equalTo(parsedAmounts[field] ?? '0'), } }, - {} + {}, ) const router = useV2RouterContract() @@ -235,7 +236,7 @@ export default function AddLiquidity() { tokenB: currencyB.wrapped, }), }) - }) + }), ) .catch((error) => { setAttemptingTxn(false) @@ -319,7 +320,7 @@ export default function AddLiquidity() { navigate(`/add/v2/${newCurrencyIdA}/${currencyIdB}`) } }, - [currencyIdB, navigate, currencyIdA] + [currencyIdB, navigate, currencyIdA], ) const handleCurrencyBSelect = useCallback( (currencyB: Currency) => { @@ -334,7 +335,7 @@ export default function AddLiquidity() { navigate(`/add/v2/${currencyIdA ? currencyIdA : 'ETH'}/${newCurrencyIdB}`) } }, - [currencyIdA, navigate, currencyIdB] + [currencyIdA, navigate, currencyIdB], ) const handleDismissConfirmation = useCallback(() => { diff --git a/apps/web/src/pages/App/Body.tsx b/apps/web/src/pages/App/Body.tsx index 4ae3e35c4d3..b30526af4e1 100644 --- a/apps/web/src/pages/App/Body.tsx +++ b/apps/web/src/pages/App/Body.tsx @@ -28,7 +28,7 @@ export const Body = memo(function Body() { /> ))} - ) : null + ) : null, )} diff --git a/apps/web/src/pages/CreateProposal/ProposalActionDetail.tsx b/apps/web/src/pages/CreateProposal/ProposalActionDetail.tsx index 8d67f5644ee..f0991179da5 100644 --- a/apps/web/src/pages/CreateProposal/ProposalActionDetail.tsx +++ b/apps/web/src/pages/CreateProposal/ProposalActionDetail.tsx @@ -87,7 +87,7 @@ export const ProposalActionDetail = ({ id="currency-input" currencySearchFilters={CREATE_PROPOSAL_CURRENCY_SEARCH_FILTERS} /> - ) : null + ) : null, )} ) diff --git a/apps/web/src/pages/CreateProposal/ProposalActionSelector.tsx b/apps/web/src/pages/CreateProposal/ProposalActionSelector.tsx index f870ae25e43..fe03452fe97 100644 --- a/apps/web/src/pages/CreateProposal/ProposalActionSelector.tsx +++ b/apps/web/src/pages/CreateProposal/ProposalActionSelector.tsx @@ -95,7 +95,7 @@ export function ProposalActionSelectorModal({ onProposalActionSelect(proposalAction) onDismiss() }, - [onDismiss, onProposalActionSelect] + [onDismiss, onProposalActionSelect], ) return ( diff --git a/apps/web/src/pages/CreateProposal/index.tsx b/apps/web/src/pages/CreateProposal/index.tsx index 782f5af6dc6..2d212b302a5 100644 --- a/apps/web/src/pages/CreateProposal/index.tsx +++ b/apps/web/src/pages/CreateProposal/index.tsx @@ -82,7 +82,7 @@ const CreateProposalButton = ({ const formattedProposalThreshold = proposalThreshold ? JSBI.divide( proposalThreshold.quotient, - JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(proposalThreshold.currency.decimals)) + JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(proposalThreshold.currency.decimals)), ).toLocaleString() : undefined @@ -151,7 +151,7 @@ export default function CreateProposal() { (proposalAction: ProposalAction) => { setProposalAction(proposalAction) }, - [setProposalAction] + [setProposalAction], ) const handleDismissActionSelector = useCallback(() => { @@ -167,35 +167,35 @@ export default function CreateProposal() { (toAddress: string) => { setToAddressValue(toAddress) }, - [setToAddressValue] + [setToAddressValue], ) const handleCurrencySelect = useCallback( (currency: Currency) => { setCurrencyValue(currency) }, - [setCurrencyValue] + [setCurrencyValue], ) const handleAmountInput = useCallback( (amount: string) => { setAmountValue(amount) }, - [setAmountValue] + [setAmountValue], ) const handleTitleInput = useCallback( (title: string) => { setTitleValue(title) }, - [setTitleValue] + [setTitleValue], ) const handleBodyInput = useCallback( (body: string) => { setBodyValue(body) }, - [setBodyValue] + [setBodyValue], ) const isFormInvalid = useMemo( @@ -206,13 +206,13 @@ export default function CreateProposal() { !currencyValue?.isToken || amountValue === '' || titleValue === '' || - bodyValue === '' + bodyValue === '', ), - [proposalAction, toAddressValue, currencyValue, amountValue, titleValue, bodyValue] + [proposalAction, toAddressValue, currencyValue, amountValue, titleValue, bodyValue], ) const hasEnoughVote = Boolean( - availableVotes && proposalThreshold && JSBI.greaterThanOrEqual(availableVotes.quotient, proposalThreshold.quotient) + availableVotes && proposalThreshold && JSBI.greaterThanOrEqual(availableVotes.quotient, proposalThreshold.quotient), ) const createProposalCallback = useCreateProposalCallback() diff --git a/apps/web/src/pages/Explore/charts/ExploreChartsSection.tsx b/apps/web/src/pages/Explore/charts/ExploreChartsSection.tsx index 1b1627697a2..83f238b5919 100644 --- a/apps/web/src/pages/Explore/charts/ExploreChartsSection.tsx +++ b/apps/web/src/pages/Explore/charts/ExploreChartsSection.tsx @@ -97,7 +97,7 @@ function VolumeChartSection({ chainId }: { chainId: SupportedInterfaceChainId }) const { entries, loading, dataQuality } = useHistoricalProtocolVolume( chainIdToBackendChain({ chainId, withFallback: true }), - isSmallScreen ? HistoryDuration.Month : timeGranularityToHistoryDuration(timePeriod) + isSmallScreen ? HistoryDuration.Month : timeGranularityToHistoryDuration(timePeriod), ) const params = useMemo<{ data: StackedHistogramData[]; colors: [string, string]; headerHeight: number }>( @@ -107,7 +107,7 @@ function VolumeChartSection({ chainId }: { chainId: SupportedInterfaceChainId }) headerHeight: 85, stale: dataQuality === DataQuality.STALE, }), - [entries, dataQuality, theme.accent1, theme.accent3] + [entries, dataQuality, theme.accent1, theme.accent3], ) const cumulativeVolume = useMemo(() => getCumulativeVolume(entries), [entries]) @@ -169,7 +169,7 @@ function TVLChartSection({ chainId }: { chainId: SupportedInterfaceChainId }) { data: entries, colors: EXPLORE_PRICE_SOURCES?.map((source) => getProtocolColor(source, theme)) ?? [theme.accent1], }), - [entries, theme] + [entries, theme], ) const isSmallScreen = !useScreenSize()['sm'] diff --git a/apps/web/src/pages/Explore/index.tsx b/apps/web/src/pages/Explore/index.tsx index 952e097cecb..fd3d3f03b66 100644 --- a/apps/web/src/pages/Explore/index.tsx +++ b/apps/web/src/pages/Explore/index.tsx @@ -152,8 +152,8 @@ const Explore = ({ initialTab }: { initialTab?: ExploreTab }) => { navigate(getTokenExploreURL({ tab, chain })) } }, - [navigate, tab] - ) + [navigate, tab], + ), ) return ( diff --git a/apps/web/src/pages/Explore/tables/RecentTransactions.tsx b/apps/web/src/pages/Explore/tables/RecentTransactions.tsx index cbce36bb557..33a6bad568e 100644 --- a/apps/web/src/pages/Explore/tables/RecentTransactions.tsx +++ b/apps/web/src/pages/Explore/tables/RecentTransactions.tsx @@ -102,7 +102,7 @@ export default function RecentTransactions() { {transaction.getValue?.().type === PoolTransactionType.Swap ? ( ) : ( - + )} diff --git a/apps/web/src/pages/Landing/components/StatCard.tsx b/apps/web/src/pages/Landing/components/StatCard.tsx index 801155ce9f0..fadc17961af 100644 --- a/apps/web/src/pages/Landing/components/StatCard.tsx +++ b/apps/web/src/pages/Landing/components/StatCard.tsx @@ -173,10 +173,10 @@ function StringInterpolationWithMotion({ value, delay, inView, live }: Omit })} diff --git a/apps/web/src/pages/Landing/components/TokenCloud/Token.tsx b/apps/web/src/pages/Landing/components/TokenCloud/Token.tsx index 361adc78d32..282ea97af69 100644 --- a/apps/web/src/pages/Landing/components/TokenCloud/Token.tsx +++ b/apps/web/src/pages/Landing/components/TokenCloud/Token.tsx @@ -162,9 +162,9 @@ export function Token(props: { address, chain, }) - : `/nfts/collection/${address}` + : `/nfts/collection/${address}`, ), - [address, chain, navigate, standard] + [address, chain, navigate, standard], ) const borderRadius = size / 8 diff --git a/apps/web/src/pages/Landing/components/cards/WebappCard.tsx b/apps/web/src/pages/Landing/components/cards/WebappCard.tsx index 140a731805c..2ab93566ce8 100644 --- a/apps/web/src/pages/Landing/components/cards/WebappCard.tsx +++ b/apps/web/src/pages/Landing/components/cards/WebappCard.tsx @@ -62,7 +62,9 @@ const TokenRow = styled.div` padding: 12px; border-radius: 16px; } - transition: background-color 125ms ease-in, transform 125ms ease-in; + transition: + background-color 125ms ease-in, + transform 125ms ease-in; &:hover { background-color: ${({ theme }) => theme.surface2}; transform: scale(1.03); @@ -209,10 +211,10 @@ function Token({ chainId, address }: { chainId: SupportedInterfaceChainId; addre getTokenDetailsURL({ address: address === 'ETH' ? NATIVE_CHAIN_ID : address, chain: chainIdToBackendChain({ chainId }), - }) + }), ) }, - [address, chainId, navigate] + [address, chainId, navigate], ) return ( diff --git a/apps/web/src/pages/Landing/index.tsx b/apps/web/src/pages/Landing/index.tsx index 21f2015d37c..e811e3cc288 100644 --- a/apps/web/src/pages/Landing/index.tsx +++ b/apps/web/src/pages/Landing/index.tsx @@ -44,7 +44,7 @@ export default function Landing() { disconnect() } }, - isExitAnimationEnabled ? TRANSITION_DURATIONS.slow : TRANSITION_DURATIONS.fast + isExitAnimationEnabled ? TRANSITION_DURATIONS.slow : TRANSITION_DURATIONS.fast, ) return () => clearTimeout(timeoutId) }, [ diff --git a/apps/web/src/pages/Landing/sections/Hero.tsx b/apps/web/src/pages/Landing/sections/Hero.tsx index c0b321d9cec..e843c230bb7 100644 --- a/apps/web/src/pages/Landing/sections/Hero.tsx +++ b/apps/web/src/pages/Landing/sections/Hero.tsx @@ -7,6 +7,7 @@ import { TokenCloud } from 'pages/Landing/components/TokenCloud/index' import { Hover, RiseIn, RiseInText } from 'pages/Landing/components/animations' import { Swap } from 'pages/Swap' import { ChevronDown } from 'react-feather' +import { useTranslation } from 'react-i18next' import styled, { css, keyframes } from 'styled-components' import { BREAKPOINTS } from 'theme' import { Text } from 'ui/src' @@ -95,6 +96,7 @@ interface HeroProps { export function Hero({ scrollToRef, transition }: HeroProps) { const { height: scrollPosition } = useScroll() const initialInputCurrency = useCurrency('ETH') + const { t } = useTranslation() const translateY = -scrollPosition / 7 const opacityY = 1 - scrollPosition / 1000 @@ -116,18 +118,22 @@ export function Hero({ scrollToRef, transition }: HeroProps) { > - - - {' '} - - - + {t('hero.swap.title') + .split(' ') + .map((word, index) => { + if (word === '
') { + return
+ } else { + return ( + <> + + {word} + {' '} + + ) + } + })}
- - - - -
diff --git a/apps/web/src/pages/Landing/sections/useInView.tsx b/apps/web/src/pages/Landing/sections/useInView.tsx index 4f89e394add..4b87b91e2be 100644 --- a/apps/web/src/pages/Landing/sections/useInView.tsx +++ b/apps/web/src/pages/Landing/sections/useInView.tsx @@ -17,7 +17,7 @@ export const useInView = () => { }, { threshold: 0.25, - } + }, ) io.observe(ref.current) diff --git a/apps/web/src/pages/MigrateV2/MigrateV2Pair.tsx b/apps/web/src/pages/MigrateV2/MigrateV2Pair.tsx index f53d6d1df42..d7c7d22fed7 100644 --- a/apps/web/src/pages/MigrateV2/MigrateV2Pair.tsx +++ b/apps/web/src/pages/MigrateV2/MigrateV2Pair.tsx @@ -147,17 +147,17 @@ function V2PairMigration({ () => CurrencyAmount.fromRawAmount( token0, - JSBI.divide(JSBI.multiply(pairBalance.quotient, reserve0.quotient), totalSupply.quotient) + JSBI.divide(JSBI.multiply(pairBalance.quotient, reserve0.quotient), totalSupply.quotient), ), - [token0, pairBalance, reserve0, totalSupply] + [token0, pairBalance, reserve0, totalSupply], ) const token1Value = useMemo( () => CurrencyAmount.fromRawAmount( token1, - JSBI.divide(JSBI.multiply(pairBalance.quotient, reserve1.quotient), totalSupply.quotient) + JSBI.divide(JSBI.multiply(pairBalance.quotient, reserve1.quotient), totalSupply.quotient), ), - [token1, pairBalance, reserve1, totalSupply] + [token1, pairBalance, reserve1, totalSupply], ) // set up v3 pool @@ -168,7 +168,7 @@ function V2PairMigration({ // get spot prices + price difference const v2SpotPrice = useMemo( () => new Price(token0, token1, reserve0.quotient, reserve1.quotient), - [token0, token1, reserve0, reserve1] + [token0, token1, reserve0, reserve1], ) const v3SpotPrice = poolState === PoolState.EXISTS ? pool?.token0Price : undefined @@ -186,7 +186,7 @@ function V2PairMigration({ token0, token1, feeAmount, - baseToken + baseToken, ) // get value and prices at ticks @@ -198,7 +198,7 @@ function V2PairMigration({ baseToken.equals(token0) ? token1 : token0, feeAmount, tickLower, - tickUpper + tickUpper, ) const { onLeftRangeInput, onRightRangeInput } = useV3MintActionHandlers(noLiquidity) @@ -221,18 +221,18 @@ function V2PairMigration({ const { amount0: v3Amount0Min, amount1: v3Amount1Min } = useMemo( () => (position ? position.mintAmountsWithSlippage(allowedSlippage) : { amount0: undefined, amount1: undefined }), - [position, allowedSlippage] + [position, allowedSlippage], ) const refund0 = useMemo( () => position && CurrencyAmount.fromRawAmount(token0, JSBI.subtract(token0Value.quotient, position.amount0.quotient)), - [token0Value, position, token0] + [token0Value, position, token0], ) const refund1 = useMemo( () => position && CurrencyAmount.fromRawAmount(token1, JSBI.subtract(token1Value.quotient, position.amount1.quotient)), - [token1Value, position, token1] + [token1Value, position, token1], ) const [confirmingMigration, setConfirmingMigration] = useState(false) @@ -300,7 +300,7 @@ function V2PairMigration({ signatureData.v, signatureData.r, signatureData.s, - ]) + ]), ) } @@ -312,7 +312,7 @@ function V2PairMigration({ token1.address, feeAmount, `0x${sqrtPrice.toString(16)}`, - ]) + ]), ) } @@ -334,7 +334,7 @@ function V2PairMigration({ deadline, refundAsETH: true, // hard-code this for now }, - ]) + ]), ) setConfirmingMigration(true) @@ -408,7 +408,7 @@ function V2PairMigration({ href={getExplorerLink( account.chainId ?? UniverseChainId.Mainnet, migrator?.address ?? '', - ExplorerDataType.ADDRESS + ExplorerDataType.ADDRESS, )} > @@ -726,7 +726,7 @@ export default function MigrateV2Pair() { // get liquidity token balance const liquidityToken: Token | undefined = useMemo( () => (account.chainId && validatedAddress ? new Token(account.chainId, validatedAddress, 18) : undefined), - [account.chainId, validatedAddress] + [account.chainId, validatedAddress], ) // get data required for V2 pair migration @@ -735,11 +735,11 @@ export default function MigrateV2Pair() { const [reserve0Raw, reserve1Raw] = useSingleCallResult(pair, 'getReserves')?.result ?? [] const reserve0 = useMemo( () => (token0 && reserve0Raw ? CurrencyAmount.fromRawAmount(token0, reserve0Raw) : undefined), - [token0, reserve0Raw] + [token0, reserve0Raw], ) const reserve1 = useMemo( () => (token1 && reserve1Raw ? CurrencyAmount.fromRawAmount(token1, reserve1Raw) : undefined), - [token1, reserve1Raw] + [token1, reserve1Raw], ) // redirect for invalid url params diff --git a/apps/web/src/pages/MigrateV2/index.tsx b/apps/web/src/pages/MigrateV2/index.tsx index c0ec19dbc69..031df93eb33 100644 --- a/apps/web/src/pages/MigrateV2/index.tsx +++ b/apps/web/src/pages/MigrateV2/index.tsx @@ -41,7 +41,7 @@ const computeSushiPairAddress = ({ tokenA, tokenB }: { tokenA: Token; tokenB: To return getCreate2Address( '0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac', keccak256(['bytes'], [pack(['address', 'address'], [token0.address, token1.address])]), - '0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303' + '0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303', ) } @@ -75,7 +75,7 @@ export default function MigrateV2() { tokens, } }), - [trackedTokenPairs, account.chainId, v2FactoryAddress] + [trackedTokenPairs, account.chainId, v2FactoryAddress], ) // get pair liquidity token addresses for balance-fetching purposes @@ -91,7 +91,7 @@ export default function MigrateV2() { // fetch pair balances const [pairBalances, fetchingPairBalances] = useRpcTokenBalancesWithLoadingIndicator( account.address, - allLiquidityTokens + allLiquidityTokens, ) // filter for v2 liquidity tokens that the user has a balance in @@ -112,7 +112,7 @@ export default function MigrateV2() { } return tokenPairsWithLiquidityTokens.filter( - ({ sushiLiquidityToken }) => !!sushiLiquidityToken && pairBalances[sushiLiquidityToken.address]?.greaterThan(0) + ({ sushiLiquidityToken }) => !!sushiLiquidityToken && pairBalances[sushiLiquidityToken.address]?.greaterThan(0), ) }, [fetchingPairBalances, tokenPairsWithLiquidityTokens, pairBalances]) diff --git a/apps/web/src/pages/Pool/PositionPage.test.tsx b/apps/web/src/pages/Pool/PositionPage.test.tsx index f521cc7c870..32ae03eb969 100644 --- a/apps/web/src/pages/Pool/PositionPage.test.tsx +++ b/apps/web/src/pages/Pool/PositionPage.test.tsx @@ -40,7 +40,7 @@ const pool = new Pool( FeeAmount.LOW, '1829845065927797685282268152898194', '118646741804633449199', - 200958 + 200958, ) const USDC_AMOUNT = CurrencyAmount.fromRawAmount(USDC_MAINNET, '1224156977') diff --git a/apps/web/src/pages/Pool/PositionPage.tsx b/apps/web/src/pages/Pool/PositionPage.tsx index 5d9f84fc319..249abc620f8 100644 --- a/apps/web/src/pages/Pool/PositionPage.tsx +++ b/apps/web/src/pages/Pool/PositionPage.tsx @@ -253,7 +253,7 @@ function LinkedCurrency({ chainId, currency }: { chainId: number; currency?: Cur function getRatio( lower: Price, current: Price, - upper: Price + upper: Price, ) { try { if (!current.greaterThan(lower)) { @@ -478,7 +478,7 @@ function PositionPageContent() { ? getRatio( inverted ? priceUpper.invert() : priceLower, pool.token0Price, - inverted ? priceLower.invert() : priceUpper + inverted ? priceLower.invert() : priceUpper, ) : undefined }, [inverted, pool, priceLower, priceUpper]) @@ -670,7 +670,7 @@ function PositionPageContent() { currency0 && currency1 && (currency0.isNative || currency1.isNative) && - !collectMigrationHash + !collectMigrationHash, ) if (!positionDetails && !loading) { @@ -737,7 +737,7 @@ function PositionPageContent() { poolAddress ? getPoolDetailsURL( poolAddress, - chainIdToBackendChain({ chainId: supportedChain, withFallback: true }) + chainIdToBackendChain({ chainId: supportedChain, withFallback: true }), ) : '' } diff --git a/apps/web/src/pages/Pool/index.tsx b/apps/web/src/pages/Pool/index.tsx index c421daa33a1..ee5b1b0508c 100644 --- a/apps/web/src/pages/Pool/index.tsx +++ b/apps/web/src/pages/Pool/index.tsx @@ -205,12 +205,12 @@ export default function Pool() { acc[p.liquidity?.isZero() ? 1 : 0].push(p) return acc }, - [[], []] + [[], []], ) ?? [[], []] const userSelectedPositionSet = useMemo( () => [...openPositions, ...(userHideClosedPositions ? [] : closedPositions)], - [closedPositions, openPositions, userHideClosedPositions] + [closedPositions, openPositions, userHideClosedPositions], ) const filteredPositions = useFilterPossiblyMaliciousPositions(userSelectedPositionSet) diff --git a/apps/web/src/pages/Pool/v2.tsx b/apps/web/src/pages/Pool/v2.tsx index c106f957933..108a3c93925 100644 --- a/apps/web/src/pages/Pool/v2.tsx +++ b/apps/web/src/pages/Pool/v2.tsx @@ -97,7 +97,7 @@ export default function Pool() { } const tokenPairsWithLiquidityTokens = useMemo( () => trackedTokenPairs.map((tokens) => ({ liquidityToken: toV2LiquidityToken(tokens), tokens })), - [trackedTokenPairs] + [trackedTokenPairs], ) const [balanceMap, fetchingV2PairBalances] = useRpcTokenBalancesWithLoadingIndicator( account.address, @@ -111,7 +111,7 @@ export default function Pool() { tokenPairsWithLiquidityTokens.filter(({ liquidityToken }) => { return balanceMap[liquidityToken.address]?.greaterThan(0) }), - [tokenPairsWithLiquidityTokens, balanceMap] + [tokenPairsWithLiquidityTokens, balanceMap], ) const v2Pairs = useV2Pairs(liquidityTokensWithBalances.map(({ tokens }) => tokens)) @@ -123,7 +123,7 @@ export default function Pool() { // show liquidity even if its deposited in rewards contract const stakingInfo = useStakingInfo() const stakingInfosWithBalance = stakingInfo?.filter((pool) => - JSBI.greaterThan(pool.stakedAmount.quotient, BIG_INT_ZERO) + JSBI.greaterThan(pool.stakedAmount.quotient, BIG_INT_ZERO), ) const stakingPairs = useV2Pairs(stakingInfosWithBalance?.map((stakingInfo) => stakingInfo.tokens)) @@ -236,7 +236,7 @@ export default function Pool() { pair={stakingPair[1]} stakedBalance={stakingInfosWithBalance[i].stakedAmount} /> - ) + ), )} { name: 'USD Coin', decimals: 6, }, - }) + }), ) store.dispatch( addSerializedToken({ @@ -52,7 +52,7 @@ describe('PoolDetailsPage', () => { name: 'Wrapped Ether', decimals: 18, }, - }) + }), ) }) diff --git a/apps/web/src/pages/PoolFinder/index.tsx b/apps/web/src/pages/PoolFinder/index.tsx index b178f94322e..84dd941fa7c 100644 --- a/apps/web/src/pages/PoolFinder/index.tsx +++ b/apps/web/src/pages/PoolFinder/index.tsx @@ -51,7 +51,7 @@ export default function PoolFinder() { const [activeField, setActiveField] = useState(Fields.TOKEN1) const [currency0, setCurrency0] = useState(() => - account.chainId ? nativeOnChain(account.chainId) : null + account.chainId ? nativeOnChain(account.chainId) : null, ) const [currency1, setCurrency1] = useState(null) @@ -69,7 +69,7 @@ export default function PoolFinder() { pairState === PairState.EXISTS && pair && JSBI.equal(pair.reserve0.quotient, JSBI.BigInt(0)) && - JSBI.equal(pair.reserve1.quotient, JSBI.BigInt(0)) + JSBI.equal(pair.reserve1.quotient, JSBI.BigInt(0)), ) const position: CurrencyAmount | undefined = useTokenBalance(account.address, pair?.liquidityToken) @@ -83,7 +83,7 @@ export default function PoolFinder() { setCurrency1(currency) } }, - [activeField] + [activeField], ) const handleSearchDismiss = useCallback(() => { diff --git a/apps/web/src/pages/RemoveLiquidity/V3.tsx b/apps/web/src/pages/RemoveLiquidity/V3.tsx index 1a7be92290d..369873d6bc7 100644 --- a/apps/web/src/pages/RemoveLiquidity/V3.tsx +++ b/apps/web/src/pages/RemoveLiquidity/V3.tsx @@ -178,7 +178,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { positionSDK.amount0.currency, positionSDK.amount1.currency, position.fee, - account.chainId + account.chainId, ) : undefined, }) @@ -321,7 +321,7 @@ function Remove({ tokenId }: { tokenId: BigNumber }) { (liquidityValue0.currency.isNative || liquidityValue1.currency.isNative || WRAPPED_NATIVE_CURRENCY[liquidityValue0.currency.chainId]?.equals(liquidityValue0.currency.wrapped) || - WRAPPED_NATIVE_CURRENCY[liquidityValue1.currency.chainId]?.equals(liquidityValue1.currency.wrapped)) + WRAPPED_NATIVE_CURRENCY[liquidityValue1.currency.chainId]?.equals(liquidityValue1.currency.wrapped)), ) return ( diff --git a/apps/web/src/pages/RemoveLiquidity/index.tsx b/apps/web/src/pages/RemoveLiquidity/index.tsx index a1e1bb6c6eb..7cd955c4f6b 100644 --- a/apps/web/src/pages/RemoveLiquidity/index.tsx +++ b/apps/web/src/pages/RemoveLiquidity/index.tsx @@ -105,8 +105,8 @@ function RemoveLiquidity() { [Field.LIQUIDITY_PERCENT]: parsedAmounts[Field.LIQUIDITY_PERCENT].equalTo('0') ? '0' : parsedAmounts[Field.LIQUIDITY_PERCENT].lessThan(new Percent('1', '100')) - ? '<1' - : parsedAmounts[Field.LIQUIDITY_PERCENT].toFixed(0), + ? '<1' + : parsedAmounts[Field.LIQUIDITY_PERCENT].toFixed(0), [Field.LIQUIDITY]: independentField === Field.LIQUIDITY ? typedValue : parsedAmounts[Field.LIQUIDITY]?.toSignificant(6) ?? '', [Field.CURRENCY_A]: @@ -125,7 +125,7 @@ function RemoveLiquidity() { // allowance handling const { gatherPermitSignature, signatureData } = useV2LiquidityTokenPermit( parsedAmounts[Field.LIQUIDITY], - router?.address + router?.address, ) const [approval, approveCallback] = useApproveCallback(parsedAmounts[Field.LIQUIDITY], router?.address) @@ -157,20 +157,20 @@ function RemoveLiquidity() { (field: Field, typedValue: string) => { return _onUserInput(field, typedValue) }, - [_onUserInput] + [_onUserInput], ) const onLiquidityInput = useCallback( (typedValue: string): void => onUserInput(Field.LIQUIDITY, typedValue), - [onUserInput] + [onUserInput], ) const onCurrencyAInput = useCallback( (typedValue: string): void => onUserInput(Field.CURRENCY_A, typedValue), - [onUserInput] + [onUserInput], ) const onCurrencyBInput = useCallback( (typedValue: string): void => onUserInput(Field.CURRENCY_B, typedValue), - [onUserInput] + [onUserInput], ) // tx sending @@ -291,12 +291,12 @@ function RemoveLiquidity() { args, }) return undefined - }) - ) + }), + ), ) const indexOfSuccessfulEstimation = safeGasEstimates.findIndex((safeGasEstimate) => - BigNumber.isBigNumber(safeGasEstimate) + BigNumber.isBigNumber(safeGasEstimate), ) // all estimations failed... @@ -453,7 +453,7 @@ function RemoveLiquidity() { (value: number) => { onUserInput(Field.LIQUIDITY_PERCENT, value.toString()) }, - [onUserInput] + [onUserInput], ) const oneCurrencyIsETH = currencyA?.isNative || currencyB?.isNative @@ -463,7 +463,7 @@ function RemoveLiquidity() { account.chainId && WRAPPED_NATIVE_CURRENCY[account.chainId] && ((currencyA && WRAPPED_NATIVE_CURRENCY[account.chainId]?.equals(currencyA)) || - (currencyB && WRAPPED_NATIVE_CURRENCY[account.chainId]?.equals(currencyB))) + (currencyB && WRAPPED_NATIVE_CURRENCY[account.chainId]?.equals(currencyB))), ) const handleSelectCurrencyA = useCallback( @@ -474,7 +474,7 @@ function RemoveLiquidity() { navigate(`/remove/v2/${currencyId(currency)}/${currencyIdB}`) } }, - [currencyIdA, currencyIdB, navigate] + [currencyIdA, currencyIdB, navigate], ) const handleSelectCurrencyB = useCallback( (currency: Currency) => { @@ -484,7 +484,7 @@ function RemoveLiquidity() { navigate(`/remove/v2/${currencyIdA}/${currencyId(currency)}`) } }, - [currencyIdA, currencyIdB, navigate] + [currencyIdA, currencyIdB, navigate], ) const handleDismissConfirmation = useCallback(() => { @@ -498,7 +498,7 @@ function RemoveLiquidity() { const [innerLiquidityPercentage, setInnerLiquidityPercentage] = useDebouncedChangeHandler( Number.parseInt(parsedAmounts[Field.LIQUIDITY_PERCENT].toFixed(0)), - liquidityPercentChangeCallback + liquidityPercentChangeCallback, ) if (!networkSupportsV2) { diff --git a/apps/web/src/pages/RouteDefinitions.tsx b/apps/web/src/pages/RouteDefinitions.tsx index 4f9e997d91a..78b00997544 100644 --- a/apps/web/src/pages/RouteDefinitions.tsx +++ b/apps/web/src/pages/RouteDefinitions.tsx @@ -64,7 +64,7 @@ export function useRouterConfig(): RouterConfig { hash, shouldDisableNFTRoutes: Boolean(shouldDisableNFTRoutes), }), - [browserRouterEnabled, hash, shouldDisableNFTRoutes] + [browserRouterEnabled, hash, shouldDisableNFTRoutes], ) } diff --git a/apps/web/src/pages/Swap/Buy/BuyForm.tsx b/apps/web/src/pages/Swap/Buy/BuyForm.tsx index c9d9588ddb6..5d166a4fb3f 100644 --- a/apps/web/src/pages/Swap/Buy/BuyForm.tsx +++ b/apps/web/src/pages/Swap/Buy/BuyForm.tsx @@ -1,15 +1,12 @@ -import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' -import { ButtonLight, ButtonPrimary } from 'components/Button' import Column from 'components/Column' import Row from 'components/Row' -import { buildCurrencyInfo } from 'constants/routing' -import { nativeOnChain } from 'constants/tokens' -import { useAccount } from 'hooks/useAccount' import { useActiveLocalCurrencyComponents } from 'hooks/useActiveLocalCurrency' -import { useUSDTokenUpdater } from 'hooks/useUSDTokenUpdater' +import { BuyFormButton } from 'pages/Swap/Buy/BuyFormButton' +import { BuyFormContextProvider, ethCurrencyInfo, useBuyFormContext } from 'pages/Swap/Buy/BuyFormContext' +import { ChooseProviderModal } from 'pages/Swap/Buy/ChooseProviderModal' import { CountryListModal } from 'pages/Swap/Buy/CountryListModal' +import { FiatOnRampCurrencyModal } from 'pages/Swap/Buy/FiatOnRampCurrencyModal' import { PredefinedAmount } from 'pages/Swap/Buy/PredefinedAmount' -import { useMeldFiatCurrencyInfo } from 'pages/Swap/Buy/hooks' import { NumericalInputMimic, NumericalInputSymbolContainer, @@ -17,31 +14,26 @@ import { StyledNumericalInput, useWidthAdjustedDisplayValue, } from 'pages/Swap/common/shared' -import { useEffect, useMemo, useState } from 'react' +import { useEffect } from 'react' import { Trans } from 'react-i18next' import styled from 'styled-components' import { Text } from 'ui/src/components/text/Text' import { FiatOnRampCountryPicker } from 'uniswap/src/features/fiatOnRamp/FiatOnRampCountryPicker' import { SelectTokenButton } from 'uniswap/src/features/fiatOnRamp/SelectTokenButton' -import { - useFiatOnRampAggregatorCountryListQuery, - useFiatOnRampAggregatorGetCountryQuery, -} from 'uniswap/src/features/fiatOnRamp/api' -import { FORCountry, FiatOnRampCurrency } from 'uniswap/src/features/fiatOnRamp/types' +import { useFiatOnRampAggregatorGetCountryQuery } from 'uniswap/src/features/fiatOnRamp/api' import Trace from 'uniswap/src/features/telemetry/Trace' import { InterfacePageNameLocal } from 'uniswap/src/features/telemetry/constants' -import { UniverseChainId } from 'uniswap/src/types/chains' import useResizeObserver from 'use-resize-observer' import { NumberType, useFormatter } from 'utils/formatNumbers' const InputWrapper = styled(Column)` position: relative; background-color: ${({ theme }) => theme.surface2}; - padding: 16px; - height: 312px; + padding: 0 16px 40px 16px; + height: 342px; align-items: center; border-radius: 20px; - justify-content: center; + justify-content: flex-end; overflow: hidden; gap: 16px; ` @@ -56,78 +48,33 @@ const HeaderRow = styled(Row)` ` const PREDEFINED_AMOUNTS = [100, 300, 1000] -const ethCurrencyInfo = buildCurrencyInfo(nativeOnChain(UniverseChainId.Mainnet)) type BuyFormProps = { disabled?: boolean } -export function BuyForm({ disabled }: BuyFormProps) { - const { isConnected } = useAccount() - const accountDrawer = useAccountDrawer() +function BuyFormInner({ disabled }: BuyFormProps) { const { formatNumberOrString } = useFormatter() const { symbol: fiatSymbol } = useActiveLocalCurrencyComponents() - const [inputAmount, setInputAmount] = useState('') - const [quoteCurrency, setQuoteCurrency] = useState({ - currencyInfo: ethCurrencyInfo, - meldCurrencyCode: 'ETH', - }) + + const { buyFormState, setBuyFormState, derivedBuyFormInfo } = useBuyFormContext() + const { inputAmount, selectedCountry, quoteCurrency, currencyModalOpen, countryModalOpen, providerModalOpen } = + buyFormState + const { amountOut, supportedTokens, countryOptionsResult, error, notAvailableInThisRegion } = derivedBuyFormInfo + const postWidthAdjustedDisplayValue = useWidthAdjustedDisplayValue(inputAmount) const hiddenObserver = useResizeObserver() - const exactAmountOut = useUSDTokenUpdater(true /* inputInFiat */, inputAmount, quoteCurrency?.currencyInfo?.currency) const handleUserInput = (newValue: string) => { - setInputAmount(newValue) + setBuyFormState((state) => ({ ...state, inputAmount: newValue })) } - const [selectedCountry, setSelectedCountry] = useState() - const [countryModalOpen, setCountryModalOpen] = useState(false) const { data: countryResult } = useFiatOnRampAggregatorGetCountryQuery() useEffect(() => { if (!selectedCountry && countryResult) { - setSelectedCountry(countryResult) - } - }, [countryResult, selectedCountry]) - const { data: countryOptionsResult } = useFiatOnRampAggregatorCountryListQuery() - - const { notAvailableInThisRegion } = useMeldFiatCurrencyInfo(selectedCountry) - - const buyButtonState = useMemo(() => { - if (!isConnected) { - return { - label: , - disabled: false, - onClick: accountDrawer.open, - Component: ButtonLight, - } - } - if (!inputAmount) { - return { - label: , - disabled: true, - onClick: undefined, - Component: ButtonPrimary, - } + setBuyFormState((state) => ({ ...state, selectedCountry: countryResult })) } - - if (notAvailableInThisRegion) { - return { - label: , - disabled: true, - onClick: undefined, - Component: ButtonPrimary, - } - } - - return { - label: , - disabled: false, - Component: ButtonPrimary, - onClick: () => { - // TODO: open "choose FOR provider" modal - }, - } - }, [accountDrawer.open, inputAmount, isConnected, notAvailableInThisRegion]) + }, [buyFormState.selectedCountry, countryResult, selectedCountry, setBuyFormState]) return ( @@ -139,11 +86,16 @@ export function BuyForm({ disabled }: BuyFormProps) { { - setCountryModalOpen(true) + setBuyFormState((state) => ({ ...state, countryModalOpen: true })) }} countryCode={selectedCountry?.countryCode} /> + {error && ( + + {error.message} + + )} {fiatSymbol} { - // TODO: open currency selector modal and call setQuoteCurrency - setQuoteCurrency({ - currencyInfo: ethCurrencyInfo, - meldCurrencyCode: 'ETH', - }) + setBuyFormState((state) => ({ ...state, currencyModalOpen: true })) }} selectedCurrencyInfo={quoteCurrency.currencyInfo ?? ethCurrencyInfo} formattedAmount={formatNumberOrString({ - input: exactAmountOut || '0', + input: amountOut || '0', type: NumberType.TokenNonTx, })} - showCaret={false} disabled={disabled} + iconSize={18} + chevronDirection="down" + backgroundColor="$surface1" /> {PREDEFINED_AMOUNTS.map((amount: number) => ( { - setInputAmount(amount.toString()) + setBuyFormState((state) => ({ ...state, inputAmount: amount.toString() })) }} key={amount} amount={amount} @@ -185,20 +135,47 @@ export function BuyForm({ disabled }: BuyFormProps) { /> ))} + {notAvailableInThisRegion && ( + + + + )} - - {buyButtonState.label} - + + {supportedTokens && Boolean(supportedTokens?.length) && ( + { + setBuyFormState((state) => ({ ...state, currencyModalOpen: false })) + }} + onSelectCurrency={(currency) => { + setBuyFormState((state) => ({ ...state, quoteCurrency: currency })) + }} + currencies={supportedTokens} + /> + )} {countryOptionsResult?.supportedCountries && ( setSelectedCountry(selectedCountry)} + onSelectCountry={(selectedCountry) => setBuyFormState((state) => ({ ...state, selectedCountry }))} countryList={countryOptionsResult?.supportedCountries} isOpen={countryModalOpen} - onDismiss={() => setCountryModalOpen(false)} + onDismiss={() => setBuyFormState((state) => ({ ...state, countryModalOpen: false }))} selectedCountry={selectedCountry} /> )} + setBuyFormState((prev) => ({ ...prev, providerModalOpen: false }))} + /> ) } + +export function BuyForm(props: BuyFormProps) { + return ( + + + + ) +} diff --git a/apps/web/src/pages/Swap/Buy/BuyFormButton.tsx b/apps/web/src/pages/Swap/Buy/BuyFormButton.tsx new file mode 100644 index 00000000000..d566cd0e3fa --- /dev/null +++ b/apps/web/src/pages/Swap/Buy/BuyFormButton.tsx @@ -0,0 +1,70 @@ +import { useAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' +import { ButtonLight, ButtonPrimary } from 'components/Button' +import { useBuyFormContext } from 'pages/Swap/Buy/BuyFormContext' +import { useMemo } from 'react' +import { Trans } from 'react-i18next' +import { useAccount } from 'wagmi' + +interface BuyFormButtonProps { + forceDisabled?: boolean +} + +export function BuyFormButton({ forceDisabled }: BuyFormButtonProps) { + const account = useAccount() + const accountDrawer = useAccountDrawer() + + const { buyFormState, derivedBuyFormInfo, setBuyFormState } = useBuyFormContext() + const { inputAmount } = buyFormState + const { notAvailableInThisRegion, quotes, fetchingQuotes } = derivedBuyFormInfo + + const buyButtonState = useMemo(() => { + if (!account.isConnected) { + return { + label: , + disabled: false, + onClick: accountDrawer.open, + Component: ButtonLight, + } + } + if (!inputAmount || forceDisabled) { + return { + label: , + disabled: true, + onClick: undefined, + Component: ButtonPrimary, + } + } + + if (notAvailableInThisRegion) { + return { + label: , + disabled: true, + onClick: undefined, + Component: ButtonPrimary, + } + } + + return { + label: , + disabled: Boolean(fetchingQuotes || !quotes || !quotes.quotes || quotes.quotes.length === 0), + Component: ButtonPrimary, + onClick: () => { + setBuyFormState((prev) => ({ ...prev, providerModalOpen: true })) + }, + } + }, [ + account.isConnected, + inputAmount, + forceDisabled, + notAvailableInThisRegion, + fetchingQuotes, + quotes, + accountDrawer.open, + setBuyFormState, + ]) + return ( + + {buyButtonState.label} + + ) +} diff --git a/apps/web/src/pages/Swap/Buy/BuyFormContext.tsx b/apps/web/src/pages/Swap/Buy/BuyFormContext.tsx new file mode 100644 index 00000000000..40218e68f95 --- /dev/null +++ b/apps/web/src/pages/Swap/Buy/BuyFormContext.tsx @@ -0,0 +1,190 @@ +import { skipToken } from '@reduxjs/toolkit/query/react' +import { buildCurrencyInfo } from 'constants/routing' +import { nativeOnChain } from 'constants/tokens' +import { useUSDTokenUpdater } from 'hooks/useUSDTokenUpdater' +import { t } from 'i18next' +import { useFiatOnRampSupportedTokens, useMeldFiatCurrencyInfo } from 'pages/Swap/Buy/hooks' +import { Dispatch, PropsWithChildren, SetStateAction, createContext, useContext, useMemo, useState } from 'react' +import { + useFiatOnRampAggregatorCountryListQuery, + useFiatOnRampAggregatorCryptoQuoteQuery, +} from 'uniswap/src/features/fiatOnRamp/api' +import { + FORCountry, + FORQuoteResponse, + FORSupportedCountriesResponse, + FiatCurrencyInfo, + FiatOnRampCurrency, +} from 'uniswap/src/features/fiatOnRamp/types' +import { + InvalidRequestAmountTooLow, + isFiatOnRampApiError, + isInvalidRequestAmountTooHigh, + isInvalidRequestAmountTooLow, +} from 'uniswap/src/features/fiatOnRamp/utils' +import { UniverseChainId } from 'uniswap/src/types/chains' +import { NumberType, useFormatter } from 'utils/formatNumbers' +import { useAccount } from 'wagmi' + +class BuyFormError extends Error { + constructor(public readonly message: string) { + super(message) + } +} + +type BuyFormState = { + readonly inputAmount: string + readonly quoteCurrency: FiatOnRampCurrency + readonly selectedCountry?: FORCountry + readonly countryModalOpen: boolean + readonly currencyModalOpen: boolean + readonly providerModalOpen: boolean +} + +type BuyInfo = { + readonly meldSupportedFiatCurrency?: FiatCurrencyInfo + readonly notAvailableInThisRegion: boolean + readonly countryOptionsResult?: FORSupportedCountriesResponse + readonly supportedTokens?: FiatOnRampCurrency[] + readonly amountOut?: string + readonly quotes?: FORQuoteResponse + readonly fetchingQuotes: boolean + readonly error?: BuyFormError +} + +type BuyFormContextType = { + buyFormState: BuyFormState + setBuyFormState: Dispatch> + derivedBuyFormInfo: BuyInfo +} + +export const ethCurrencyInfo = buildCurrencyInfo(nativeOnChain(UniverseChainId.Mainnet)) +const DEFAULT_BUY_FORM_STATE: BuyFormState = { + inputAmount: '', + quoteCurrency: { + currencyInfo: ethCurrencyInfo, + meldCurrencyCode: 'ETH', + }, + selectedCountry: undefined, + countryModalOpen: false, + currencyModalOpen: false, + providerModalOpen: false, +} + +const BuyFormContext = createContext({ + buyFormState: DEFAULT_BUY_FORM_STATE, + setBuyFormState: () => undefined, + derivedBuyFormInfo: { + meldSupportedFiatCurrency: undefined, + notAvailableInThisRegion: false, + countryOptionsResult: undefined, + supportedTokens: [], + amountOut: undefined, + quotes: undefined, + fetchingQuotes: false, + error: undefined, + }, +}) + +export function useBuyFormContext() { + return useContext(BuyFormContext) +} + +function useDerivedBuyFormInfo(state: BuyFormState): BuyInfo { + const account = useAccount() + const amountOut = useUSDTokenUpdater( + true /* inputInFiat */, + state.inputAmount, + state.quoteCurrency?.currencyInfo?.currency, + ) + const { formatNumberOrString } = useFormatter() + + const { meldSupportedFiatCurrency, notAvailableInThisRegion } = useMeldFiatCurrencyInfo(state.selectedCountry) + const { data: countryOptionsResult } = useFiatOnRampAggregatorCountryListQuery() + const supportedTokens = useFiatOnRampSupportedTokens(meldSupportedFiatCurrency, state.selectedCountry?.countryCode) + + const { + data: quotes, + isFetching: fetchingQuotes, + error: quotesError, + } = useFiatOnRampAggregatorCryptoQuoteQuery( + state.inputAmount && + state.inputAmount !== '' && + account.address && + state.selectedCountry?.countryCode && + state.quoteCurrency && + meldSupportedFiatCurrency + ? { + sourceAmount: parseFloat(state.inputAmount), + sourceCurrencyCode: meldSupportedFiatCurrency.code, + destinationCurrencyCode: state.quoteCurrency.meldCurrencyCode ?? 'ETH', + countryCode: state.selectedCountry.countryCode, + walletAddress: account.address, + state: state.selectedCountry.state, + } + : skipToken, + { + refetchOnMountOrArgChange: true, + }, + ) + + const error = useMemo(() => { + if (quotesError && isFiatOnRampApiError(quotesError)) { + if (isInvalidRequestAmountTooLow(quotesError)) { + const formattedAmount = formatNumberOrString({ + input: (quotesError as InvalidRequestAmountTooLow).data.context.minimumAllowed, + type: NumberType.FiatTokenQuantity, + }) + return new BuyFormError(t(`fiatOnRamp.error.min`, { amount: formattedAmount })) + } + if (isInvalidRequestAmountTooHigh(quotesError)) { + const formattedAmount = formatNumberOrString({ + input: quotesError.data.context.maximumAllowed, + type: NumberType.FiatTokenQuantity, + }) + return new BuyFormError(t(`fiatOnRamp.error.max`, { amount: formattedAmount })) + } + return new BuyFormError(t('common.somethingWentWrong.error')) + } + return undefined + }, [formatNumberOrString, quotesError]) + + return useMemo( + () => ({ + amountOut, + notAvailableInThisRegion, + meldSupportedFiatCurrency, + supportedTokens, + countryOptionsResult, + quotes, + fetchingQuotes, + error, + }), + [ + amountOut, + countryOptionsResult, + error, + fetchingQuotes, + meldSupportedFiatCurrency, + notAvailableInThisRegion, + quotes, + supportedTokens, + ], + ) +} + +export function BuyFormContextProvider({ children }: PropsWithChildren) { + const [buyFormState, setBuyFormState] = useState({ ...DEFAULT_BUY_FORM_STATE }) + const derivedBuyFormInfo = useDerivedBuyFormInfo(buyFormState) + + const value = useMemo( + () => ({ + buyFormState, + setBuyFormState, + derivedBuyFormInfo, + }), + [buyFormState, derivedBuyFormInfo], + ) + + return {children} +} diff --git a/apps/web/src/pages/Swap/Buy/ChooseProviderModal.tsx b/apps/web/src/pages/Swap/Buy/ChooseProviderModal.tsx new file mode 100644 index 00000000000..8e2b323492b --- /dev/null +++ b/apps/web/src/pages/Swap/Buy/ChooseProviderModal.tsx @@ -0,0 +1,212 @@ +import { skipToken } from '@reduxjs/toolkit/query/react' +import GetHelpButton from 'components/Button/GetHelp' +import Column, { AutoColumn } from 'components/Column' +import Modal from 'components/Modal' +import Row, { RowBetween } from 'components/Row' +import { useAccount } from 'hooks/useAccount' +import { useBuyFormContext } from 'pages/Swap/Buy/BuyFormContext' +import { ContentWrapper } from 'pages/Swap/Buy/shared' +import { useEffect, useMemo, useState } from 'react' +import { Trans } from 'react-i18next' +import styled from 'styled-components' +import { CloseIcon } from 'theme/components' +import { Flex, useIsDarkMode } from 'ui/src' +import { FOR_CONNECTING_BACKGROUND_DARK, FOR_CONNECTING_BACKGROUND_LIGHT } from 'ui/src/assets' +import { Text } from 'ui/src/components/text/Text' +import { UNISWAP_WEB_URL, uniswapUrls } from 'uniswap/src/constants/urls' +import { FORQuoteItem } from 'uniswap/src/features/fiatOnRamp/FORQuoteItem' +import { FiatOnRampConnectingView } from 'uniswap/src/features/fiatOnRamp/FiatOnRampConnectingView' +import { useFiatOnRampAggregatorWidgetQuery } from 'uniswap/src/features/fiatOnRamp/api' +import { ServiceProviderLogoStyles } from 'uniswap/src/features/fiatOnRamp/constants' +import { FORQuote, FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types' +import { getOptionalServiceProviderLogo } from 'uniswap/src/features/fiatOnRamp/utils' +import { ONE_SECOND_MS } from 'utilities/src/time/time' +import { useTimeout } from 'utilities/src/time/timing' +import { v4 as uuid } from 'uuid' + +const ConnectingContainer = styled(Column)` + margin: 64px 0 0 0; + align-items: center; +` + +const ProviderListPaddedColumn = styled(AutoColumn)` + position: relative; + padding: 16px 24px 24px 24px; +` + +const ConnectedPaddedColumn = styled(AutoColumn)` + position: relative; + padding: 16px 24px 80px 24px; +` + +const ConnectingBackgroundImage = styled.img` + pointer-events: none; + position: absolute; + width: 100%; + height: 100%; + z-index: 0; + box-shadow: 0 0 12px 12px transparent inset; +` + +interface ChooseProviderModal { + isOpen: boolean + closeModal: () => void +} + +function ChooseProviderModalContent({ closeModal }: ChooseProviderModal) { + const { derivedBuyFormInfo, buyFormState } = useBuyFormContext() + const { quoteCurrency, selectedCountry, inputAmount } = buyFormState + const { quotes, meldSupportedFiatCurrency } = derivedBuyFormInfo + + const account = useAccount() + const isDarkMode = useIsDarkMode() + + const [selectedServiceProvider, setSelectedServiceProvider] = useState() + const [delayElapsed, setDelayElapsed] = useState(false) + + const widgetQueryParams = useMemo(() => { + return selectedServiceProvider && + quoteCurrency.meldCurrencyCode && + meldSupportedFiatCurrency && + inputAmount && + account.address && + selectedCountry?.countryCode + ? { + serviceProvider: selectedServiceProvider.serviceProvider, + countryCode: selectedCountry.countryCode, + destinationCurrencyCode: quoteCurrency.meldCurrencyCode, + sourceAmount: parseFloat(inputAmount), + sourceCurrencyCode: meldSupportedFiatCurrency.code, + walletAddress: account.address, + externalSessionId: uuid(), + redirectUrl: `${UNISWAP_WEB_URL}/buy`, + } + : skipToken + }, [ + account.address, + inputAmount, + meldSupportedFiatCurrency, + quoteCurrency.meldCurrencyCode, + selectedCountry?.countryCode, + selectedServiceProvider, + ]) + + const { data: widgetData } = useFiatOnRampAggregatorWidgetQuery(widgetQueryParams) + useTimeout(() => { + if (selectedServiceProvider && !delayElapsed) { + setDelayElapsed(true) + } + }, 2 * ONE_SECOND_MS) + + useEffect(() => { + if (selectedServiceProvider && delayElapsed && widgetData?.widgetUrl) { + window.open(widgetData.widgetUrl, '_blank') + } + }, [delayElapsed, selectedServiceProvider, widgetData?.widgetUrl]) + + // TODO(WEB-4332): handle errors from widget query + // TODO: add externalSessionId to local state for status fetching immediately when opening widget. + + if (selectedServiceProvider && delayElapsed) { + return ( + + + + + + + + + + + + + + + + + + ) + } + + if (selectedServiceProvider) { + return ( + + + + + + + + } + /> + + + + + + ) + } + + return ( + + + + + + + + + + + + + {quotes?.quotes?.map((q: FORQuote) => { + return ( + { + setSelectedServiceProvider(q.serviceProviderDetails) + }} + /> + ) + })} + + + + + ) +} + +export function ChooseProviderModal(props: ChooseProviderModal) { + return ( + + + + + + ) +} diff --git a/apps/web/src/pages/Swap/Buy/CountryListModal.test.tsx b/apps/web/src/pages/Swap/Buy/CountryListModal.test.tsx index 44894915c43..651e6fc921d 100644 --- a/apps/web/src/pages/Swap/Buy/CountryListModal.test.tsx +++ b/apps/web/src/pages/Swap/Buy/CountryListModal.test.tsx @@ -6,7 +6,7 @@ jest.mock( 'react-virtualized-auto-sizer', () => ({ children }: { children: any }) => - children({ width: 100, height: 100 }) + children({ width: 100, height: 100 }), ) describe('CountryListModal', () => { @@ -20,7 +20,7 @@ describe('CountryListModal', () => { onDismiss={closeHandler} onSelectCountry={selectHandler} selectedCountry={US} - /> + />, ) screen.getByText('United States').click() expect(selectHandler).toHaveBeenCalledWith(US) @@ -38,7 +38,7 @@ describe('CountryListModal', () => { onDismiss={closeHandler} onSelectCountry={selectHandler} selectedCountry={US} - /> + />, ) const closeButton = screen.getByTestId('CountryListModal-close') fireEvent(closeButton, new MouseEvent('click', { bubbles: true })) diff --git a/apps/web/src/pages/Swap/Buy/CountryListModal.tsx b/apps/web/src/pages/Swap/Buy/CountryListModal.tsx index cc8ff9fd46d..3adaabc444b 100644 --- a/apps/web/src/pages/Swap/Buy/CountryListModal.tsx +++ b/apps/web/src/pages/Swap/Buy/CountryListModal.tsx @@ -1,29 +1,19 @@ -import Column from 'components/Column' import Modal from 'components/Modal' import { RowBetween } from 'components/Row' import { scrollbarStyle } from 'components/SearchModal/CurrencyList/index.css' import { PaddedColumn, SearchInput } from 'components/SearchModal/styled' import { t } from 'i18next' import { CountryListRow } from 'pages/Swap/Buy/CountryListRow' +import { ContentWrapper } from 'pages/Swap/Buy/shared' import { ChangeEvent, useCallback, useMemo, useRef, useState } from 'react' import { Trans } from 'react-i18next' import AutoSizer from 'react-virtualized-auto-sizer' import { FixedSizeList } from 'react-window' -import styled from 'styled-components' import { CloseIcon } from 'theme/components' import { Text } from 'ui/src/components/text/Text' import { FORCountry } from 'uniswap/src/features/fiatOnRamp/types' import { bubbleToTop } from 'utilities/src/primitives/array' -const ContentWrapper = styled(Column)` - background-color: ${({ theme }) => theme.surface1}; - width: 100%; - overflow: hidden; - flex: 1 1; - position: relative; - border-radius: 20px; -` - const ROW_ITEM_SIZE = 56 interface CountryListModalProps { diff --git a/apps/web/src/pages/Swap/Buy/CountryListRow.test.tsx b/apps/web/src/pages/Swap/Buy/CountryListRow.test.tsx index 379e157f051..59eb5759bb0 100644 --- a/apps/web/src/pages/Swap/Buy/CountryListRow.test.tsx +++ b/apps/web/src/pages/Swap/Buy/CountryListRow.test.tsx @@ -6,7 +6,7 @@ describe('CountryListRow', () => { it('should render', () => { const clickHandler = jest.fn() const { container } = render( - + , ) screen.getByText('United States').click() expect(clickHandler).toHaveBeenCalledTimes(1) diff --git a/apps/web/src/pages/Swap/Buy/FiatOnRampCurrencyModal.tsx b/apps/web/src/pages/Swap/Buy/FiatOnRampCurrencyModal.tsx new file mode 100644 index 00000000000..4e8d27d424a --- /dev/null +++ b/apps/web/src/pages/Swap/Buy/FiatOnRampCurrencyModal.tsx @@ -0,0 +1,83 @@ +import Modal from 'components/Modal' +import { RowBetween } from 'components/Row' +import { CurrencyRow } from 'components/SearchModal/CurrencyList' +import { scrollbarStyle } from 'components/SearchModal/CurrencyList/index.css' +import { PaddedColumn } from 'components/SearchModal/styled' +import { ContentWrapper } from 'pages/Swap/Buy/shared' +import { CSSProperties } from 'react' +import { Trans } from 'react-i18next' +import AutoSizer from 'react-virtualized-auto-sizer' +import { FixedSizeList } from 'react-window' +import { CloseIcon } from 'theme/components' +import { Text } from 'ui/src/components/text/Text' +import { FiatOnRampCurrency } from 'uniswap/src/features/fiatOnRamp/types' + +const ROW_ITEM_SIZE = 56 + +interface FiatOnRampCurrencyModalProps { + isOpen: boolean + onDismiss: () => void + onSelectCurrency: (currency: FiatOnRampCurrency) => void + selectedCurrency?: FiatOnRampCurrency + currencies: FiatOnRampCurrency[] +} + +export function FiatOnRampCurrencyModal({ + isOpen, + onDismiss, + currencies, + selectedCurrency, + onSelectCurrency, +}: FiatOnRampCurrencyModalProps) { + return ( + + + + + + + + + + +
+ + {({ height }: { height: number }) => ( +
+ data[index]?.meldCurrencyCode ?? index} + > + {({ style, data, index }: { data: FiatOnRampCurrency[]; index: number; style: CSSProperties }) => { + const currencyInfo = data[index].currencyInfo + if (!currencyInfo) { + return null + } + return ( + { + onSelectCurrency(data[index]) + onDismiss() + }} + isSelected={selectedCurrency?.meldCurrencyCode === data[index].meldCurrencyCode} + eventProperties={{}} + otherSelected={false} + /> + ) + }} + +
+ )} +
+
+
+
+ ) +} diff --git a/apps/web/src/pages/Swap/Buy/PredefinedAmount.test.tsx b/apps/web/src/pages/Swap/Buy/PredefinedAmount.test.tsx index a60d4f322db..611cf975b91 100644 --- a/apps/web/src/pages/Swap/Buy/PredefinedAmount.test.tsx +++ b/apps/web/src/pages/Swap/Buy/PredefinedAmount.test.tsx @@ -12,12 +12,12 @@ describe('PredefinedAmount', () => { async (amount, currentAmount, disabled) => { const clickHandler = jest.fn() const { container } = render( - + , ) screen.getByText('$' + amount).click() expect(clickHandler).toHaveBeenCalledTimes(disabled ? 0 : 1) expect(container.firstChild).toMatchSnapshot() - } + }, ) }) diff --git a/apps/web/src/pages/Swap/Buy/hooks.ts b/apps/web/src/pages/Swap/Buy/hooks.ts index 616a2a90e5f..8b3704d514b 100644 --- a/apps/web/src/pages/Swap/Buy/hooks.ts +++ b/apps/web/src/pages/Swap/Buy/hooks.ts @@ -1,9 +1,13 @@ +import { meldSupportedCurrencyToCurrencyInfo } from 'graphql/data/types' import { useActiveLocalCurrency } from 'hooks/useActiveLocalCurrency' import { useActiveLocale } from 'hooks/useActiveLocale' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { useFiatOnRampAggregatorSupportedFiatCurrenciesQuery } from 'uniswap/src/features/fiatOnRamp/api' -import { FORCountry, FiatCurrencyInfo } from 'uniswap/src/features/fiatOnRamp/types' +import { + useFiatOnRampAggregatorSupportedFiatCurrenciesQuery, + useFiatOnRampAggregatorSupportedTokensQuery, +} from 'uniswap/src/features/fiatOnRamp/api' +import { FORCountry, FiatCurrencyInfo, FiatOnRampCurrency } from 'uniswap/src/features/fiatOnRamp/types' import { getFiatCurrencyComponents } from 'utilities/src/format/localeBased' import { getFiatCurrencyName } from 'utils/fiatCurrency' @@ -36,7 +40,7 @@ export function useMeldFiatCurrencyInfo(selectedCountry?: FORCountry): FiatOnRam const appFiatCurrencySupported = supportedFiatCurrencies && supportedFiatCurrencies.fiatCurrencies.some( - (currency): boolean => activeLocalCurrency.toLowerCase() === currency.fiatCurrencyCode.toLowerCase() + (currency): boolean => activeLocalCurrency.toLowerCase() === currency.fiatCurrencyCode.toLowerCase(), ) const meldSupportedFiatCurrency: FiatCurrencyInfo = useMemo(() => { const activeLocalCurrencyComponents = getFiatCurrencyComponents(activeLocale, activeLocalCurrency) @@ -55,3 +59,23 @@ export function useMeldFiatCurrencyInfo(selectedCountry?: FORCountry): FiatOnRam notAvailableInThisRegion: supportedFiatCurrencies?.fiatCurrencies?.length === 0, } } + +export function useFiatOnRampSupportedTokens( + fiatCurrency: FiatCurrencyInfo, + countryCode?: string, +): FiatOnRampCurrency[] { + const { data: quoteCurrencyOptions } = useFiatOnRampAggregatorSupportedTokensQuery({ + fiatCurrency: fiatCurrency.code, + countryCode: countryCode ?? 'US', + }) + + return useMemo(() => { + return ( + quoteCurrencyOptions?.supportedTokens?.map((currency) => { + const meldCurrencyCode = currency.cryptoCurrencyCode + const currencyInfo = meldSupportedCurrencyToCurrencyInfo(currency) + return { currencyInfo, meldCurrencyCode } + }) ?? [] + ) + }, [quoteCurrencyOptions?.supportedTokens]) +} diff --git a/apps/web/src/pages/Swap/Buy/shared.ts b/apps/web/src/pages/Swap/Buy/shared.ts new file mode 100644 index 00000000000..8546bea8d97 --- /dev/null +++ b/apps/web/src/pages/Swap/Buy/shared.ts @@ -0,0 +1,11 @@ +import Column from 'components/Column' +import styled from 'styled-components' + +export const ContentWrapper = styled(Column)` + background-color: ${({ theme }) => theme.surface1}; + width: 100%; + overflow: hidden; + flex: 1 1; + position: relative; + border-radius: 20px; +` diff --git a/apps/web/src/pages/Swap/Limit/LimitExpirySection.test.tsx b/apps/web/src/pages/Swap/Limit/LimitExpirySection.test.tsx index 79407ba2051..01d3bed2435 100644 --- a/apps/web/src/pages/Swap/Limit/LimitExpirySection.test.tsx +++ b/apps/web/src/pages/Swap/Limit/LimitExpirySection.test.tsx @@ -25,7 +25,7 @@ describe('LimitExpirySection', () => { const result = render( - + , ) expect(result.getByText('Expiry')).toBeInTheDocument() expect(result.getByText('1 day')).toBeInTheDocument() @@ -39,7 +39,7 @@ describe('LimitExpirySection', () => { const result = render( - + , ) result.getByText('1 month').click() expect(callback).toHaveBeenCalled() @@ -50,7 +50,7 @@ describe('LimitExpirySection', () => { const result = render( - + , ) result.getByText('1 day').click() expect(callback).not.toHaveBeenCalled() diff --git a/apps/web/src/pages/Swap/Limit/LimitForm.tsx b/apps/web/src/pages/Swap/Limit/LimitForm.tsx index 7f77e64f571..332fe7bb938 100644 --- a/apps/web/src/pages/Swap/Limit/LimitForm.tsx +++ b/apps/web/src/pages/Swap/Limit/LimitForm.tsx @@ -21,6 +21,7 @@ import { getChain, isUniswapXSupportedChain, useIsSupportedChainId } from 'const import { ZERO_PERCENT } from 'constants/misc' import { useAccount } from 'hooks/useAccount' import usePermit2Allowance, { AllowanceState } from 'hooks/usePermit2Allowance' +import useSelectChain from 'hooks/useSelectChain' import { SwapResult, useSwapCallback } from 'hooks/useSwapCallback' import { useUSDPrice } from 'hooks/useUSDPrice' import { Trans } from 'i18n' @@ -85,6 +86,7 @@ type LimitFormProps = { function LimitForm({ onCurrencyChange }: LimitFormProps) { const account = useAccount() + const selectChain = useSelectChain() const { chainId, currencyState: { inputCurrency, outputCurrency }, @@ -153,7 +155,7 @@ function LimitForm({ onCurrencyChange }: LimitFormProps) { isInputAmountFixed: type !== 'outputAmount', })) }, - [setLimitState] + [setLimitState], ) const switchTokens = useCallback(() => { @@ -193,28 +195,22 @@ function LimitForm({ onCurrencyChange }: LimitFormProps) { onCurrencyChange?.(newCurrencyState) setCurrencyState(newCurrencyState) }, - [inputCurrency, onCurrencyChange, outputCurrency, setCurrencyState, setLimitState, switchTokens] + [inputCurrency, onCurrencyChange, outputCurrency, setCurrencyState, setLimitState, switchTokens], ) useEffect(() => { - if (!outputCurrency && isSupportedChain && account.chainId) { - onSelectCurrency('outputCurrency', getChain({ chainId: account.chainId }).spotPriceStablecoinAmount.currency) + if (!outputCurrency && isSupportedChain) { + onSelectCurrency('outputCurrency', getChain({ chainId }).spotPriceStablecoinAmount.currency) } - }, [account.chainId, onSelectCurrency, outputCurrency, isSupportedChain]) + }, [onSelectCurrency, outputCurrency, isSupportedChain, chainId]) useEffect(() => { - if ( - isSupportedChain && - inputCurrency && - outputCurrency && - (inputCurrency.isNative || outputCurrency.isNative) && - account.chainId - ) { + if (isSupportedChain && inputCurrency && outputCurrency && (inputCurrency.isNative || outputCurrency.isNative)) { const [nativeCurrency, nonNativeCurrency] = inputCurrency.isNative ? [inputCurrency, outputCurrency] : [outputCurrency, inputCurrency] if (nativeCurrency.wrapped.equals(nonNativeCurrency)) { - onSelectCurrency('outputCurrency', getChain({ chainId: account.chainId }).spotPriceStablecoinAmount.currency) + onSelectCurrency('outputCurrency', getChain({ chainId }).spotPriceStablecoinAmount.currency) } } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -222,7 +218,7 @@ function LimitForm({ onCurrencyChange }: LimitFormProps) { const maxInputAmount: CurrencyAmount | undefined = useMemo( () => maxAmountSpend(currencyBalances[Field.INPUT]), - [currencyBalances] + [currencyBalances], ) const showMaxButton = Boolean(maxInputAmount?.greaterThan(0) && !parsedAmounts[Field.INPUT]?.equalTo(maxInputAmount)) @@ -235,8 +231,8 @@ function LimitForm({ onCurrencyChange }: LimitFormProps) { const allowance = usePermit2Allowance( parsedAmounts.INPUT?.currency?.isNative ? undefined : (parsedAmounts.INPUT as CurrencyAmount), - isSupportedChain && account.chainId ? UNIVERSAL_ROUTER_ADDRESS(account.chainId) : undefined, - TradeFillType.UniswapX + isSupportedChain ? UNIVERSAL_ROUTER_ADDRESS(chainId) : undefined, + TradeFillType.UniswapX, ) const fiatValueTradeInput = useUSDPrice(parsedAmounts.INPUT) @@ -287,7 +283,7 @@ function LimitForm({ onCurrencyChange }: LimitFormProps) { limitOrderTrade, fiatValues, ZERO_PERCENT, - allowance.state === AllowanceState.ALLOWED ? allowance.permitSignature : undefined + allowance.state === AllowanceState.ALLOWED ? allowance.permitSignature : undefined, ) const handleSubmit = useCallback(async () => { @@ -352,7 +348,13 @@ function LimitForm({ onCurrencyChange }: LimitFormProps) { {parsedLimitPrice && } { + handleContinueToReview={async () => { + if (chainId && chainId !== account.chainId) { + const didSwitchChain = await selectChain(chainId) + if (!didSwitchChain) { + return + } + } setShowConfirm(true) }} trade={limitOrderTrade} @@ -378,12 +380,9 @@ function LimitForm({ onCurrencyChange }: LimitFormProps) { /> )} - + - {!isUniswapXSupportedChain(account.chainId) ? ( + {!isUniswapXSupportedChain(chainId) ? ( @@ -441,8 +440,9 @@ function SubmitOrderButton({ }) { const accountDrawer = useAccountDrawer() const account = useAccount() + const { chainId } = useSwapAndLimitContext() - if (!isUniswapXSupportedChain(account.chainId)) { + if (!isUniswapXSupportedChain(chainId)) { return ( diff --git a/apps/web/src/pages/Swap/Limit/LimitPriceError.test.tsx b/apps/web/src/pages/Swap/Limit/LimitPriceError.test.tsx index 38a52f0ea17..d511f3a188e 100644 --- a/apps/web/src/pages/Swap/Limit/LimitPriceError.test.tsx +++ b/apps/web/src/pages/Swap/Limit/LimitPriceError.test.tsx @@ -17,7 +17,7 @@ describe('LimitPriceError', () => { priceInverted={inverted} priceAdjustmentPercentage={change} priceError={LimitPriceErrorType.BELOW_MARKET} - /> + />, ) expect(container.firstChild).toMatchSnapshot() }) @@ -30,7 +30,7 @@ describe('LimitPriceError', () => { priceInverted={false} priceAdjustmentPercentage={0} priceError={LimitPriceErrorType.CALCULATION_ERROR} - /> + />, ) expect(container.firstChild).toMatchSnapshot() }) diff --git a/apps/web/src/pages/Swap/Send/NewAddressSpeedBump.test.tsx b/apps/web/src/pages/Swap/Send/NewAddressSpeedBump.test.tsx index 56b4433f932..82a5f1c533b 100644 --- a/apps/web/src/pages/Swap/Send/NewAddressSpeedBump.test.tsx +++ b/apps/web/src/pages/Swap/Send/NewAddressSpeedBump.test.tsx @@ -35,7 +35,7 @@ describe('NewAddressSpeedBumpModal', () => { render( - + , ) expect(document.body).toMatchSnapshot() diff --git a/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.test.tsx b/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.test.tsx index 98d908ef0b2..c0baf071c9c 100644 --- a/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.test.tsx +++ b/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.test.tsx @@ -20,6 +20,7 @@ const mockSwapAndLimitContextValue = { setCurrencyState: jest.fn(), currentTab: SwapTab.Limit, setCurrentTab: jest.fn(), + isSwapAndLimitContext: true, } const mockedSendContextDefault: SendContextType = { @@ -75,7 +76,7 @@ describe('SendCurrencyInputform', () => { - + , ) expect(await screen.getByPlaceholderText('0')).toBeVisible() expect(screen.getByText('0 DAI')).toBeVisible() @@ -89,7 +90,7 @@ describe('SendCurrencyInputform', () => { - + , ) await waitFor(() => { expect(screen.getByDisplayValue('1000')).toBeVisible() @@ -105,7 +106,7 @@ describe('SendCurrencyInputform', () => { - + , ) await waitFor(() => { expect(screen.getByDisplayValue('1')).toBeVisible() diff --git a/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.tsx b/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.tsx index 3f78adbbf74..32744118766 100644 --- a/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.tsx +++ b/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.tsx @@ -208,7 +208,7 @@ export default function SendCurrencyInputForm({ const [tokenSelectorOpen, setTokenSelectorOpen] = useState(false) const fiatCurrency = useMemo( () => getChain({ chainId: supportedChain, withFallback: true }).spotPriceStablecoinAmount.currency, - [supportedChain] + [supportedChain], ) const fiatCurrencyEqualsTransferCurrency = !!inputCurrency && fiatCurrency.equals(inputCurrency) @@ -230,7 +230,7 @@ export default function SendCurrencyInputForm({ [inputInFiat ? 'exactAmountFiat' : 'exactAmountToken']: newValue, })) }, - [inputInFiat, setSendState] + [inputInFiat, setSendState], ) const handleSelectCurrency = useCallback( @@ -260,7 +260,7 @@ export default function SendCurrencyInputForm({ inputCurrency: currency, })) }, - [exactAmountFiat, exactAmountToken, fiatCurrency, inputInFiat, onCurrencyChange, setSendState] + [exactAmountFiat, exactAmountToken, fiatCurrency, inputInFiat, onCurrencyChange, setSendState], ) const toggleFiatInputAmountEnabled = useCallback(() => { @@ -296,7 +296,7 @@ export default function SendCurrencyInputForm({ () => ({ onlyShowCurrenciesWithBalance: account.isConnected, }), - [account.isConnected] + [account.isConnected], ) return ( diff --git a/apps/web/src/pages/Swap/Send/SendForm.tsx b/apps/web/src/pages/Swap/Send/SendForm.tsx index bc00bf2e1c8..b1c0895d2b6 100644 --- a/apps/web/src/pages/Swap/Send/SendForm.tsx +++ b/apps/web/src/pages/Swap/Send/SendForm.tsx @@ -5,8 +5,8 @@ import Column from 'components/Column' import { useIsSupportedChainId } from 'constants/chains' import { useAccount } from 'hooks/useAccount' import { useGroupedRecentTransfers } from 'hooks/useGroupedRecentTransfers' +import useSelectChain from 'hooks/useSelectChain' import { useSendCallback } from 'hooks/useSendCallback' -import { useSwitchChain } from 'hooks/useSwitchChain' import { Trans } from 'i18n' import { NewAddressSpeedBumpModal } from 'pages/Swap/Send/NewAddressSpeedBump' import SendCurrencyInputForm from 'pages/Swap/Send/SendCurrencyInputForm' @@ -20,7 +20,6 @@ import { CurrencyState } from 'state/swap/types' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import Trace from 'uniswap/src/features/telemetry/Trace' import { InterfacePageNameLocal } from 'uniswap/src/features/telemetry/constants' -import { didUserReject } from 'utils/swapErrorToUserReadableMessage' import { useIsSmartContractAddress } from 'utils/transfer' type SendFormProps = { @@ -76,7 +75,7 @@ enum SendSpeedBump { function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFormProps) { const account = useAccount() - const switchChain = useSwitchChain() + const selectChain = useSelectChain() const accountDrawer = useAccountDrawer() @@ -85,13 +84,13 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor [SendSpeedBump.NEW_ADDRESS_SPEED_BUMP]: false, [SendSpeedBump.SMART_CONTRACT_SPEED_BUMP]: false, }) - const { chainId, multichainUXEnabled } = useSwapAndLimitContext() + const { initialChainId, chainId, multichainUXEnabled } = useSwapAndLimitContext() const isSupportedChain = useIsSupportedChainId(chainId) const { setSendState, derivedSendInfo } = useSendContext() const { inputError, parsedTokenAmount, recipientData, transaction, gasFee } = derivedSendInfo const { isSmartContractAddress, loading: loadingSmartContractAddress } = useIsSmartContractAddress( - recipientData?.address + recipientData?.address, ) const { transfers: recentTransfers, loading: transfersLoading } = useGroupedRecentTransfers(account.address) const isRecentAddress = useMemo(() => { @@ -141,7 +140,7 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor handleModalState(SendFormModalState.REVIEW) }, - [handleModalState, sendFormSpeedBumpState] + [handleModalState, sendFormSpeedBumpState], ) const handleConfirmSmartContractSpeedBump = useCallback(() => { @@ -154,7 +153,7 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor }, [handleModalState, handleSendButton]) const handleCancelSmartContractSpeedBump = useCallback( () => handleModalState(SendFormModalState.None), - [handleModalState] + [handleModalState], ) const handleConfirmNewAddressSpeedBump = useCallback(() => { @@ -167,7 +166,7 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor }, [handleModalState, handleSendButton]) const handleCancelNewAddressSpeedBump = useCallback( () => handleModalState(SendFormModalState.None), - [handleModalState] + [handleModalState], ) const handleSend = useCallback(() => { @@ -201,25 +200,11 @@ function SendFormInner({ disableTokenInputs = false, onCurrencyChange }: SendFor - ) : !multichainUXEnabled && chainId && chainId !== account.chainId ? ( - { - try { - await switchChain(chainId) - } catch (error) { - if (didUserReject(error)) { - // Ignore error, which keeps the user on the previous chain. - } else { - // TODO(WEB-3306): This UX could be improved to show an error state. - throw error - } - } - }} - > + ) : !multichainUXEnabled && initialChainId && initialChainId !== account.chainId ? ( + await selectChain(initialChainId)}> ) : ( diff --git a/apps/web/src/pages/Swap/Send/SendRecipientForm.test.tsx b/apps/web/src/pages/Swap/Send/SendRecipientForm.test.tsx index 60c712770f8..3b733ab4506 100644 --- a/apps/web/src/pages/Swap/Send/SendRecipientForm.test.tsx +++ b/apps/web/src/pages/Swap/Send/SendRecipientForm.test.tsx @@ -16,6 +16,7 @@ const mockSwapAndLimitContextValue = { setCurrencyState: jest.fn(), currentTab: SwapTab.Limit, setCurrentTab: jest.fn(), + isSwapAndLimitContext: true, } const mockedSendContextDefault: SendContextType = { @@ -92,7 +93,7 @@ describe('SendCurrencyInputform', () => { - + , ) expect(screen.getByPlaceholderText('Wallet address or ENS name')).toBeVisible() expect(container.firstChild).toMatchSnapshot() @@ -104,7 +105,7 @@ describe('SendCurrencyInputform', () => { - + , ) expect(screen.getByDisplayValue('hayden.eth')).toBeVisible() expect(container.firstChild).toMatchSnapshot() @@ -116,7 +117,7 @@ describe('SendCurrencyInputform', () => { - + , ) expect(screen.getByText('hayden.eth')).toBeVisible() expect(screen.getByText(shortenAddress('0x9984b4b4E408e8D618A879e5315BD30952c89103'))).toBeVisible() @@ -129,7 +130,7 @@ describe('SendCurrencyInputform', () => { - + , ) expect(screen.getByText('hayden')).toBeVisible() expect(screen.getByText(shortenAddress('0x9984b4b4E408e8D618A879e5315BD30952c89103'))).toBeVisible() diff --git a/apps/web/src/pages/Swap/Send/SendRecipientForm.tsx b/apps/web/src/pages/Swap/Send/SendRecipientForm.tsx index a27d9c9fa08..4d7c2090099 100644 --- a/apps/web/src/pages/Swap/Send/SendRecipientForm.tsx +++ b/apps/web/src/pages/Swap/Send/SendRecipientForm.tsx @@ -83,7 +83,10 @@ const MenuFlyout = styled(AutoColumn)` width: calc(100% - 8px); background-color: ${({ theme }) => theme.surface2}; border: 1px solid ${({ theme }) => theme.surface3}; - box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04), + box-shadow: + 0px 0px 1px rgba(0, 0, 0, 0.01), + 0px 4px 8px rgba(0, 0, 0, 0.04), + 0px 16px 24px rgba(0, 0, 0, 0.04), 0px 24px 32px rgba(0, 0, 0, 0.01); border-radius: 12px; position: absolute; @@ -131,7 +134,7 @@ const AutocompleteRow = ({ ensName: cachedEnsName, unitag: unitag?.username, }), - [address, cachedEnsName, selectRecipient, unitag?.username] + [address, cachedEnsName, selectRecipient, unitag?.username], ) return ( @@ -243,7 +246,7 @@ export function SendRecipientForm({ disabled }: { disabled?: boolean }) { validatedRecipient: value, })) }, - [setSendState] + [setSendState], ) const handleInput = useCallback( @@ -256,7 +259,7 @@ export function SendRecipientForm({ disabled }: { disabled?: boolean }) { validatedRecipient: undefined, })) }, - [setSendState] + [setSendState], ) const selectValidatedRecipient = useCallback( @@ -268,7 +271,7 @@ export function SendRecipientForm({ disabled }: { disabled?: boolean }) { handleFocus(false) inputNode.current?.blur() }, - [handleFocus, handleInputValidatedRecipient, recipientData] + [handleFocus, handleInputValidatedRecipient, recipientData], ) const clearValidatedRecipient = useCallback( @@ -278,7 +281,7 @@ export function SendRecipientForm({ disabled }: { disabled?: boolean }) { handleForceFocus(true) handleInputValidatedRecipient(undefined) }, - [handleForceFocus, handleInputValidatedRecipient] + [handleForceFocus, handleInputValidatedRecipient], ) const editValidatedRecipient = useCallback(() => { @@ -294,7 +297,7 @@ export function SendRecipientForm({ disabled }: { disabled?: boolean }) { } } }, - [handleFocus, recipientData] + [handleFocus, recipientData], ) const showInputField = !recipientData || isFocusing || isForcingFocus diff --git a/apps/web/src/pages/Swap/Send/SendReviewModal.test.tsx b/apps/web/src/pages/Swap/Send/SendReviewModal.test.tsx index 93052b8ce61..61d92ef5b2f 100644 --- a/apps/web/src/pages/Swap/Send/SendReviewModal.test.tsx +++ b/apps/web/src/pages/Swap/Send/SendReviewModal.test.tsx @@ -19,6 +19,7 @@ const mockSwapAndLimitContextValue = { setCurrencyState: jest.fn(), currentTab: SwapTab.Limit, setCurrentTab: jest.fn(), + isSwapAndLimitContext: true, } const mockedSendContextFiatInput: SendContextType = { @@ -66,7 +67,7 @@ describe('SendCurrencyInputform', () => { - + , ) expect(screen.getByText('$1,000.00')).toBeVisible() expect(screen.getByText('100.00 DAI')).toBeVisible() @@ -82,7 +83,7 @@ describe('SendCurrencyInputform', () => { - + , ) expect(screen.getByText('$100.00')).toBeVisible() expect(screen.getByText('1.00 DAI')).toBeVisible() diff --git a/apps/web/src/pages/Swap/Send/SmartContractSpeedbump.test.tsx b/apps/web/src/pages/Swap/Send/SmartContractSpeedbump.test.tsx index 2a08f3ffc91..7a611ca0dfa 100644 --- a/apps/web/src/pages/Swap/Send/SmartContractSpeedbump.test.tsx +++ b/apps/web/src/pages/Swap/Send/SmartContractSpeedbump.test.tsx @@ -29,7 +29,7 @@ describe('SmartContractSpeedBumpModal', () => { render( - + , ) expect(document.body).toMatchSnapshot() diff --git a/apps/web/src/pages/Swap/SwapForm.tsx b/apps/web/src/pages/Swap/SwapForm.tsx index 2bbab954475..06f1d1994e5 100644 --- a/apps/web/src/pages/Swap/SwapForm.tsx +++ b/apps/web/src/pages/Swap/SwapForm.tsx @@ -21,7 +21,7 @@ import SwapDetailsDropdown from 'components/swap/SwapDetailsDropdown' import confirmPriceImpactWithoutFee from 'components/swap/confirmPriceImpactWithoutFee' import { Field } from 'components/swap/constants' import { ArrowContainer, ArrowWrapper, OutputSwapSection, SwapSection } from 'components/swap/styled' -import { useIsSupportedChainId } from 'constants/chains' +import { useIsSupportedChainId, useSupportedChainId } from 'constants/chains' import { useCurrencyInfo } from 'hooks/Tokens' import { useAccount } from 'hooks/useAccount' import { useIsSwapUnsupported } from 'hooks/useIsSwapUnsupported' @@ -78,8 +78,8 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF const trace = useTrace() - const { chainId, prefilledState, currencyState, multichainUXEnabled } = useSwapAndLimitContext() - const isSupportedChain = useIsSupportedChainId(chainId) + const { initialChainId, chainId, prefilledState, currencyState, multichainUXEnabled } = useSwapAndLimitContext() + const supportedChainId = useSupportedChainId(chainId) const { swapState, setSwapState, derivedSwapInfo } = useSwapContext() const { typedValue, independentField } = swapState @@ -96,14 +96,15 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF // dismiss warning if all imported tokens are in active lists const urlTokensNotInDefault = useMemo( () => - prefilledInputCurrencyInfo && prefilledOutputCurrencyInfo + prefilledInputCurrencyInfo || prefilledOutputCurrencyInfo ? [prefilledInputCurrencyInfo, prefilledOutputCurrencyInfo] - .filter((token: CurrencyInfo) => { - return token.currency.isToken && token.safetyLevel !== SafetyLevel.Verified - }) + .filter( + (token): token is CurrencyInfo => + (token?.currency.isToken && token.safetyLevel !== SafetyLevel.Verified) ?? false, + ) .map((token: CurrencyInfo) => token.currency as Token) : [], - [prefilledInputCurrencyInfo, prefilledOutputCurrencyInfo] + [prefilledInputCurrencyInfo, prefilledOutputCurrencyInfo], ) const theme = useTheme() @@ -125,7 +126,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF const [inputTokenHasTax, outputTokenHasTax] = useMemo( () => [!inputTax.equalTo(0), !outputTax.equalTo(0)], - [inputTax, outputTax] + [inputTax, outputTax], ) useEffect(() => { @@ -157,7 +158,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF [Field.INPUT]: independentField === Field.INPUT ? parsedAmount : trade?.inputAmount, [Field.OUTPUT]: independentField === Field.OUTPUT ? parsedAmount : trade?.outputAmount, }, - [independentField, parsedAmount, showWrap, trade] + [independentField, parsedAmount, showWrap, trade], ) const showFiatValueInput = Boolean(parsedAmounts[Field.INPUT]) @@ -171,11 +172,11 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF const fiatValueInput = useUSDPrice( parsedAmounts[Field.INPUT] ?? getSingleUnitAmount(currencies[Field.INPUT]), - currencies[Field.INPUT] + currencies[Field.INPUT], ) const fiatValueOutput = useUSDPrice( parsedAmounts[Field.OUTPUT] ?? getSingleUnitAmount(currencies[Field.OUTPUT]), - currencies[Field.OUTPUT] + currencies[Field.OUTPUT], ) const [routeNotFound, routeIsLoading, routeIsSyncing] = useMemo( @@ -184,7 +185,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF tradeState === TradeState.LOADING, tradeState === TradeState.LOADING && Boolean(trade), ], - [trade, tradeState] + [trade, tradeState], ) const fiatValueTradeInput = useUSDPrice(trade?.inputAmount) @@ -198,7 +199,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF computeFiatValuePriceImpact(fiatValueTradeInput.data, fiatValueTradeOutput.data), computeFiatValuePriceImpact(fiatValueTradeInput.data, preTaxFiatValueTradeOutput.data), ], - [fiatValueTradeInput, fiatValueTradeOutput, preTaxFiatValueTradeOutput, routeIsSyncing, trade, showWrap] + [fiatValueTradeInput, fiatValueTradeOutput, preTaxFiatValueTradeOutput, routeIsSyncing, trade, showWrap], ) const { onSwitchTokens, onCurrencySelection, onUserInput } = useSwapActionHandlers() @@ -209,14 +210,14 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF onUserInput(Field.INPUT, value) maybeLogFirstSwapAction(trace) }, - [onUserInput, trace] + [onUserInput, trace], ) const handleTypeOutput = useCallback( (value: string) => { onUserInput(Field.OUTPUT, value) maybeLogFirstSwapAction(trace) }, - [onUserInput, trace] + [onUserInput, trace], ) const navigate = useNavigate() @@ -284,11 +285,13 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF placeholder: '', }), }), - [dependentField, formatCurrencyAmount, independentField, parsedAmounts, showWrap, typedValue] + [dependentField, formatCurrencyAmount, independentField, parsedAmounts, showWrap, typedValue], ) + const selectChain = useSelectChain() + const userHasSpecifiedInputOutput = Boolean( - currencies[Field.INPUT] && currencies[Field.OUTPUT] && parsedAmounts[independentField]?.greaterThan(JSBI.BigInt(0)) + currencies[Field.INPUT] && currencies[Field.OUTPUT] && parsedAmounts[independentField]?.greaterThan(JSBI.BigInt(0)), ) const maximumAmountIn = useMaxAmountIn(trade, allowedSlippage) @@ -297,13 +300,13 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF (parsedAmounts[Field.INPUT]?.currency.isToken ? (parsedAmounts[Field.INPUT] as CurrencyAmount) : undefined), - isSupportedChain ? UNIVERSAL_ROUTER_ADDRESS(chainId) : undefined, - trade?.fillType + supportedChainId ? UNIVERSAL_ROUTER_ADDRESS(supportedChainId) : undefined, + trade?.fillType, ) const maxInputAmount: CurrencyAmount | undefined = useMemo( () => maxAmountSpend(currencyBalances[Field.INPUT]), - [currencyBalances] + [currencyBalances], ) const showMaxButton = Boolean(maxInputAmount?.greaterThan(0) && !parsedAmounts[Field.INPUT]?.equalTo(maxInputAmount)) const swapFiatValues = useMemo(() => { @@ -315,7 +318,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF trade, swapFiatValues, allowedSlippage, - allowance.state === AllowanceState.ALLOWED ? allowance.permitSignature : undefined + allowance.state === AllowanceState.ALLOWED ? allowance.permitSignature : undefined, ) const handleContinueToReview = useCallback(() => { @@ -363,7 +366,14 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF if (!onWrap) { return } + try { + if (supportedChainId && connectedChainId !== chainId) { + const correctChain = await selectChain(supportedChainId) + if (!correctChain) { + return + } + } const txHash = await onWrap() setSwapFormState((currentState) => ({ ...currentState, @@ -387,7 +397,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF txHash: undefined, })) } - }, [currencies, onUserInput, onWrap, wrapType]) + }, [currencies, onUserInput, onWrap, wrapType, connectedChainId, chainId, supportedChainId, selectChain]) // warnings on the greater of fiat value price impact and execution price impact const { priceImpactSeverity, largerPriceImpact } = useMemo(() => { @@ -423,7 +433,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF }) maybeLogFirstSwapAction(trace) }, - [onCurrencyChange, onCurrencySelection, currencyState, trace] + [onCurrencyChange, onCurrencySelection, currencyState, trace], ) const inputCurrencyNumericalInputRef = useRef(null) @@ -441,7 +451,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF }) maybeLogFirstSwapAction(trace) }, - [onCurrencyChange, onCurrencySelection, currencyState, trace] + [onCurrencyChange, onCurrencySelection, currencyState, trace], ) const showPriceImpactWarning = isClassicTrade(trade) && largerPriceImpact && priceImpactSeverity > 3 @@ -459,11 +469,10 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF }, [prevTrade, trade, trace, allowedSlippage, swapQuoteLatency, outputFeeFiatValue]) const showDetailsDropdown = Boolean( - !showWrap && userHasSpecifiedInputOutput && (trade || routeIsLoading || routeIsSyncing) + !showWrap && userHasSpecifiedInputOutput && (trade || routeIsLoading || routeIsSyncing), ) const inputCurrency = currencies[Field.INPUT] ?? undefined - const selectChain = useSelectChain() const switchingChain = useAppSelector((state) => state.wallets.switchingChain) const targetChain = switchingChain ? switchingChain : undefined @@ -539,7 +548,7 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF /> - + - ) : !multichainUXEnabled && chainId && chainId !== connectedChainId ? ( - { - try { - await selectChain(chainId) - } catch (error) { - if (didUserReject(error)) { - // Ignore error, which keeps the user on the previous chain. - } else { - // TODO(WEB-3306): This UX could be improved to show an error state. - throw error - } - } - }} - > + ) : !multichainUXEnabled && initialChainId && initialChainId !== connectedChainId ? ( + await selectChain(initialChainId)}> ) : showWrap ? ( @@ -675,10 +670,13 @@ export function SwapForm({ disableTokenInputs = false, onCurrencyChange }: SwapF { const inputChainId = trade?.inputAmount?.currency?.chainId + let correctChain = true if (inputChainId && inputChainId !== connectedChainId) { - await selectChain(inputChainId) + correctChain = await selectChain(inputChainId) + } + if (correctChain) { + showPriceImpactWarning ? setShowPriceImpactModal(true) : handleContinueToReview() } - showPriceImpactWarning ? setShowPriceImpactModal(true) : handleContinueToReview() }} id="swap-button" data-testid="swap-button" diff --git a/apps/web/src/pages/Swap/index.tsx b/apps/web/src/pages/Swap/index.tsx index 21c4512cf72..4b4bb35723a 100644 --- a/apps/web/src/pages/Swap/index.tsx +++ b/apps/web/src/pages/Swap/index.tsx @@ -29,7 +29,7 @@ import { SwapTab } from 'uniswap/src/types/screens/interface' export function getIsReviewableQuote( trade: InterfaceTrade | undefined, tradeState: TradeState, - swapInputError?: ReactNode + swapInputError?: ReactNode, ): boolean { if (swapInputError) { return false @@ -46,15 +46,16 @@ export default function SwapPage({ className }: { className?: string }) { const location = useLocation() const multichainUXEnabled = useFeatureFlag(FeatureFlags.MultichainUX) - const { initialInputCurrency, initialOutputCurrency, chainId } = useInitialCurrencyState() - const shouldDisableTokenInputs = useSupportedChainId(useAccount().chainId) === undefined + const { initialInputCurrency, initialOutputCurrency, initialChainId } = useInitialCurrencyState() + const isUnsupportedConnectedChain = useSupportedChainId(useAccount().chainId) === undefined + const shouldDisableTokenInputs = multichainUXEnabled ? false : isUnsupportedConnectedChain return ( , tokenAddress: string, currencyChainId: InterfaceChainId, - isNative: boolean + isNative: boolean, ) { const { chainId } = useAccount() const appChainId = chainId ?? UniverseChainId.Mainnet @@ -110,7 +110,7 @@ function useCreateTDPContext(): PendingTDPContext | LoadedTDPContext { tokenQuery, tokenAddress, currencyChainInfo.id, - isNative + isNative, ) const warning = useTokenWarning(tokenAddress, currencyChainInfo.id) @@ -123,7 +123,7 @@ function useCreateTDPContext(): PendingTDPContext | LoadedTDPContext { useSrcColor( extractedColorSrc, tokenQuery.data?.token?.project?.name ?? tokenQuery.data?.token?.name, - theme.surface2 + theme.surface2, ).tokenColor ?? undefined return useMemo(() => { diff --git a/apps/web/src/pages/Vote/Landing.tsx b/apps/web/src/pages/Vote/Landing.tsx index bd10bf78585..90d17f8a05d 100644 --- a/apps/web/src/pages/Vote/Landing.tsx +++ b/apps/web/src/pages/Vote/Landing.tsx @@ -140,13 +140,13 @@ export default function Landing() { const { loading: loadingAvailableVotes, votes: availableVotes } = useUserVotes() const uniBalance: CurrencyAmount | undefined = useTokenBalance( account.address, - account.chainId ? UNI[account.chainId] : undefined + account.chainId ? UNI[account.chainId] : undefined, ) const userDelegatee: string | undefined = useUserDelegatee() // show delegation option if they have have a balance, but have not delegated const showUnlockVoting = Boolean( - uniBalance && JSBI.notEqual(uniBalance.quotient, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS + uniBalance && JSBI.notEqual(uniBalance.quotient, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS, ) return ( <> diff --git a/apps/web/src/pages/Vote/VotePage.tsx b/apps/web/src/pages/Vote/VotePage.tsx index 432cb3e45b5..a8e9b691882 100644 --- a/apps/web/src/pages/Vote/VotePage.tsx +++ b/apps/web/src/pages/Vote/VotePage.tsx @@ -147,14 +147,14 @@ function getDateFromBlock( targetBlock: number | undefined, currentBlock: number | undefined, averageBlockTimeInSeconds: number | undefined, - currentTimestamp: BigNumber | undefined + currentTimestamp: BigNumber | undefined, ): Date | undefined { if (targetBlock && currentBlock && averageBlockTimeInSeconds && currentTimestamp) { const date = new Date() date.setTime( currentTimestamp .add(BigNumber.from(averageBlockTimeInSeconds).mul(BigNumber.from(targetBlock - currentBlock))) - .toNumber() * ms(`1s`) + .toNumber() * ms(`1s`), ) return date } @@ -199,13 +199,13 @@ export default function VotePage() { proposalData?.startBlock, currentBlock, (account.chainId && AVERAGE_BLOCK_TIME_IN_SECS[account.chainId]) ?? DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS, - currentTimestamp + currentTimestamp, ) const endDate = getDateFromBlock( proposalData?.endBlock, currentBlock, (account.chainId && AVERAGE_BLOCK_TIME_IN_SECS[account.chainId]) ?? DEFAULT_AVERAGE_BLOCK_TIME_IN_SECS, - currentTimestamp + currentTimestamp, ) const now = new Date() const locale = useActiveLocale() @@ -245,13 +245,13 @@ export default function VotePage() { const uniBalance: CurrencyAmount | undefined = useTokenBalance( account.address, - account.chainId ? UNI[account.chainId] : undefined + account.chainId ? UNI[account.chainId] : undefined, ) const userDelegatee: string | undefined = useUserDelegatee() // in blurb link to home page if they are able to unlock const showLinkForUnlock = Boolean( - uniBalance && JSBI.notEqual(uniBalance.quotient, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS + uniBalance && JSBI.notEqual(uniBalance.quotient, JSBI.BigInt(0)) && userDelegatee === ZERO_ADDRESS, ) // show links in propsoal details if content is an address diff --git a/apps/web/src/pages/getExploreTitle.ts b/apps/web/src/pages/getExploreTitle.ts index edf3aba75f7..68f73f213b5 100644 --- a/apps/web/src/pages/getExploreTitle.ts +++ b/apps/web/src/pages/getExploreTitle.ts @@ -24,6 +24,6 @@ export const getExploreDescription = (path?: string) => { `Discover and research tokens on {{network}}. Explore top pools. View real-time prices, trading volume, TVL, charts, and transaction data.`, { network: capitalize(network), - } + }, ) } diff --git a/apps/web/src/pages/metatags.ts b/apps/web/src/pages/metatags.ts index 1b66255054d..229aced489a 100644 --- a/apps/web/src/pages/metatags.ts +++ b/apps/web/src/pages/metatags.ts @@ -32,7 +32,7 @@ export function useDynamicMetatags(metaTags: MetaTagInjectorInput = DEFAULT_META if (metaTags.description) { attributes.push( { property: 'og:description', content: metaTags.description }, - { name: 'description', content: metaTags.description } + { name: 'description', content: metaTags.description }, ) } if (metaTags.image) { @@ -40,7 +40,7 @@ export function useDynamicMetatags(metaTags: MetaTagInjectorInput = DEFAULT_META { property: 'og:image', content: metaTags.image }, { property: 'og:image:alt', content: metaTags.title }, { property: 'twitter:image', content: metaTags.image }, - { property: 'twitter:image:alt', content: metaTags.title } + { property: 'twitter:image:alt', content: metaTags.title }, ) } setMetaTagAttributes(attributes) diff --git a/apps/web/src/rpc/AppJsonRpcProvider.ts b/apps/web/src/rpc/AppJsonRpcProvider.ts index b3817a34dcc..f62d8094920 100644 --- a/apps/web/src/rpc/AppJsonRpcProvider.ts +++ b/apps/web/src/rpc/AppJsonRpcProvider.ts @@ -66,7 +66,7 @@ export default class AppJsonRpcProvider extends ConfiguredJsonRpcProvider { constructor( providers: JsonRpcProvider[], - { minimumBackoffTime = AVERAGE_L1_BLOCK_TIME }: AppJsonRpcProviderOptions = {} + { minimumBackoffTime = AVERAGE_L1_BLOCK_TIME }: AppJsonRpcProviderOptions = {}, ) { if (providers.length === 0) { throw new Error('Missing providers for AppJsonRpcProvider') diff --git a/apps/web/src/rpc/ConfiguredJsonRpcProvider.ts b/apps/web/src/rpc/ConfiguredJsonRpcProvider.ts index 93273bd4ffe..99c900d5234 100644 --- a/apps/web/src/rpc/ConfiguredJsonRpcProvider.ts +++ b/apps/web/src/rpc/ConfiguredJsonRpcProvider.ts @@ -7,7 +7,7 @@ export default class ConfiguredJsonRpcProvider extends StaticJsonRpcProvider { url: string | undefined, // Including networkish allows ethers to skip the initial detectNetwork call. networkish: Networkish & { chainId: SupportedInterfaceChainId }, - pollingInterval = AVERAGE_L1_BLOCK_TIME + pollingInterval = AVERAGE_L1_BLOCK_TIME, ) { super(url, networkish) diff --git a/apps/web/src/setupRive.ts b/apps/web/src/setupRive.ts new file mode 100644 index 00000000000..cece0657daf --- /dev/null +++ b/apps/web/src/setupRive.ts @@ -0,0 +1,5 @@ +// @ts-ignore this file is here but ts doesn't like it +import riveWASMResource from '@rive-app/canvas/rive.wasm' +import { RuntimeLoader } from '@rive-app/react-canvas' + +RuntimeLoader.setWasmUrl(riveWASMResource) diff --git a/apps/web/src/setupTests.ts b/apps/web/src/setupTests.ts index 782577901ef..3dee88aeb99 100644 --- a/apps/web/src/setupTests.ts +++ b/apps/web/src/setupTests.ts @@ -83,7 +83,7 @@ jest.mock('@web3-react/core', () => { ...web3React, initializeConnector: () => web3React.initializeConnector( - (actions: Parameters[0]) => new Empty(actions) + (actions: Parameters[0]) => new Empty(actions), ), useWeb3React: jest.fn(), } diff --git a/apps/web/src/shared-cloud/metatags.ts b/apps/web/src/shared-cloud/metatags.ts index 154e6a687ae..de04e1212bf 100644 --- a/apps/web/src/shared-cloud/metatags.ts +++ b/apps/web/src/shared-cloud/metatags.ts @@ -18,7 +18,7 @@ export function formatTokenMetatagTitleName(symbol: string | undefined, name: st export function formatNFTAssetMetatagTitleName( name: string | undefined, collectionName: string | undefined, - tokenId: string + tokenId: string, ) { if (name) { return name diff --git a/apps/web/src/state/activity/polling/orders.ts b/apps/web/src/state/activity/polling/orders.ts index 81320dc4287..48e23cc6a3b 100644 --- a/apps/web/src/state/activity/polling/orders.ts +++ b/apps/web/src/state/activity/polling/orders.ts @@ -21,7 +21,7 @@ if (UNISWAP_GATEWAY_DNS_URL === undefined) { async function fetchStatuses( orders: UniswapXOrderDetails[], filter: (order: UniswapXOrderDetails) => boolean, - path: (hashes: string[]) => string + path: (hashes: string[]) => string, ): Promise { const hashes = orders.filter(filter).map((order) => order.orderHash) if (!hashes || hashes.length === 0) { @@ -37,7 +37,7 @@ async function fetchLimitStatuses(account: string, orders: UniswapXOrderDetails[ return fetchStatuses( orders, (order) => order.type === SignatureType.SIGN_LIMIT, - (hashes) => `/limit-orders?swapper=${account}&orderHashes=${hashes}` + (hashes) => `/limit-orders?swapper=${account}&orderHashes=${hashes}`, ) } @@ -45,7 +45,7 @@ async function fetchOrderStatuses(account: string, orders: UniswapXOrderDetails[ return fetchStatuses( orders, (order) => order.type === SignatureType.SIGN_UNISWAPX_ORDER || order.type === SignatureType.SIGN_UNISWAPX_V2_ORDER, - (hashes) => `/orders?swapper=${account}&orderHashes=${hashes}&orderType=${OffchainOrderType.DUTCH_V1_AND_V2}` + (hashes) => `/orders?swapper=${account}&orderHashes=${hashes}&orderType=${OffchainOrderType.DUTCH_V1_AND_V2}`, ) } diff --git a/apps/web/src/state/activity/polling/retry.ts b/apps/web/src/state/activity/polling/retry.ts index b3dcf47f403..9b3b5fefedb 100644 --- a/apps/web/src/state/activity/polling/retry.ts +++ b/apps/web/src/state/activity/polling/retry.ts @@ -28,7 +28,7 @@ export class RetryableError extends Error { */ export function retry( fn: () => Promise, - { n, minWait, maxWait }: RetryOptions + { n, minWait, maxWait }: RetryOptions, ): { promise: Promise; cancel: () => void } { let completed = false let rejectCancelled: (error: Error) => void diff --git a/apps/web/src/state/activity/polling/transactions.ts b/apps/web/src/state/activity/polling/transactions.ts index 1c77aa10cbb..6328a7fe82f 100644 --- a/apps/web/src/state/activity/polling/transactions.ts +++ b/apps/web/src/state/activity/polling/transactions.ts @@ -77,7 +77,7 @@ export function usePollPendingTransactions(onActivityUpdate: OnActivityUpdate) { account.chainId && (SUBSCRIPTION_CHAINIDS as unknown as InterfaceChainId[]).includes(account.chainId) ? undefined - : account.chainId + : account.chainId, ) const supportedChain = useSupportedChainId(account.chainId) const hasPending = pendingTransactions.length > 0 @@ -114,10 +114,10 @@ export function usePollPendingTransactions(onActivityUpdate: OnActivityUpdate) { } return receipt }), - retryOptions + retryOptions, ) }, - [account.isConnected, blockTimestamp, provider, removeTransaction, supportedChain] + [account.isConnected, blockTimestamp, provider, removeTransaction, supportedChain], ) useEffect(() => { diff --git a/apps/web/src/state/activity/subscription.ts b/apps/web/src/state/activity/subscription.ts index 3dfc47aa1e8..50fd03625f5 100644 --- a/apps/web/src/state/activity/subscription.ts +++ b/apps/web/src/state/activity/subscription.ts @@ -27,7 +27,7 @@ export function useOnAssetActivity(onActivityUpdate: OnActivityUpdate) { onTransactionActivity(activity as TransactionActivity) } }, - [onOrderActivity, onTransactionActivity] + [onOrderActivity, onTransactionActivity], ) const result = useAssetActivitySubscription() @@ -51,7 +51,7 @@ function useOnOrderActivity(onActivityUpdate: OnActivityUpdate) { update: updatedOrder, }) }, - [onActivityUpdate] + [onActivityUpdate], ) } @@ -68,7 +68,7 @@ function useOnTransactionActivity(onActivityUpdate: OnActivityUpdate) { } const pendingTransaction = pendingTransactions.current?.find( - ([tx, txChainId]) => tx.hash === activity.details.hash && txChainId === chainId + ([tx, txChainId]) => tx.hash === activity.details.hash && txChainId === chainId, )?.[0] // TODO(WEB-4007): Add transactions which were submitted from a different client (and are not already tracked). if (!pendingTransaction) { @@ -82,7 +82,7 @@ function useOnTransactionActivity(onActivityUpdate: OnActivityUpdate) { if (updatedTransaction.info.type === TransactionType.SWAP) { if (updatedTransaction.info.tradeType === TradeType.EXACT_INPUT) { const change = activity.details.assetChanges.find( - (change) => change?.__typename === 'TokenTransfer' && change?.direction === TransactionDirection.Out + (change) => change?.__typename === 'TokenTransfer' && change?.direction === TransactionDirection.Out, ) as TokenTransfer if (change.asset.decimals && change.quantity) { // The quantity is returned as a decimal string, but the state expects a BigInt-compatible string. @@ -101,6 +101,6 @@ function useOnTransactionActivity(onActivityUpdate: OnActivityUpdate) { update: updatedTransaction, }) }, - [onActivityUpdate] + [onActivityUpdate], ) } diff --git a/apps/web/src/state/activity/updater.tsx b/apps/web/src/state/activity/updater.tsx index b9fd6f64e21..ca2bcdae589 100644 --- a/apps/web/src/state/activity/updater.tsx +++ b/apps/web/src/state/activity/updater.tsx @@ -85,6 +85,6 @@ function useOnActivityUpdate(): OnActivityUpdate { } } }, - [addPopup, analyticsContext, dispatch] + [addPopup, analyticsContext, dispatch], ) } diff --git a/apps/web/src/state/application/hooks.ts b/apps/web/src/state/application/hooks.ts index 81ae98e6a92..10a1c271876 100644 --- a/apps/web/src/state/application/hooks.ts +++ b/apps/web/src/state/application/hooks.ts @@ -110,7 +110,7 @@ export function useCloseModal() { dispatch(setOpenModal(null)) } }, - [currentlyOpenModal, dispatch] + [currentlyOpenModal, dispatch], ) } @@ -162,7 +162,7 @@ export function useAddPopup(): (content: PopupContent, key?: string, removeAfter (content: PopupContent, key?: string, removeAfterMs?: number) => { dispatch(addPopup({ content, key, removeAfterMs: removeAfterMs ?? DEFAULT_TXN_DISMISS_MS })) }, - [dispatch] + [dispatch], ) } @@ -173,7 +173,7 @@ export function useRemovePopup(): (key: string) => void { (key: string) => { dispatch(removePopup({ key })) }, - [dispatch] + [dispatch], ) } diff --git a/apps/web/src/state/burn/hooks.tsx b/apps/web/src/state/burn/hooks.tsx index 6bec61ad771..6f710e433ca 100644 --- a/apps/web/src/state/burn/hooks.tsx +++ b/apps/web/src/state/burn/hooks.tsx @@ -18,7 +18,7 @@ export function useBurnState(): AppState['burn'] { export function useDerivedBurnInfo( currencyA: Currency | undefined, - currencyB: Currency | undefined + currencyB: Currency | undefined, ): { pair?: Pair | null parsedAmounts: { @@ -107,7 +107,7 @@ export function useDerivedBurnInfo( userLiquidity && percentToRemove && percentToRemove.greaterThan('0') ? CurrencyAmount.fromRawAmount( userLiquidity.currency, - percentToRemove.multiply(userLiquidity.quotient).quotient + percentToRemove.multiply(userLiquidity.quotient).quotient, ) : undefined, [Field.CURRENCY_A]: @@ -141,7 +141,7 @@ export function useBurnActionHandlers(): { (field: Field, typedValue: string) => { dispatch(typeInput({ field, typedValue })) }, - [dispatch] + [dispatch], ) return { diff --git a/apps/web/src/state/burn/reducer.ts b/apps/web/src/state/burn/reducer.ts index 06d83197c3b..0749aca5c01 100644 --- a/apps/web/src/state/burn/reducer.ts +++ b/apps/web/src/state/burn/reducer.ts @@ -18,5 +18,5 @@ export default createReducer(initialState, (builder) => independentField: field, typedValue, } - }) + }), ) diff --git a/apps/web/src/state/burn/v3/hooks.tsx b/apps/web/src/state/burn/v3/hooks.tsx index 98d978c8a7b..87cd626ec87 100644 --- a/apps/web/src/state/burn/v3/hooks.tsx +++ b/apps/web/src/state/burn/v3/hooks.tsx @@ -18,7 +18,7 @@ export function useBurnV3State(): AppState['burnV3'] { export function useDerivedV3BurnInfo( position?: PositionDetails, - asWETH = false + asWETH = false, ): { position?: Position liquidityPercentage?: Percent @@ -47,7 +47,7 @@ export function useDerivedV3BurnInfo( tickUpper: position.tickUpper, }) : undefined, - [pool, position] + [pool, position], ) const liquidityPercentage = new Percent(percent, 100) @@ -101,7 +101,7 @@ export function useBurnV3ActionHandlers(): { (percent: number) => { dispatch(selectPercent({ percent })) }, - [dispatch] + [dispatch], ) return { diff --git a/apps/web/src/state/burn/v3/reducer.ts b/apps/web/src/state/burn/v3/reducer.ts index 694c64f75a7..ccfade1f874 100644 --- a/apps/web/src/state/burn/v3/reducer.ts +++ b/apps/web/src/state/burn/v3/reducer.ts @@ -15,5 +15,5 @@ export default createReducer(initialState, (builder) => ...state, percent, } - }) + }), ) diff --git a/apps/web/src/state/claim/hooks.ts b/apps/web/src/state/claim/hooks.ts index 1dd41549a58..dbd9f60ea74 100644 --- a/apps/web/src/state/claim/hooks.ts +++ b/apps/web/src/state/claim/hooks.ts @@ -36,7 +36,7 @@ function fetchClaimMapping(): Promise { return ( FETCH_CLAIM_MAPPING_PROMISE ?? (FETCH_CLAIM_MAPPING_PROMISE = fetch( - `https://raw.githubusercontent.com/Uniswap/mrkl-drop-data-chunks/final/chunks/mapping.json` + `https://raw.githubusercontent.com/Uniswap/mrkl-drop-data-chunks/final/chunks/mapping.json`, ) .then((res) => res.json()) .catch((error) => { @@ -51,7 +51,7 @@ function fetchClaimFile(key: string): Promise<{ [address: string]: UserClaimData return ( FETCH_CLAIM_FILE_PROMISES[key] ?? (FETCH_CLAIM_FILE_PROMISES[key] = fetch( - `https://raw.githubusercontent.com/Uniswap/mrkl-drop-data-chunks/final/chunks/${key}.json` + `https://raw.githubusercontent.com/Uniswap/mrkl-drop-data-chunks/final/chunks/${key}.json`, ) .then((res) => res.json()) .catch((error) => { @@ -120,7 +120,7 @@ function useUserClaimData(account: string | null | undefined): UserClaimData | n ...claimInfo, [account]: accountClaimInfo, } - }) + }), ) .catch(() => { setClaimInfo((claimInfo) => { diff --git a/apps/web/src/state/governance/hooks.ts b/apps/web/src/state/governance/hooks.ts index b319c256014..369b5bccc94 100644 --- a/apps/web/src/state/governance/hooks.ts +++ b/apps/web/src/state/governance/hooks.ts @@ -131,7 +131,7 @@ function useFormattedProposalCreatedLogs( contract: Contract | null, indices: number[][], fromBlock?: number, - toBlock?: number + toBlock?: number, ): FormattedProposalLog[] | undefined { // create filters for ProposalCreated events const filter = useMemo(() => { @@ -419,13 +419,13 @@ export function useDelegateCallback(): (delegatee: string | undefined) => undefi }) }) }, - [account.address, addTransaction, account.chainId, provider, uniContract] + [account.address, addTransaction, account.chainId, provider, uniContract], ) } export function useVoteCallback(): ( proposalId: string | undefined, - voteOption: VoteOption + voteOption: VoteOption, ) => undefined | Promise { const account = useAccount() const latestGovernanceContract = useLatestGovernanceContract() @@ -452,7 +452,7 @@ export function useVoteCallback(): ( }) }) }, - [account.address, addTransaction, latestGovernanceContract, account.chainId] + [account.address, addTransaction, latestGovernanceContract, account.chainId], ) } @@ -480,7 +480,7 @@ export function useQueueCallback(): (proposalId: string | undefined) => undefine }) }) }, - [account.address, addTransaction, latestGovernanceContract, account.chainId] + [account.address, addTransaction, latestGovernanceContract, account.chainId], ) } @@ -508,12 +508,12 @@ export function useExecuteCallback(): (proposalId: string | undefined) => undefi }) }) }, - [account.address, addTransaction, latestGovernanceContract, account.chainId] + [account.address, addTransaction, latestGovernanceContract, account.chainId], ) } export function useCreateProposalCallback(): ( - createProposalData: CreateProposalData | undefined + createProposalData: CreateProposalData | undefined, ) => undefined | Promise { const account = useAccount() const latestGovernanceContract = useLatestGovernanceContract() @@ -544,7 +544,7 @@ export function useCreateProposalCallback(): ( }) }) }, - [account.address, addTransaction, latestGovernanceContract, account.chainId] + [account.address, addTransaction, latestGovernanceContract, account.chainId], ) } diff --git a/apps/web/src/state/index.ts b/apps/web/src/state/index.ts index fd1fecba28c..03feaa243a0 100644 --- a/apps/web/src/state/index.ts +++ b/apps/web/src/state/index.ts @@ -7,6 +7,7 @@ import reducer from 'state/reducer' import { quickRouteApi } from 'state/routing/quickRouteSlice' import { routingApi } from 'state/routing/slice' import { fiatOnRampAggregatorApi } from 'uniswap/src/features/fiatOnRamp/api' +import { isTestEnv } from 'utilities/src/environment' export function createDefaultStore() { return configureStore({ @@ -15,29 +16,27 @@ export function createDefaultStore() { middleware: (getDefaultMiddleware) => getDefaultMiddleware({ thunk: true, - immutableCheck: - process.env.NODE_ENV === 'test' - ? false - : { - ignoredPaths: [routingApi.reducerPath, 'logs', 'lists'], - }, - serializableCheck: - process.env.NODE_ENV === 'test' - ? false - : { - warnAfter: 128, - // meta.arg and meta.baseQueryMeta are defaults. payload.trade is a nonserializable return value, but that's ok - // because we are not adding it into any persisted store that requires serialization (e.g. localStorage) - ignoredActionPaths: ['meta.arg', 'meta.baseQueryMeta', 'payload.trade'], - ignoredPaths: [routingApi.reducerPath, quickRouteApi.reducerPath], - ignoredActions: [ - // ignore the redux-persist actions - 'persist/PERSIST', - 'persist/REHYDRATE', - 'persist/PURGE', - 'persist/FLUSH', - ], - }, + immutableCheck: isTestEnv() + ? false + : { + ignoredPaths: [routingApi.reducerPath, 'logs', 'lists'], + }, + serializableCheck: isTestEnv() + ? false + : { + warnAfter: 128, + // meta.arg and meta.baseQueryMeta are defaults. payload.trade is a nonserializable return value, but that's ok + // because we are not adding it into any persisted store that requires serialization (e.g. localStorage) + ignoredActionPaths: ['meta.arg', 'meta.baseQueryMeta', 'payload.trade'], + ignoredPaths: [routingApi.reducerPath, quickRouteApi.reducerPath], + ignoredActions: [ + // ignore the redux-persist actions + 'persist/PERSIST', + 'persist/REHYDRATE', + 'persist/PURGE', + 'persist/FLUSH', + ], + }, }) .concat(routingApi.middleware) .concat(quickRouteApi.middleware) diff --git a/apps/web/src/state/limit/hooks.ts b/apps/web/src/state/limit/hooks.ts index 1aab780b3ba..4ef27665e18 100644 --- a/apps/web/src/state/limit/hooks.ts +++ b/apps/web/src/state/limit/hooks.ts @@ -43,7 +43,7 @@ export function useDerivedLimitInfo(state: LimitState, setState: Dispatch [inputCurrency ?? undefined, outputCurrency ?? undefined], [inputCurrency, outputCurrency]) + useMemo(() => [inputCurrency ?? undefined, outputCurrency ?? undefined], [inputCurrency, outputCurrency]), ) useEffect(() => { @@ -58,7 +58,7 @@ export function useDerivedLimitInfo(state: LimitState, setState: Dispatch { @@ -79,7 +79,7 @@ export function useDerivedLimitInfo(state: LimitState, setState: Dispatch; fee?: SwapFeeInfo } { const skip = !(inputCurrency && outputCurrency) // TODO(limits): update amount for MATIC and CELO once Limits are supported on those chains @@ -221,7 +221,7 @@ function useMarketPriceAndFee( TradeType.EXACT_OUTPUT, baseCurrencyAmount, inputCurrency, - RouterPreference.API + RouterPreference.API, ) const { trade: tradeB } = useRoutingAPITrade( @@ -229,7 +229,7 @@ function useMarketPriceAndFee( TradeType.EXACT_INPUT, baseCurrencyAmount, outputCurrency, - RouterPreference.API + RouterPreference.API, ) const marketPrice: Price | undefined = useMemo(() => { diff --git a/apps/web/src/state/lists/hooks.ts b/apps/web/src/state/lists/hooks.ts index 6d089456372..d4f0910672c 100644 --- a/apps/web/src/state/lists/hooks.ts +++ b/apps/web/src/state/lists/hooks.ts @@ -25,7 +25,7 @@ function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): TokenAddress .reduce<{ [chainId: string]: true }>((memo, value) => { memo[value] = true return memo - }, {}) + }, {}), ).map((id) => parseInt(id)) return chainIds.reduce>((memo, chainId) => { diff --git a/apps/web/src/state/lists/reducer.ts b/apps/web/src/state/lists/reducer.ts index 4a0d00ddebb..7d083c4138e 100644 --- a/apps/web/src/state/lists/reducer.ts +++ b/apps/web/src/state/lists/reducer.ts @@ -107,7 +107,7 @@ export default createReducer(initialState, (builder) => } else if (state.lastInitializedDefaultListOfLists) { const lastInitializedSet = state.lastInitializedDefaultListOfLists.reduce>( (s, l) => s.add(l), - new Set() + new Set(), ) const newListOfListsSet = DEFAULT_INACTIVE_LIST_URLS.reduce>((s, l) => s.add(l), new Set()) @@ -125,5 +125,5 @@ export default createReducer(initialState, (builder) => } state.lastInitializedDefaultListOfLists = DEFAULT_INACTIVE_LIST_URLS - }) + }), ) diff --git a/apps/web/src/state/lists/updater.ts b/apps/web/src/state/lists/updater.ts index 6a479cf54c2..d22eae07799 100644 --- a/apps/web/src/state/lists/updater.ts +++ b/apps/web/src/state/lists/updater.ts @@ -29,7 +29,7 @@ export default function Updater(): null { } DEFAULT_INACTIVE_LIST_URLS.forEach((url) => { fetchList(url, false).catch((error) => - logger.debug('lists/updater', 'Updater', 'interval list fetching error', error) + logger.debug('lists/updater', 'Updater', 'interval list fetching error', error), ) }) }, [fetchList, isWindowVisible]) @@ -47,7 +47,7 @@ export default function Updater(): null { const list = lists[listUrl] if (!list.current && !list.loadingRequestId && !list.error) { fetchList(listUrl).catch((error) => - logger.debug('lists/updater', 'Updater', 'list added fetching error', error) + logger.debug('lists/updater', 'Updater', 'list added fetching error', error), ) } }) @@ -55,7 +55,7 @@ export default function Updater(): null { const list = lists[listUrl] if (!list || (!list.current && !list.loadingRequestId && !list.error)) { fetchList(listUrl, /* isUnsupportedList= */ true).catch((error) => - logger.debug('lists/updater', 'Updater', 'list added fetching error', error) + logger.debug('lists/updater', 'Updater', 'list added fetching error', error), ) } }) diff --git a/apps/web/src/state/logs/hooks.ts b/apps/web/src/state/logs/hooks.ts index 6d57df7a9d5..7e0e07119ac 100644 --- a/apps/web/src/state/logs/hooks.ts +++ b/apps/web/src/state/logs/hooks.ts @@ -77,8 +77,8 @@ export function useLogs(filter: Filter | undefined): UseLogsResult { state: isHistoricalLog(filter, blockNumber) ? LogsState.SYNCED : result.blockNumber >= blockNumber - ? LogsState.SYNCED - : LogsState.SYNCING, + ? LogsState.SYNCED + : LogsState.SYNCING, logs: result.logs, } }, [blockNumber, chainId, filter, logs]) diff --git a/apps/web/src/state/logs/slice.ts b/apps/web/src/state/logs/slice.ts index c3b540dbd69..bbe2c1aefb4 100644 --- a/apps/web/src/state/logs/slice.ts +++ b/apps/web/src/state/logs/slice.ts @@ -43,7 +43,7 @@ const slice = createSlice({ state, { payload: { chainId, filters, blockNumber }, - }: PayloadAction<{ chainId: number; filters: Filter[]; blockNumber: number }> + }: PayloadAction<{ chainId: number; filters: Filter[]; blockNumber: number }>, ) { if (!state[chainId]) { return @@ -60,7 +60,7 @@ const slice = createSlice({ state, { payload: { chainId, filter, results }, - }: PayloadAction<{ chainId: number; filter: Filter; results: { blockNumber: number; logs: Log[] } }> + }: PayloadAction<{ chainId: number; filter: Filter; results: { blockNumber: number; logs: Log[] } }>, ) { if (!state[chainId]) { return @@ -76,7 +76,7 @@ const slice = createSlice({ state, { payload: { chainId, filter, blockNumber }, - }: PayloadAction<{ chainId: number; blockNumber: number; filter: Filter }> + }: PayloadAction<{ chainId: number; blockNumber: number; filter: Filter }>, ) { if (!state[chainId]) { return diff --git a/apps/web/src/state/logs/updater.ts b/apps/web/src/state/logs/updater.ts index 034d6d5310b..6b3990bd277 100644 --- a/apps/web/src/state/logs/updater.ts +++ b/apps/web/src/state/logs/updater.ts @@ -75,7 +75,7 @@ export default function Updater(): null { chainId, filter, results: { logs, blockNumber }, - }) + }), ) }) .catch((error) => { @@ -85,7 +85,7 @@ export default function Updater(): null { chainId, filter, blockNumber, - }) + }), ) }) }) diff --git a/apps/web/src/state/migrations/3.ts b/apps/web/src/state/migrations/3.ts index 8528a78c209..990fae677ed 100644 --- a/apps/web/src/state/migrations/3.ts +++ b/apps/web/src/state/migrations/3.ts @@ -28,7 +28,7 @@ export const migration3 = (state: PersistAppStateV3 | undefined) => { const chainIdKey = Number(chainId) as InterfaceChainId if (state.user.tokens?.[chainIdKey]?.[address]) { state.user.tokens[chainIdKey][address] = serializeToken( - new Token(chainIdKey, address, 6, 'USDC.e', 'Bridged USDC') + new Token(chainIdKey, address, 6, 'USDC.e', 'Bridged USDC'), ) } } @@ -38,7 +38,7 @@ export const migration3 = (state: PersistAppStateV3 | undefined) => { '0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA', 6, 'USDbC', - 'USD Base Coin' + 'USD Base Coin', ) if (state.user.tokens?.[UniverseChainId.Base]?.[USDbC_BASE.address]) { state.user.tokens[UniverseChainId.Base][USDbC_BASE.address] = serializeToken(USDbC_BASE) diff --git a/apps/web/src/state/migrations/9.ts b/apps/web/src/state/migrations/9.ts index b7a281ec992..156c9935514 100644 --- a/apps/web/src/state/migrations/9.ts +++ b/apps/web/src/state/migrations/9.ts @@ -19,7 +19,7 @@ export const migration9 = (state: PersistAppStateV9 | undefined) => { updatedListsByUrl = Object.fromEntries( Object.entries(state.lists.byUrl).filter(([url]) => { return DEFAULT_INACTIVE_LIST_URLS.includes(url) - }) + }), ) } diff --git a/apps/web/src/state/mint/hooks.tsx b/apps/web/src/state/mint/hooks.tsx index bd2a5fdab66..62523e813de 100644 --- a/apps/web/src/state/mint/hooks.tsx +++ b/apps/web/src/state/mint/hooks.tsx @@ -29,14 +29,14 @@ export function useMintActionHandlers(noLiquidity: boolean | undefined): { (typedValue: string) => { dispatch(typeInput({ field: Field.CURRENCY_A, typedValue, noLiquidity: noLiquidity === true })) }, - [dispatch, noLiquidity] + [dispatch, noLiquidity], ) const onFieldBInput = useCallback( (typedValue: string) => { dispatch(typeInput({ field: Field.CURRENCY_B, typedValue, noLiquidity: noLiquidity === true })) }, - [dispatch, noLiquidity] + [dispatch, noLiquidity], ) return { @@ -47,7 +47,7 @@ export function useMintActionHandlers(noLiquidity: boolean | undefined): { export function useDerivedMintInfo( currencyA: Currency | undefined, - currencyB: Currency | undefined + currencyB: Currency | undefined, ): { dependentField: Field currencies: { [field in Field]?: Currency } @@ -73,7 +73,7 @@ export function useDerivedMintInfo( [Field.CURRENCY_A]: currencyA ?? undefined, [Field.CURRENCY_B]: currencyB ?? undefined, }), - [currencyA, currencyB] + [currencyA, currencyB], ) // pair @@ -87,13 +87,13 @@ export function useDerivedMintInfo( pairState === PairState.EXISTS && pair && JSBI.equal(pair.reserve0.quotient, ZERO) && - JSBI.equal(pair.reserve1.quotient, ZERO) + JSBI.equal(pair.reserve1.quotient, ZERO), ) // balances const balances = useCurrencyBalances( account.address, - useMemo(() => [currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B]], [currencies]) + useMemo(() => [currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B]], [currencies]), ) const currencyBalances: { [field in Field]?: CurrencyAmount } = { [Field.CURRENCY_A]: balances[0], @@ -103,7 +103,7 @@ export function useDerivedMintInfo( // amounts const independentAmount: CurrencyAmount | undefined = tryParseCurrencyAmount( typedValue, - currencies[independentField] + currencies[independentField], ) const dependentAmount: CurrencyAmount | undefined = useMemo(() => { if (noLiquidity) { @@ -164,7 +164,7 @@ export function useDerivedMintInfo( 'mint/hooks', 'useDerivedMintInfo', `Error getLiquidityMinted: ${error}. Total supply: ${totalSupply}, tokenAmountA: ${tokenAmountA}, tokenAmountB: ${tokenAmountB}`, - { error } + { error }, ) return undefined } diff --git a/apps/web/src/state/mint/reducer.ts b/apps/web/src/state/mint/reducer.ts index 0b8a3622c06..a3e464afcc5 100644 --- a/apps/web/src/state/mint/reducer.ts +++ b/apps/web/src/state/mint/reducer.ts @@ -49,5 +49,5 @@ export default createReducer(initialState, (builder) => otherTypedValue: '', } } - }) + }), ) diff --git a/apps/web/src/state/mint/v3/actions.ts b/apps/web/src/state/mint/v3/actions.ts index c333d48d067..d62ec070d31 100644 --- a/apps/web/src/state/mint/v3/actions.ts +++ b/apps/web/src/state/mint/v3/actions.ts @@ -11,7 +11,7 @@ export enum Bound { } export const typeInput = createAction<{ field: Field; typedValue: string; noLiquidity: boolean }>( - 'mintV3/typeInputMint' + 'mintV3/typeInputMint', ) export const typeStartPriceInput = createAction<{ typedValue: string }>('mintV3/typeStartPriceInput') export const typeLeftRangeInput = createAction<{ typedValue: string }>('mintV3/typeLeftRangeInput') diff --git a/apps/web/src/state/mint/v3/hooks.tsx b/apps/web/src/state/mint/v3/hooks.tsx index 0744087fcba..6c89499e225 100644 --- a/apps/web/src/state/mint/v3/hooks.tsx +++ b/apps/web/src/state/mint/v3/hooks.tsx @@ -51,14 +51,14 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): { (typedValue: string) => { dispatch(typeInput({ field: Field.CURRENCY_A, typedValue, noLiquidity: noLiquidity === true })) }, - [dispatch, noLiquidity] + [dispatch, noLiquidity], ) const onFieldBInput = useCallback( (typedValue: string) => { dispatch(typeInput({ field: Field.CURRENCY_B, typedValue, noLiquidity: noLiquidity === true })) }, - [dispatch, noLiquidity] + [dispatch, noLiquidity], ) const [searchParams, setSearchParams] = useSearchParams() @@ -72,7 +72,7 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): { setSearchParams(searchParams) } }, - [dispatch, searchParams, setSearchParams] + [dispatch, searchParams, setSearchParams], ) const onRightRangeInput = useCallback( @@ -84,14 +84,14 @@ export function useV3MintActionHandlers(noLiquidity: boolean | undefined): { setSearchParams(searchParams) } }, - [dispatch, searchParams, setSearchParams] + [dispatch, searchParams, setSearchParams], ) const onStartPriceInput = useCallback( (typedValue: string) => { dispatch(typeStartPriceInput({ typedValue })) }, - [dispatch] + [dispatch], ) return { @@ -109,7 +109,7 @@ export function useV3DerivedMintInfo( feeAmount?: FeeAmount, baseCurrency?: Currency, // override for existing position - existingPosition?: Position + existingPosition?: Position, ): { pool?: Pool | null poolState: PoolState @@ -150,25 +150,25 @@ export function useV3DerivedMintInfo( [Field.CURRENCY_A]: currencyA, [Field.CURRENCY_B]: currencyB, }), - [currencyA, currencyB] + [currencyA, currencyB], ) // formatted with tokens const [tokenA, tokenB, baseToken] = useMemo( () => [currencyA?.wrapped, currencyB?.wrapped, baseCurrency?.wrapped], - [currencyA, currencyB, baseCurrency] + [currencyA, currencyB, baseCurrency], ) const [token0, token1] = useMemo( () => tokenA && tokenB ? (tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]) : [undefined, undefined], - [tokenA, tokenB] + [tokenA, tokenB], ) // balances const balances = useCurrencyBalances( account.address, - useMemo(() => [currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B]], [currencies]) + useMemo(() => [currencies[Field.CURRENCY_A], currencies[Field.CURRENCY_B]], [currencies]), ) const currencyBalances: { [field in Field]?: CurrencyAmount } = { [Field.CURRENCY_A]: balances[0], @@ -195,7 +195,7 @@ export function useV3DerivedMintInfo( baseAmount.currency, parsedQuoteAmount.currency, baseAmount.quotient, - parsedQuoteAmount.quotient + parsedQuoteAmount.quotient, ) : undefined return (invertPrice ? price?.invert() : price) ?? undefined @@ -240,7 +240,7 @@ export function useV3DerivedMintInfo( [Bound.LOWER]: feeAmount ? nearestUsableTick(TickMath.MIN_TICK, TICK_SPACINGS[feeAmount]) : undefined, [Bound.UPPER]: feeAmount ? nearestUsableTick(TickMath.MAX_TICK, TICK_SPACINGS[feeAmount]) : undefined, }), - [feeAmount] + [feeAmount], ) // parse typed range values and determine closest ticks @@ -251,20 +251,20 @@ export function useV3DerivedMintInfo( typeof existingPosition?.tickLower === 'number' ? existingPosition.tickLower : (invertPrice && typeof rightRangeTypedValue === 'boolean') || - (!invertPrice && typeof leftRangeTypedValue === 'boolean') - ? tickSpaceLimits[Bound.LOWER] - : invertPrice - ? tryParseTick(token1, token0, feeAmount, rightRangeTypedValue.toString()) - : tryParseTick(token0, token1, feeAmount, leftRangeTypedValue.toString()), + (!invertPrice && typeof leftRangeTypedValue === 'boolean') + ? tickSpaceLimits[Bound.LOWER] + : invertPrice + ? tryParseTick(token1, token0, feeAmount, rightRangeTypedValue.toString()) + : tryParseTick(token0, token1, feeAmount, leftRangeTypedValue.toString()), [Bound.UPPER]: typeof existingPosition?.tickUpper === 'number' ? existingPosition.tickUpper : (!invertPrice && typeof rightRangeTypedValue === 'boolean') || - (invertPrice && typeof leftRangeTypedValue === 'boolean') - ? tickSpaceLimits[Bound.UPPER] - : invertPrice - ? tryParseTick(token1, token0, feeAmount, leftRangeTypedValue.toString()) - : tryParseTick(token0, token1, feeAmount, rightRangeTypedValue.toString()), + (invertPrice && typeof leftRangeTypedValue === 'boolean') + ? tickSpaceLimits[Bound.UPPER] + : invertPrice + ? tryParseTick(token1, token0, feeAmount, leftRangeTypedValue.toString()) + : tryParseTick(token0, token1, feeAmount, rightRangeTypedValue.toString()), } }, [ existingPosition, @@ -285,7 +285,7 @@ export function useV3DerivedMintInfo( [Bound.LOWER]: feeAmount && tickLower === tickSpaceLimits.LOWER, [Bound.UPPER]: feeAmount && tickUpper === tickSpaceLimits.UPPER, }), - [tickSpaceLimits, tickLower, tickUpper, feeAmount] + [tickSpaceLimits, tickLower, tickUpper, feeAmount], ) // mark invalid range @@ -309,13 +309,13 @@ export function useV3DerivedMintInfo( // liquidity range warning const outOfRange = Boolean( - !invalidRange && price && lowerPrice && upperPrice && (price.lessThan(lowerPrice) || price.greaterThan(upperPrice)) + !invalidRange && price && lowerPrice && upperPrice && (price.lessThan(lowerPrice) || price.greaterThan(upperPrice)), ) // amounts const independentAmount: CurrencyAmount | undefined = tryParseCurrencyAmount( typedValue, - currencies[independentField] + currencies[independentField], ) const dependentAmount: CurrencyAmount | undefined = useMemo(() => { @@ -377,10 +377,10 @@ export function useV3DerivedMintInfo( // single deposit only if price is out of range const deposit0Disabled = Boolean( - typeof tickUpper === 'number' && poolForPosition && poolForPosition.tickCurrent >= tickUpper + typeof tickUpper === 'number' && poolForPosition && poolForPosition.tickCurrent >= tickUpper, ) const deposit1Disabled = Boolean( - typeof tickLower === 'number' && poolForPosition && poolForPosition.tickCurrent <= tickLower + typeof tickLower === 'number' && poolForPosition && poolForPosition.tickCurrent <= tickLower, ) // sorted for token order @@ -388,18 +388,19 @@ export function useV3DerivedMintInfo( invalidRange || Boolean( (deposit0Disabled && poolForPosition && tokenA && poolForPosition.token0.equals(tokenA)) || - (deposit1Disabled && poolForPosition && tokenA && poolForPosition.token1.equals(tokenA)) + (deposit1Disabled && poolForPosition && tokenA && poolForPosition.token1.equals(tokenA)), ) const depositBDisabled = invalidRange || Boolean( (deposit0Disabled && poolForPosition && tokenB && poolForPosition.token0.equals(tokenB)) || - (deposit1Disabled && poolForPosition && tokenB && poolForPosition.token1.equals(tokenB)) + (deposit1Disabled && poolForPosition && tokenB && poolForPosition.token1.equals(tokenB)), ) const { inputTax: currencyATax, outputTax: currencyBTax } = useSwapTaxes( currencyA?.isToken ? currencyA.address : undefined, - currencyB?.isToken ? currencyB.address : undefined + currencyB?.isToken ? currencyB.address : undefined, + account.chainId, ) // create position entity based on users selection @@ -525,7 +526,7 @@ export function useRangeHopCallbacks( feeAmount: FeeAmount | undefined, tickLower: number | undefined, tickUpper: number | undefined, - pool?: Pool | undefined | null + pool?: Pool | undefined | null, ) { const dispatch = useAppDispatch() diff --git a/apps/web/src/state/mint/v3/reducer.ts b/apps/web/src/state/mint/v3/reducer.ts index 979ab6d033a..700b1680d87 100644 --- a/apps/web/src/state/mint/v3/reducer.ts +++ b/apps/web/src/state/mint/v3/reducer.ts @@ -80,5 +80,5 @@ export default createReducer(initialState, (builder) => typedValue, } } - }) + }), ) diff --git a/apps/web/src/state/mint/v3/utils.ts b/apps/web/src/state/mint/v3/utils.ts index 9a56ffe340b..92bb994f089 100644 --- a/apps/web/src/state/mint/v3/utils.ts +++ b/apps/web/src/state/mint/v3/utils.ts @@ -27,7 +27,7 @@ export function tryParsePrice(baseToken?: Token, quoteToken?: Token, value?: str baseToken, quoteToken, JSBI.multiply(JSBI.BigInt(10 ** decimals), JSBI.BigInt(10 ** baseToken.decimals)), - JSBI.multiply(withoutDecimals, JSBI.BigInt(10 ** quoteToken.decimals)) + JSBI.multiply(withoutDecimals, JSBI.BigInt(10 ** quoteToken.decimals)), ) } @@ -35,7 +35,7 @@ export function tryParseTick( baseToken?: Token, quoteToken?: Token, feeAmount?: FeeAmount, - value?: string + value?: string, ): number | undefined { if (!baseToken || !quoteToken || !feeAmount || !value) { return undefined diff --git a/apps/web/src/state/reducer.ts b/apps/web/src/state/reducer.ts index 4c13e02b42b..06bd63640a7 100644 --- a/apps/web/src/state/reducer.ts +++ b/apps/web/src/state/reducer.ts @@ -17,7 +17,7 @@ import transactions from 'state/transactions/reducer' import user from 'state/user/reducer' import wallets from 'state/wallets/reducer' import { fiatOnRampAggregatorApi } from 'uniswap/src/features/fiatOnRamp/api' -import { isDevEnv } from 'uniswap/src/utils/env' +import { isDevEnv } from 'utilities/src/environment' const persistedReducers = { user, diff --git a/apps/web/src/state/routing/gas.ts b/apps/web/src/state/routing/gas.ts index a2a4e02261f..7c55af63716 100644 --- a/apps/web/src/state/routing/gas.ts +++ b/apps/web/src/state/routing/gas.ts @@ -18,7 +18,7 @@ export async function getApproveInfo( account: string | undefined, currency: Currency, amount: string, - usdCostPerGas?: number + usdCostPerGas?: number, ): Promise { // native currencies do not need token approvals if (currency.isNative) { @@ -66,7 +66,7 @@ export async function getWrapInfo( account: string | undefined, chainId: SupportedInterfaceChainId, amount: string, - usdCostPerGas?: number + usdCostPerGas?: number, ): Promise { if (!needsWrap) { return { needsWrap: false } diff --git a/apps/web/src/state/routing/slice.ts b/apps/web/src/state/routing/slice.ts index 3cb5156957f..07c67a209de 100644 --- a/apps/web/src/state/routing/slice.ts +++ b/apps/web/src/state/routing/slice.ts @@ -171,7 +171,7 @@ export const routingApi = createApi({ 'queryFn', `GetQuote failed on Unified Routing API, falling back to client: ${ error?.message ?? error?.detail ?? error - }` + }`, ) } diff --git a/apps/web/src/state/routing/types.ts b/apps/web/src/state/routing/types.ts index 9b092555c74..409c6087034 100644 --- a/apps/web/src/state/routing/types.ts +++ b/apps/web/src/state/routing/types.ts @@ -575,7 +575,7 @@ export class PreviewTrade { this.inputAmount.currency, this.outputAmount.currency, this.inputAmount.quotient, - this.outputAmount.quotient + this.outputAmount.quotient, )) ) } @@ -585,7 +585,7 @@ export class PreviewTrade { this.inputAmount.currency, this.outputAmount.currency, this.maximumAmountIn(slippageTolerance).quotient, - this.minimumAmountOut(slippageTolerance).quotient + this.minimumAmountOut(slippageTolerance).quotient, ) } } diff --git a/apps/web/src/state/routing/usePreviewTrade.ts b/apps/web/src/state/routing/usePreviewTrade.ts index e20c056ce63..34fb0d1bf9d 100644 --- a/apps/web/src/state/routing/usePreviewTrade.ts +++ b/apps/web/src/state/routing/usePreviewTrade.ts @@ -65,7 +65,7 @@ export function usePreviewTrade( amountSpecified: CurrencyAmount | undefined, otherCurrency: Currency | undefined, inputTax = ZERO_PERCENT, - outputTax = ZERO_PERCENT + outputTax = ZERO_PERCENT, ): { state: TradeState trade?: PreviewTrade @@ -77,7 +77,7 @@ export function usePreviewTrade( tradeType === TradeType.EXACT_INPUT ? [amountSpecified?.currency, otherCurrency] : [otherCurrency, amountSpecified?.currency], - [amountSpecified, otherCurrency, tradeType] + [amountSpecified, otherCurrency, tradeType], ) const queryArgs = useQuickRouteArguments({ diff --git a/apps/web/src/state/routing/useRoutingAPITrade.ts b/apps/web/src/state/routing/useRoutingAPITrade.ts index 185101bc166..5839f5ef9aa 100644 --- a/apps/web/src/state/routing/useRoutingAPITrade.ts +++ b/apps/web/src/state/routing/useRoutingAPITrade.ts @@ -29,7 +29,7 @@ export function useRoutingAPITrade( account?: string, protocolPreferences?: Protocol[], inputTax?: Percent, - outputTax?: Percent + outputTax?: Percent, ): { state: TradeState trade?: ClassicTrade @@ -46,7 +46,7 @@ export function useRoutingAPITrade( account?: string, protocolPreferences?: Protocol[], inputTax?: Percent, - outputTax?: Percent + outputTax?: Percent, ): { state: TradeState trade?: SubmittableTrade @@ -67,7 +67,7 @@ export function useRoutingAPITrade( otherCurrency: Currency | undefined, routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE, account?: string, - protocolPreferences?: Protocol[] + protocolPreferences?: Protocol[], ): { state: TradeState trade?: SubmittableTrade @@ -80,7 +80,7 @@ export function useRoutingAPITrade( tradeType === TradeType.EXACT_INPUT ? [amountSpecified?.currency, otherCurrency] : [otherCurrency, amountSpecified?.currency], - [amountSpecified, otherCurrency, tradeType] + [amountSpecified, otherCurrency, tradeType], ) const queryArgs = useRoutingAPIArguments({ diff --git a/apps/web/src/state/routing/utils.ts b/apps/web/src/state/routing/utils.ts index bb118c65d2c..19ca1bd2de3 100644 --- a/apps/web/src/state/routing/utils.ts +++ b/apps/web/src/state/routing/utils.ts @@ -100,7 +100,7 @@ const parsePoolOrPair = (pool: V3PoolInRoute | V2PoolInRoute): Pool | Pair => { function isVersionedRoute( type: T['type'], - route: (V3PoolInRoute | V2PoolInRoute)[] + route: (V3PoolInRoute | V2PoolInRoute)[], ): route is T[] { return route.every((pool) => pool.type === type) } @@ -148,7 +148,7 @@ function toUnsignedV2DutchOrderInfo(orderInfoJSON: UnsignedV2DutchOrderInfoJSON) function getTradeCurrencies( args: GetQuoteArgs | GetQuickQuoteArgs, isUniswapXTrade: boolean, - routes?: ClassicQuoteData['route'] + routes?: ClassicQuoteData['route'], ): [Currency, Currency] { const { tokenInAddress, @@ -196,7 +196,7 @@ function getTradeCurrencies( } function getSwapFee( - data: ClassicQuoteData | URADutchOrderQuoteData | URADutchOrderV2QuoteData + data: ClassicQuoteData | URADutchOrderQuoteData | URADutchOrderV2QuoteData, ): SwapFeeInfo | undefined { const { portionAmount, portionBips, portionRecipient } = data @@ -213,7 +213,7 @@ function getSwapFee( function getClassicTradeDetails( args: GetQuoteArgs, - data: URAQuoteResponse + data: URAQuoteResponse, ): { gasUseEstimate?: number gasUseEstimateUSD?: number @@ -259,7 +259,7 @@ export function getUSDCostPerGas(gasUseEstimateUSD?: number, gasUseEstimate?: nu export async function transformQuoteToTrade( args: GetQuoteArgs, data: URAQuoteResponse, - quoteMethod: QuoteMethod + quoteMethod: QuoteMethod, ): Promise { const { tradeType, needsWrapIfUniswapX, isXv2, routerPreference, account, amount } = args @@ -295,7 +295,7 @@ export async function transformQuoteToTrade( mixedRoutes: routes ?.filter( - (r): r is RouteResult & { mixedRoute: NonNullable } => r.mixedRoute !== null + (r): r is RouteResult & { mixedRoute: NonNullable } => r.mixedRoute !== null, ) .map(({ mixedRoute, inputAmount, outputAmount }) => ({ mixedRoute, @@ -382,14 +382,14 @@ function parsePool({ fee, sqrtRatioX96, liquidity, tickCurrent, tokenIn, tokenOu parseInt(fee) as FeeAmount, sqrtRatioX96, liquidity, - parseInt(tickCurrent) + parseInt(tickCurrent), ) } const parsePair = ({ reserve0, reserve1 }: V2PoolInRoute): Pair => new Pair( CurrencyAmount.fromRawAmount(parseToken(reserve0.token), reserve0.quotient), - CurrencyAmount.fromRawAmount(parseToken(reserve1.token), reserve1.quotient) + CurrencyAmount.fromRawAmount(parseToken(reserve1.token), reserve1.quotient), ) // TODO(WEB-2050): Convert other instances of tradeType comparison to use this utility function @@ -428,13 +428,13 @@ export function isSubmittableTrade(trade?: InterfaceTrade): trade is Submittable /* Returns true if trade uses UniswapX protocol. Includes both X swaps and limit orders. */ export function isUniswapXTradeType( - tradeType?: TradeFillType + tradeType?: TradeFillType, ): tradeType is TradeFillType.UniswapX | TradeFillType.UniswapXv2 { return tradeType === TradeFillType.UniswapX || tradeType === TradeFillType.UniswapXv2 } export function isUniswapXTrade( - trade?: InterfaceTrade + trade?: InterfaceTrade, ): trade is DutchOrderTrade | V2DutchOrderTrade | LimitOrderTrade { return isUniswapXTradeType(trade?.fillType) } diff --git a/apps/web/src/state/send/SendContext.tsx b/apps/web/src/state/send/SendContext.tsx index c04c4eb0ca9..ad507fdfdf4 100644 --- a/apps/web/src/state/send/SendContext.tsx +++ b/apps/web/src/state/send/SendContext.tsx @@ -12,26 +12,25 @@ import { import { RecipientData, SendInfo, useDerivedSendInfo } from 'state/send/hooks' import { useSwapAndLimitContext } from 'state/swap/hooks' -export type SendState = +export type SendState = { + readonly exactAmountToken?: string + readonly exactAmountFiat?: string + readonly recipient: string + readonly inputCurrency?: Currency + readonly inputInFiat: boolean + readonly validatedRecipientData?: RecipientData +} & ( | { - readonly exactAmountToken?: string - readonly exactAmountFiat?: string - readonly recipient: string - readonly inputCurrency?: Currency - readonly inputInFiat: boolean - readonly validatedRecipientData?: RecipientData - } & ( - | { - readonly exactAmountToken: string - readonly exactAmountFiat: undefined - readonly inputInFiat: false - } - | { - readonly exactAmountFiat: string - readonly exactAmountToken: undefined - readonly inputInFiat: true - } - ) + readonly exactAmountToken: string + readonly exactAmountFiat: undefined + readonly inputInFiat: false + } + | { + readonly exactAmountFiat: string + readonly exactAmountToken: undefined + readonly inputInFiat: true + } +) export type SendContextType = { sendState: SendState @@ -90,7 +89,7 @@ export function SendContextProvider({ children }: PropsWithChildren) { setSendState, derivedSendInfo, }), - [derivedSendInfo, setSendState, sendState] + [derivedSendInfo, setSendState, sendState], ) return {children} diff --git a/apps/web/src/state/send/hooks.tsx b/apps/web/src/state/send/hooks.tsx index 33285ebf4d3..8dacbe024d1 100644 --- a/apps/web/src/state/send/hooks.tsx +++ b/apps/web/src/state/send/hooks.tsx @@ -81,9 +81,9 @@ export function useDerivedSendInfo(state: SendState): SendInfo { ]) const nativeCurrency = useCurrency(NATIVE_CHAIN_ID, chainId) - const [inputCurrencyBalance, nativeCurencyBalance] = useCurrencyBalances( + const [inputCurrencyBalance, nativeCurrencyBalance] = useCurrencyBalances( account.address, - useMemo(() => [inputCurrency, nativeCurrency], [inputCurrency, nativeCurrency]) + useMemo(() => [inputCurrency, nativeCurrency], [inputCurrency, nativeCurrency]), ) const exactAmountOut = useUSDTokenUpdater(inputInFiat, exactAmountToken ?? exactAmountFiat, inputCurrency) @@ -116,7 +116,7 @@ export function useDerivedSendInfo(state: SendState): SendInfo { return SendInputError.INSUFFICIENT_FUNDS } - if (!gasFee.value || !nativeCurrency || !nativeCurencyBalance) { + if (!gasFee.value || !nativeCurrency || !nativeCurrencyBalance) { return undefined } @@ -125,12 +125,12 @@ export function useDerivedSendInfo(state: SendState): SendInfo { totalAmount = totalAmount?.add(parsedTokenAmount) } - if (!totalAmount || nativeCurencyBalance?.lessThan(totalAmount)) { + if (!totalAmount || nativeCurrencyBalance?.lessThan(totalAmount)) { return SendInputError.INSUFFICIENT_FUNDS_FOR_GAS } return undefined - }, [gasFee.value, inputCurrency, inputCurrencyBalance, nativeCurencyBalance, nativeCurrency, parsedTokenAmount]) + }, [gasFee.value, inputCurrency, inputCurrencyBalance, nativeCurrencyBalance, nativeCurrency, parsedTokenAmount]) return useMemo( () => ({ @@ -152,6 +152,6 @@ export function useDerivedSendInfo(state: SendState): SendInfo { parsedTokenAmount, recipientData, transferTransaction, - ] + ], ) } diff --git a/apps/web/src/state/signatures/hooks.ts b/apps/web/src/state/signatures/hooks.ts index e784b09cb0e..023739294d6 100644 --- a/apps/web/src/state/signatures/hooks.ts +++ b/apps/web/src/state/signatures/hooks.ts @@ -36,7 +36,7 @@ export function useOrder(orderHash: string): UniswapXOrderDetails | undefined { if ( !order || ![SignatureType.SIGN_UNISWAPX_ORDER, SignatureType.SIGN_UNISWAPX_V2_ORDER, SignatureType.SIGN_LIMIT].includes( - order.type as SignatureType + order.type as SignatureType, ) ) { return undefined @@ -56,7 +56,7 @@ export function useAddOrder() { expiry: number, swapInfo: UniswapXOrderDetails['swapInfo'], encodedOrder: string, - offchainOrderType: OffchainOrderType + offchainOrderType: OffchainOrderType, ) => { dispatch( addSignature({ @@ -70,10 +70,10 @@ export function useAddOrder() { status: UniswapXOrderStatus.OPEN, addedTime: Date.now(), encodedOrder, - }) + }), ) }, - [dispatch] + [dispatch], ) } diff --git a/apps/web/src/state/stake/hooks.tsx b/apps/web/src/state/stake/hooks.tsx index 3b8a6e14cab..ed44c7ea5fa 100644 --- a/apps/web/src/state/stake/hooks.tsx +++ b/apps/web/src/state/stake/hooks.tsx @@ -65,7 +65,7 @@ interface StakingInfo { getHypotheticalRewardRate: ( stakedAmount: CurrencyAmount, totalStakedAmount: CurrencyAmount, - totalRewardRate: CurrencyAmount + totalRewardRate: CurrencyAmount, ) => CurrencyAmount } @@ -83,12 +83,12 @@ export function useStakingInfo(pairToFilterBy?: Pair | null): StakingInfo[] { pairToFilterBy === undefined ? true : pairToFilterBy === null - ? false - : pairToFilterBy.involvesToken(stakingRewardInfo.tokens[0]) && - pairToFilterBy.involvesToken(stakingRewardInfo.tokens[1]) + ? false + : pairToFilterBy.involvesToken(stakingRewardInfo.tokens[0]) && + pairToFilterBy.involvesToken(stakingRewardInfo.tokens[1]), ) ?? [] : [], - [account.chainId, pairToFilterBy] + [account.chainId, pairToFilterBy], ) const uni = account.chainId ? UNI[account.chainId] : undefined @@ -108,14 +108,14 @@ export function useStakingInfo(pairToFilterBy?: Pair | null): StakingInfo[] { STAKING_REWARDS_INTERFACE, 'rewardRate', undefined, - NEVER_RELOAD + NEVER_RELOAD, ) const periodFinishes = useMultipleContractSingleData( rewardsAddresses, STAKING_REWARDS_INTERFACE, 'periodFinish', undefined, - NEVER_RELOAD + NEVER_RELOAD, ) return useMemo(() => { @@ -160,31 +160,31 @@ export function useStakingInfo(pairToFilterBy?: Pair | null): StakingInfo[] { const tokens = info[index].tokens const dummyPair = new Pair( CurrencyAmount.fromRawAmount(tokens[0], '0'), - CurrencyAmount.fromRawAmount(tokens[1], '0') + CurrencyAmount.fromRawAmount(tokens[1], '0'), ) // check for account, if no account set to 0 const stakedAmount = CurrencyAmount.fromRawAmount( dummyPair.liquidityToken, - JSBI.BigInt(balanceState?.result?.[0] ?? 0) + JSBI.BigInt(balanceState?.result?.[0] ?? 0), ) const totalStakedAmount = CurrencyAmount.fromRawAmount( dummyPair.liquidityToken, - JSBI.BigInt(totalSupplyState.result?.[0]) + JSBI.BigInt(totalSupplyState.result?.[0]), ) const totalRewardRate = CurrencyAmount.fromRawAmount(uni, JSBI.BigInt(rewardRateState.result?.[0])) const getHypotheticalRewardRate = ( stakedAmount: CurrencyAmount, totalStakedAmount: CurrencyAmount, - totalRewardRate: CurrencyAmount + totalRewardRate: CurrencyAmount, ): CurrencyAmount => { return CurrencyAmount.fromRawAmount( uni, JSBI.greaterThan(totalStakedAmount.quotient, JSBI.BigInt(0)) ? JSBI.divide(JSBI.multiply(totalRewardRate.quotient, stakedAmount.quotient), totalStakedAmount.quotient) - : JSBI.BigInt(0) + : JSBI.BigInt(0), ) } diff --git a/apps/web/src/state/swap/SwapContext.test.tsx b/apps/web/src/state/swap/SwapContext.test.tsx index 7e257237cff..13160209203 100644 --- a/apps/web/src/state/swap/SwapContext.test.tsx +++ b/apps/web/src/state/swap/SwapContext.test.tsx @@ -1,10 +1,11 @@ import { Percent } from '@uniswap/sdk-core' import { Field } from 'components/swap/constants' import { nativeOnChain } from 'constants/tokens' +import { SwapForm } from 'pages/Swap/SwapForm' import { SwapAndLimitContextProvider, SwapContextProvider } from 'state/swap/SwapContext' import { useSwapAndLimitContext, useSwapContext } from 'state/swap/hooks' import { SwapAndLimitContext, SwapInfo } from 'state/swap/types' -import { render } from 'test-utils/render' +import { render, screen } from 'test-utils/render' import { UniverseChainId } from 'uniswap/src/types/chains' import { SwapTab } from 'uniswap/src/types/screens/interface' @@ -24,7 +25,7 @@ describe('Swap Context', () => { render( - + , ) expect(swapContext).toEqual({ @@ -71,7 +72,7 @@ describe('SwapAndLimitContext', () => { render( - + , ) expect(swapAndLimitContext).toEqual({ @@ -88,7 +89,29 @@ describe('SwapAndLimitContext', () => { setCurrencyState: expect.any(Function), currentTab: SwapTab.Swap, setCurrentTab: expect.any(Function), - chainId: undefined, + chainId: 1, + pageChainId: undefined, + isSwapAndLimitContext: true, + }) + }) + + describe('SwapForm', () => { + test('multichain ux disabled', () => { + render( + + + , + ) + expect(screen.getByText('Connect to Optimism')).toBeInTheDocument() + }) + + test('multichain ux enabled', () => { + render( + + + , + ) + expect(screen.getByTestId('swap-button')).toBeInTheDocument() }) }) }) @@ -118,12 +141,13 @@ describe('Combined contexts', () => { chainId: UniverseChainId.Mainnet, currentTab: SwapTab.Swap, setCurrentTab: expect.any(Function), + isSwapAndLimitContext: true, }} > - + , ) // @ts-ignore rendering TestComponent sets derivedSwapInfo value diff --git a/apps/web/src/state/swap/SwapContext.tsx b/apps/web/src/state/swap/SwapContext.tsx index e97995c893d..f96daf8352f 100644 --- a/apps/web/src/state/swap/SwapContext.tsx +++ b/apps/web/src/state/swap/SwapContext.tsx @@ -9,17 +9,17 @@ import { SwapTab } from 'uniswap/src/types/screens/interface' export function SwapAndLimitContextProvider({ children, - chainId, + initialChainId, initialInputCurrency, initialOutputCurrency, multichainUXEnabled, }: PropsWithChildren<{ - chainId?: InterfaceChainId + initialChainId?: InterfaceChainId initialInputCurrency?: Currency initialOutputCurrency?: Currency multichainUXEnabled?: boolean }>) { - const [selectedChainId, setSelectedChainId] = useState(chainId) + const [selectedChainId, setSelectedChainId] = useState(initialChainId) const [currentTab, setCurrentTab] = useState(SwapTab.Swap) const [currencyState, setCurrencyState] = useState({ @@ -32,7 +32,7 @@ export function SwapAndLimitContextProvider({ inputCurrency: initialInputCurrency, outputCurrency: initialOutputCurrency, }), - [initialInputCurrency, initialOutputCurrency] + [initialInputCurrency, initialOutputCurrency], ) const account = useAccount() @@ -67,12 +67,12 @@ export function SwapAndLimitContextProvider({ const prefilledInputChanged = Boolean( previousPrefilledState?.inputCurrency ? !prefilledState.inputCurrency?.equals(previousPrefilledState.inputCurrency) - : prefilledState.inputCurrency + : prefilledState.inputCurrency, ) const prefilledOutputChanged = Boolean( previousPrefilledState?.outputCurrency ? !prefilledState?.outputCurrency?.equals(previousPrefilledState.outputCurrency) - : prefilledState.outputCurrency + : prefilledState.outputCurrency, ) if (chainChanged || prefilledInputChanged || prefilledOutputChanged) { @@ -91,10 +91,10 @@ export function SwapAndLimitContextProvider({ ]) useEffect(() => { - if (chainId) { - setSelectedChainId(chainId) + if (initialChainId) { + setSelectedChainId(initialChainId) } - }, [chainId, setSelectedChainId]) + }, [initialChainId, setSelectedChainId]) const value = useMemo(() => { return { @@ -104,10 +104,12 @@ export function SwapAndLimitContextProvider({ currentTab, setCurrentTab, prefilledState, - chainId: (multichainUXEnabled ? selectedChainId : chainId) ?? undefined, + initialChainId, + chainId: (multichainUXEnabled ? selectedChainId : account.chainId) ?? undefined, multichainUXEnabled, + isSwapAndLimitContext: true, } - }, [chainId, currencyState, currentTab, multichainUXEnabled, prefilledState, selectedChainId]) + }, [initialChainId, account.chainId, selectedChainId, currencyState, currentTab, prefilledState, multichainUXEnabled]) return {children} } diff --git a/apps/web/src/state/swap/hooks.test.ts b/apps/web/src/state/swap/hooks.test.ts index e9f033f3fd8..127a15101cd 100644 --- a/apps/web/src/state/swap/hooks.test.ts +++ b/apps/web/src/state/swap/hooks.test.ts @@ -110,14 +110,14 @@ describe('hooks', () => { const { result: { - current: { initialInputCurrency, initialOutputCurrency, chainId }, + current: { initialInputCurrency, initialOutputCurrency, initialChainId }, }, } = renderHook(() => useInitialCurrencyState()) waitFor(() => { expect(initialInputCurrency).toEqual(undefined) expect(initialOutputCurrency?.symbol).toEqual('UNI') - expect(chainId).toEqual(10) + expect(initialChainId).toEqual(10) }) }) @@ -132,14 +132,14 @@ describe('hooks', () => { const { result: { - current: { initialInputCurrency, initialOutputCurrency, chainId }, + current: { initialInputCurrency, initialOutputCurrency, initialChainId }, }, } = renderHook(() => useInitialCurrencyState()) waitFor(() => { expect(initialInputCurrency?.isNative).toEqual(true) expect(initialOutputCurrency?.symbol).toEqual('UNI') - expect(chainId).toEqual(10) + expect(initialChainId).toEqual(10) }) }) @@ -154,14 +154,14 @@ describe('hooks', () => { const { result: { - current: { initialInputCurrency, initialOutputCurrency, chainId }, + current: { initialInputCurrency, initialOutputCurrency, initialChainId }, }, } = renderHook(() => useInitialCurrencyState()) waitFor(() => { expect(initialInputCurrency?.isNative).toEqual(true) expect(initialOutputCurrency).not.toBeDefined() - expect(chainId).toEqual(1) + expect(initialChainId).toEqual(1) }) }) }) @@ -209,14 +209,14 @@ describe('hooks', () => { const { result: { - current: { initialInputCurrency, initialOutputCurrency, chainId }, + current: { initialInputCurrency, initialOutputCurrency, initialChainId }, }, } = renderHook(() => useInitialCurrencyState()) waitFor(() => { expect(initialInputCurrency).toEqual(undefined) expect(initialOutputCurrency?.symbol).toEqual('UNI') - expect(chainId).toEqual(10) + expect(initialChainId).toEqual(10) }) }) @@ -231,14 +231,14 @@ describe('hooks', () => { const { result: { - current: { initialInputCurrency, initialOutputCurrency, chainId }, + current: { initialInputCurrency, initialOutputCurrency, initialChainId }, }, } = renderHook(() => useInitialCurrencyState()) waitFor(() => { expect(initialInputCurrency?.isNative).toEqual(true) expect(initialOutputCurrency).not.toBeDefined() - expect(chainId).toEqual(1) + expect(initialChainId).toEqual(1) }) }) @@ -253,14 +253,14 @@ describe('hooks', () => { const { result: { - current: { initialInputCurrency, initialOutputCurrency, chainId }, + current: { initialInputCurrency, initialOutputCurrency, initialChainId }, }, } = renderHook(() => useInitialCurrencyState()) waitFor(() => { expect(initialInputCurrency?.isNative).toEqual(true) expect(initialOutputCurrency?.symbol).toEqual('UNI') - expect(chainId).toEqual(10) + expect(initialChainId).toEqual(10) }) }) @@ -275,14 +275,14 @@ describe('hooks', () => { const { result: { - current: { initialInputCurrency, initialOutputCurrency, chainId }, + current: { initialInputCurrency, initialOutputCurrency, initialChainId }, }, } = renderHook(() => useInitialCurrencyState()) waitFor(() => { expect(initialInputCurrency?.isNative).toEqual(true) expect(initialOutputCurrency).not.toBeDefined() - expect(chainId).toEqual(137) + expect(initialChainId).toEqual(137) }) }) }) diff --git a/apps/web/src/state/swap/hooks.tsx b/apps/web/src/state/swap/hooks.tsx index cbc06eb6b1b..5445ffcab97 100644 --- a/apps/web/src/state/swap/hooks.tsx +++ b/apps/web/src/state/swap/hooks.tsx @@ -2,7 +2,6 @@ import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { Field } from 'components/swap/constants' import { useSupportedChainId } from 'constants/chains' import { NATIVE_CHAIN_ID } from 'constants/tokens' -import { useTokenBalancesQuery } from 'graphql/data/apollo/TokenBalancesProvider' import { supportedChainIdFromGQLChain } from 'graphql/data/util' import { useCurrency } from 'hooks/Tokens' import { useAccount } from 'hooks/useAccount' @@ -10,6 +9,7 @@ import useAutoSlippageTolerance from 'hooks/useAutoSlippageTolerance' import { useDebouncedTrade } from 'hooks/useDebouncedTrade' import useParsedQueryString from 'hooks/useParsedQueryString' import { useSwapTaxes } from 'hooks/useSwapTaxes' +import { useTokenBalances } from 'hooks/useTokenBalances' import { useUSDPrice } from 'hooks/useUSDPrice' import { Trans } from 'i18n' import useNativeCurrency from 'lib/hooks/useNativeCurrency' @@ -39,7 +39,17 @@ export function useSwapContext() { } export function useSwapAndLimitContext() { - return useContext(SwapAndLimitContext) + const account = useAccount() + const context = useContext(SwapAndLimitContext) + + // Certain components are used both inside the swap and limit context, and outside of it. + // One example is the CurrencySearch component, which is used in the swap context, but also in + // the add/remove liquidity flows, nft flows, etc. In these cases, we want to use the chainId + // from the provider account (hooks/useAccount), instead of the swap context chainId. + return { + ...context, + chainId: context.isSwapAndLimitContext ? context.chainId : account.chainId, + } } export function useSwapActionHandlers(): { @@ -79,7 +89,7 @@ export function useSwapActionHandlers(): { })) } }, - [currencyState, setCurrencyState, setSwapState] + [currencyState, setCurrencyState, setSwapState], ) const onSwitchTokens = useCallback( @@ -108,7 +118,7 @@ export function useSwapActionHandlers(): { outputCurrency: prev.inputCurrency, })) }, - [setCurrencyState, setSwapState, swapState.independentField] + [setCurrencyState, setSwapState, swapState.independentField], ) const onUserInput = useCallback( @@ -121,7 +131,7 @@ export function useSwapActionHandlers(): { } }) }, - [setSwapState] + [setSwapState], ) return { @@ -134,30 +144,30 @@ export function useSwapActionHandlers(): { // from the current swap inputs, compute the best trade and return it. export function useDerivedSwapInfo(state: SwapState): SwapInfo { const account = useAccount() - const { chainId } = useSwapAndLimitContext() - const nativeCurrency = useNativeCurrency(chainId) - const balance = useCurrencyBalance(account.address, nativeCurrency, chainId) - const { + chainId, currencyState: { inputCurrency, outputCurrency }, } = useSwapAndLimitContext() + const nativeCurrency = useNativeCurrency(chainId) + const balance = useCurrencyBalance(account.address, nativeCurrency) + const { independentField, typedValue } = state const { inputTax, outputTax } = useSwapTaxes( inputCurrency?.isToken ? inputCurrency.address : undefined, - outputCurrency?.isToken ? outputCurrency.address : undefined + outputCurrency?.isToken ? outputCurrency.address : undefined, + chainId, ) const relevantTokenBalances = useCurrencyBalances( account.address, useMemo(() => [inputCurrency ?? undefined, outputCurrency ?? undefined], [inputCurrency, outputCurrency]), - chainId ) const isExactIn: boolean = independentField === Field.INPUT const parsedAmount = useMemo( () => tryParseCurrencyAmount(typedValue, (isExactIn ? inputCurrency : outputCurrency) ?? undefined), - [inputCurrency, isExactIn, outputCurrency, typedValue] + [inputCurrency, isExactIn, outputCurrency, typedValue], ) const trade: { @@ -169,7 +179,7 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo { parsedAmount, (isExactIn ? outputCurrency : inputCurrency) ?? undefined, state.routerPreferenceOverride as RouterPreference.API | undefined, - account.address + account.address, ) const { data: nativeCurrencyBalanceUSD } = useUSDPrice(balance, nativeCurrency) @@ -178,7 +188,7 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo { isSubmittableTrade(trade.trade) && trade.trade.swapFee ? CurrencyAmount.fromRawAmount(trade.trade.outputAmount.currency, trade.trade.swapFee.amount) : undefined, - trade.trade?.outputAmount.currency + trade.trade?.outputAmount.currency, ) const currencyBalances = useMemo( @@ -186,7 +196,7 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo { [Field.INPUT]: relevantTokenBalances[0], [Field.OUTPUT]: relevantTokenBalances[1], }), - [relevantTokenBalances] + [relevantTokenBalances], ) const currencies: { [field in Field]?: Currency } = useMemo( @@ -194,7 +204,7 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo { [Field.INPUT]: inputCurrency, [Field.OUTPUT]: outputCurrency, }), - [inputCurrency, outputCurrency] + [inputCurrency, outputCurrency], ) // allowed slippage for classic trades is either auto slippage, or custom user defined slippage if auto slippage disabled @@ -296,7 +306,7 @@ export function useDerivedSwapInfo(state: SwapState): SwapInfo { trade, inputTax, outputTax, - ] + ], ) } @@ -333,7 +343,7 @@ export function queryParametersToCurrencyState(parsedQs: ParsedQs): SerializedCu export function useInitialCurrencyState(): { initialInputCurrency?: Currency initialOutputCurrency?: Currency - chainId: InterfaceChainId + initialChainId: InterfaceChainId } { const multichainUXEnabled = useFeatureFlag(FeatureFlags.MultichainUX) @@ -346,32 +356,32 @@ export function useInitialCurrencyState(): { const supportedChainId = useSupportedChainId(parsedCurrencyState.chainId ?? account.chainId) ?? UniverseChainId.Mainnet - const { data: balanceQuery } = useTokenBalancesQuery({ cacheOnly: !multichainUXEnabled }) - const balances = balanceQuery?.portfolios?.[0]?.tokenBalances - const { initialInputCurrencyAddress, chainId } = useMemo(() => { + const { balanceList } = useTokenBalances({ cacheOnly: true }) + + const { initialInputCurrencyAddress, initialChainId } = useMemo(() => { // Handle query params or disconnected state if (parsedCurrencyState.inputCurrencyId) { return { initialInputCurrencyAddress: parsedCurrencyState.inputCurrencyId, - chainId: supportedChainId, + initialChainId: supportedChainId, } } else if ( !multichainUXEnabled || !account.isConnected || - !balances || + !balanceList || parsedCurrencyState.chainId || parsedCurrencyState.outputCurrencyId ) { return { initialInputCurrencyAddress: parsedCurrencyState.outputCurrencyId ? undefined : 'ETH', - chainId: supportedChainId, + initialChainId: supportedChainId, } } // If no query params & connected, return the native token where user has the highest USD value let highestBalance = 0 let highestBalanceNativeTokenAddress = 'ETH' let highestBalanceChainId = UniverseChainId.Mainnet - balances.forEach((balance) => { + balanceList.forEach((balance) => { if ( balance?.token?.standard === NATIVE_CHAIN_ID && balance?.denominatedValue?.value && @@ -382,10 +392,10 @@ export function useInitialCurrencyState(): { highestBalanceChainId = supportedChainIdFromGQLChain(balance.token.chain) ?? UniverseChainId.Mainnet } }) - return { initialInputCurrencyAddress: highestBalanceNativeTokenAddress, chainId: highestBalanceChainId } + return { initialInputCurrencyAddress: highestBalanceNativeTokenAddress, initialChainId: highestBalanceChainId } }, [ account.isConnected, - balances, + balanceList, multichainUXEnabled, parsedCurrencyState.chainId, parsedCurrencyState.inputCurrencyId, @@ -398,10 +408,10 @@ export function useInitialCurrencyState(): { initialInputCurrencyAddress === parsedCurrencyState.outputCurrencyId // clear output if identical ? undefined : parsedCurrencyState.outputCurrencyId, - [initialInputCurrencyAddress, parsedCurrencyState.outputCurrencyId] + [initialInputCurrencyAddress, parsedCurrencyState.outputCurrencyId], ) - const initialInputCurrency = useCurrency(initialInputCurrencyAddress, chainId) - const initialOutputCurrency = useCurrency(initialOutputCurrencyAddress, chainId) + const initialInputCurrency = useCurrency(initialInputCurrencyAddress, initialChainId) + const initialOutputCurrency = useCurrency(initialOutputCurrencyAddress, initialChainId) - return { initialInputCurrency, initialOutputCurrency, chainId } + return { initialInputCurrency, initialOutputCurrency, initialChainId } } diff --git a/apps/web/src/state/swap/types.ts b/apps/web/src/state/swap/types.ts index 5abc7e79de1..48101fcb616 100644 --- a/apps/web/src/state/swap/types.ts +++ b/apps/web/src/state/swap/types.ts @@ -80,10 +80,16 @@ type SwapAndLimitContextType = { setCurrencyState: Dispatch> currentTab: SwapTab setCurrentTab: Dispatch> - // The chainId of the page/context - can be different from the connected Chain ID if the - // page is displaying content for a different chain or if multichain UX is enabled + // The chainId of the context - can be different from the connected Chain ID + // if multichain UX is enabled, otherwise it will be the same as the connected chain ID chainId?: InterfaceChainId + // The initial chain ID - used by TDP and PDP pages to keep swap scoped to the initial chain + initialChainId?: InterfaceChainId multichainUXEnabled?: boolean + // Components may use swap and limit context while outside of the context + // this flag is used to determine if we should fallback to account.chainId + // instead of using the context chainId + isSwapAndLimitContext: boolean } export const SwapAndLimitContext = createContext({ @@ -98,9 +104,11 @@ export const SwapAndLimitContext = createContext({ outputCurrency: undefined, }, chainId: UniverseChainId.Mainnet, + initialChainId: UniverseChainId.Mainnet, currentTab: SwapTab.Swap, setCurrentTab: () => undefined, multichainUXEnabled: false, + isSwapAndLimitContext: false, }) export interface SerializedCurrencyState { diff --git a/apps/web/src/state/transactions/hooks.test.tsx b/apps/web/src/state/transactions/hooks.test.tsx index d6982e345e8..14fa9999dc2 100644 --- a/apps/web/src/state/transactions/hooks.test.tsx +++ b/apps/web/src/state/transactions/hooks.test.tsx @@ -76,7 +76,7 @@ describe('Transactions hooks', () => { chainId: UniverseChainId.Mainnet, hash: pendingTransactionResponse.hash, status: TransactionStatus.Confirmed, - }) + }), ) }) } diff --git a/apps/web/src/state/transactions/hooks.tsx b/apps/web/src/state/transactions/hooks.tsx index b9075e4b6cf..ea291a8b0fb 100644 --- a/apps/web/src/state/transactions/hooks.tsx +++ b/apps/web/src/state/transactions/hooks.tsx @@ -22,7 +22,7 @@ import { WEB_SUPPORTED_CHAIN_IDS } from 'uniswap/src/types/chains' export function useTransactionAdder(): ( response: TransactionResponse, info: TransactionInfo, - deadline?: number + deadline?: number, ) => void { const account = useAccount() const dispatch = useAppDispatch() @@ -37,9 +37,10 @@ export function useTransactionAdder(): ( if (!hash) { throw Error('No transaction hash found.') } - dispatch(addTransaction({ hash, from: account.address, info, chainId: account.chainId, nonce, deadline })) + const chainId = ('chainId' in info && info.chainId) || account.chainId + dispatch(addTransaction({ hash, from: account.address, info, chainId, nonce, deadline })) }, - [account.address, account.chainId, account.status, dispatch] + [account.address, account.chainId, account.status, dispatch], ) } @@ -55,7 +56,7 @@ export function useTransactionRemover() { dispatch(removeTransaction({ hash, chainId: account.chainId })) }, - [account.chainId, account.status, dispatch] + [account.chainId, account.status, dispatch], ) } @@ -66,7 +67,7 @@ export function useTransactionCanceller() { (hash: string, chainId: number, cancelHash: string) => { dispatch(cancelTransaction({ hash, chainId, cancelHash })) }, - [dispatch] + [dispatch], ) } @@ -75,7 +76,7 @@ export function useMultichainTransactions(): [TransactionDetails, SupportedInter return WEB_SUPPORTED_CHAIN_IDS.flatMap((chainId) => state[chainId] ? Object.values(state[chainId]).map((tx): [TransactionDetails, SupportedInterfaceChainId] => [tx, chainId]) - : [] + : [], ) } @@ -177,8 +178,8 @@ export function usePendingTransactions(): PendingTransactionDetails[] { return useMemo( () => Object.values(allTransactions).filter( - (tx): tx is PendingTransactionDetails => tx.from === account.address && isPendingTx(tx) + (tx): tx is PendingTransactionDetails => tx.from === account.address && isPendingTx(tx), ), - [account.address, allTransactions] + [account.address, allTransactions], ) } diff --git a/apps/web/src/state/transactions/reducer.ts b/apps/web/src/state/transactions/reducer.ts index eb7920147b6..5a96bd5a1ac 100644 --- a/apps/web/src/state/transactions/reducer.ts +++ b/apps/web/src/state/transactions/reducer.ts @@ -21,7 +21,7 @@ const transactionSlice = createSlice({ transactions, { payload: { chainId, hash, ...details }, - }: { payload: { chainId: InterfaceChainId } & Omit } + }: { payload: { chainId: InterfaceChainId } & Omit }, ) { if (transactions[chainId]?.[hash]) { throw Error('Attempted to add existing transaction.') @@ -43,7 +43,7 @@ const transactionSlice = createSlice({ }, removeTransaction( transactions, - { payload: { chainId, hash } }: { payload: { chainId: InterfaceChainId; hash: string } } + { payload: { chainId, hash } }: { payload: { chainId: InterfaceChainId; hash: string } }, ) { if (transactions[chainId][hash]) { delete transactions[chainId][hash] @@ -53,7 +53,7 @@ const transactionSlice = createSlice({ transactions, { payload: { chainId, hash, blockNumber }, - }: { payload: { chainId: InterfaceChainId; hash: string; blockNumber: number } } + }: { payload: { chainId: InterfaceChainId; hash: string; blockNumber: number } }, ) { const tx = transactions[chainId]?.[hash] if (!tx || tx.status !== TransactionStatus.Pending) { @@ -76,7 +76,7 @@ const transactionSlice = createSlice({ status: TransactionStatus info?: TransactionInfo } - } + }, ) { const tx = transactions[chainId]?.[hash] if (!tx) { @@ -93,7 +93,7 @@ const transactionSlice = createSlice({ transactions, { payload: { chainId, hash, cancelHash }, - }: { payload: { chainId: InterfaceChainId; hash: string; cancelHash: string } } + }: { payload: { chainId: InterfaceChainId; hash: string; cancelHash: string } }, ) { const tx = transactions[chainId]?.[hash] diff --git a/apps/web/src/state/user/hooks.tsx b/apps/web/src/state/user/hooks.tsx index 1ea48e620ae..5b4c0909528 100644 --- a/apps/web/src/state/user/hooks.tsx +++ b/apps/web/src/state/user/hooks.tsx @@ -39,7 +39,7 @@ export function useUserLocaleManager(): [SupportedLocale | null, (newLocale: Sup (newLocale: SupportedLocale) => { dispatch(updateUserLocale({ userLocale: newLocale })) }, - [dispatch] + [dispatch], ) return [locale, setLocale] @@ -54,7 +54,7 @@ export function useRouterPreference(): [RouterPreference, (routerPreference: Rou (newRouterPreference: RouterPreference) => { dispatch(updateUserRouterPreference({ userRouterPreference: newRouterPreference })) }, - [dispatch] + [dispatch], ) return [routerPreference, setRouterPreference] @@ -65,7 +65,7 @@ export function useRouterPreference(): [RouterPreference, (routerPreference: Rou */ export function useUserSlippageTolerance(): [ Percent | SlippageTolerance.Auto, - (slippageTolerance: Percent | SlippageTolerance.Auto) => void + (slippageTolerance: Percent | SlippageTolerance.Auto) => void, ] { const userSlippageToleranceRaw = useAppSelector((state) => { return state.user.userSlippageTolerance @@ -77,7 +77,7 @@ export function useUserSlippageTolerance(): [ userSlippageToleranceRaw === SlippageTolerance.Auto ? SlippageTolerance.Auto : new Percent(userSlippageToleranceRaw, 10_000), - [userSlippageToleranceRaw] + [userSlippageToleranceRaw], ) const dispatch = useAppDispatch() @@ -95,10 +95,10 @@ export function useUserSlippageTolerance(): [ dispatch( updateUserSlippageTolerance({ userSlippageTolerance: value, - }) + }), ) }, - [dispatch] + [dispatch], ) return [userSlippageTolerance, setUserSlippageTolerance] @@ -122,7 +122,7 @@ export function useUserHideClosedPositions(): [boolean, (newHideClosedPositions: (newHideClosedPositions: boolean) => { dispatch(updateHideClosedPositions({ userHideClosedPositions: newHideClosedPositions })) }, - [dispatch] + [dispatch], ) return [hideClosedPositions, setHideClosedPositions] @@ -139,7 +139,7 @@ export function useUserTransactionTTL(): [number, (slippage: number) => void] { (userDeadline: number) => { dispatch(updateUserDeadline({ userDeadline })) }, - [dispatch] + [dispatch], ) return [deadline, setUserDeadline] @@ -151,7 +151,7 @@ export function useAddUserToken(): (token: Token) => void { (token: Token) => { dispatch(addSerializedToken({ serializedToken: serializeToken(token) })) }, - [dispatch] + [dispatch], ) } @@ -169,7 +169,7 @@ export function usePairAdder(): (pair: Pair) => void { (pair: Pair) => { dispatch(addSerializedPair({ serializedPair: serializePair(pair) })) }, - [dispatch] + [dispatch], ) } @@ -194,7 +194,7 @@ export function toV2LiquidityToken([tokenA, tokenB]: [Token, Token]): Token { computePairAddress({ factoryAddress: V2_FACTORY_ADDRESSES[tokenA.chainId], tokenA, tokenB }), 18, 'UNI-V2', - 'Uniswap V2' + 'Uniswap V2', ) } @@ -243,7 +243,7 @@ export function useTrackedTokenPairs(): [Token, Token][] { ) }) : [], - [popularTokens, chainId] + [popularTokens, chainId], ) // pairs saved by users @@ -265,7 +265,7 @@ export function useTrackedTokenPairs(): [Token, Token][] { const combinedList = useMemo( () => userPairs.concat(generatedPairs).concat(pinnedPairs), - [pinnedPairs, userPairs, generatedPairs] + [pinnedPairs, userPairs, generatedPairs], ) return useMemo(() => { diff --git a/apps/web/src/state/user/utils.ts b/apps/web/src/state/user/utils.ts index de3e45ec52f..bda12b8a450 100644 --- a/apps/web/src/state/user/utils.ts +++ b/apps/web/src/state/user/utils.ts @@ -17,6 +17,6 @@ export function deserializeToken(serializedToken: SerializedToken, Class: typeof serializedToken.address, serializedToken.decimals, serializedToken.symbol, - serializedToken.name + serializedToken.name, ) } diff --git a/apps/web/src/state/wallets/hooks.tsx b/apps/web/src/state/wallets/hooks.tsx index 047dd83b947..ba3bad7b518 100644 --- a/apps/web/src/state/wallets/hooks.tsx +++ b/apps/web/src/state/wallets/hooks.tsx @@ -10,7 +10,7 @@ export function useConnectedWallets(): [Wallet[], (wallet: Wallet) => void] { (wallet: Wallet) => { dispatch(addConnectedWallet(wallet)) }, - [dispatch] + [dispatch], ) return [connectedWallets, addWallet] } diff --git a/apps/web/src/test-utils/bundle-size-test.ts b/apps/web/src/test-utils/bundle-size-test.ts index b0225ea038f..503cce3adc1 100644 --- a/apps/web/src/test-utils/bundle-size-test.ts +++ b/apps/web/src/test-utils/bundle-size-test.ts @@ -29,7 +29,7 @@ const entryGzipSize = report.reduce( acc + // only collect the entry point size to keep things simple (r.isInitialByEntrypoint?.main ? r.gzipSize || 0 : 0), - 0 + 0, ) // somewhat arbitrary, just a bit above where we are currently (6/12/2024) diff --git a/apps/web/src/test-utils/constants.ts b/apps/web/src/test-utils/constants.ts index 4780e42a103..69f36b99fb4 100644 --- a/apps/web/src/test-utils/constants.ts +++ b/apps/web/src/test-utils/constants.ts @@ -51,7 +51,7 @@ export const TEST_POOL_12 = new Pool( FeeAmount.HIGH, '2437312313659959819381354528', '10272714736694327408', - -69633 + -69633, ) export const TEST_POOL_13 = new Pool( @@ -60,7 +60,7 @@ export const TEST_POOL_13 = new Pool( FeeAmount.MEDIUM, '2437312313659959819381354528', '10272714736694327408', - -69633 + -69633, ) export const toCurrencyAmount = (token: Token, amount: number) => @@ -193,7 +193,7 @@ const SELL_FEE_TOKEN = new Token( 'Abc', false, undefined, - BigNumber.from(300) + BigNumber.from(300), ) const TEST_POOL_FOT_1 = new Pool( SELL_FEE_TOKEN, @@ -201,7 +201,7 @@ const TEST_POOL_FOT_1 = new Pool( FeeAmount.HIGH, '2437312313659959819381354528', '10272714736694327408', - -69633 + -69633, ) export const TEST_TRADE_FEE_ON_SELL = new ClassicTrade({ v3Routes: [ @@ -226,7 +226,7 @@ const BUY_FEE_TOKEN = new Token( 'Def', false, BigNumber.from(300), - undefined + undefined, ) const TEST_POOL_FOT_2 = new Pool( TEST_TOKEN_1, @@ -234,7 +234,7 @@ const TEST_POOL_FOT_2 = new Pool( FeeAmount.HIGH, '2437312313659959819381354528', '10272714736694327408', - -69633 + -69633, ) export const TEST_TRADE_FEE_ON_BUY = new ClassicTrade({ v3Routes: [ diff --git a/apps/web/src/test-utils/matchers.test.tsx b/apps/web/src/test-utils/matchers.test.tsx index 2536ee144df..2e68dbb8344 100644 --- a/apps/web/src/test-utils/matchers.test.tsx +++ b/apps/web/src/test-utils/matchers.test.tsx @@ -14,7 +14,7 @@ describe('matchers', () => { render(
test
-
+
, ) expect(screen.getByText('test')).not.toBeVisible() }) diff --git a/apps/web/src/test-utils/pools/fixtures.ts b/apps/web/src/test-utils/pools/fixtures.ts index 3f8d11be110..c8c8e5f19a4 100644 --- a/apps/web/src/test-utils/pools/fixtures.ts +++ b/apps/web/src/test-utils/pools/fixtures.ts @@ -69,7 +69,7 @@ const pool = new Pool( FeeAmount.MEDIUM, '1851127709498178402383049949138810', '7076437181775065414', - 201189 + 201189, ) const position = new Position({ diff --git a/apps/web/src/test-utils/render.tsx b/apps/web/src/test-utils/render.tsx index d895d718acf..36572a97e06 100644 --- a/apps/web/src/test-utils/render.tsx +++ b/apps/web/src/test-utils/render.tsx @@ -59,7 +59,7 @@ const customRender = (ui: ReactElement, options?: CustomRenderOptions) => { type CustomRenderHookOptions = Omit, 'wrapper'> const customRenderHook = ( hook: (initialProps: Props) => Result, - options?: CustomRenderHookOptions + options?: CustomRenderHookOptions, ) => { return renderHook(hook, { ...options, wrapper: WithProviders as WrapperComponent }) } diff --git a/apps/web/src/test-utils/tokens/mocks.ts b/apps/web/src/test-utils/tokens/mocks.ts index baa3603da72..069d96dd07c 100644 --- a/apps/web/src/test-utils/tokens/mocks.ts +++ b/apps/web/src/test-utils/tokens/mocks.ts @@ -66,7 +66,7 @@ beforeEach(() => { return TEST_TOKEN_3_INFO?.currency } return COMMON_BASES[chainId ?? UniverseChainId.Mainnet]?.find((base) => - base.currency.isNative ? base.currency.symbol === 'ETH' : base.currency.address === currencyId + base.currency.isNative ? base.currency.symbol === 'ETH' : base.currency.address === currencyId, )?.currency }) mocked(useCurrency).mockImplementation((address?: string, chainId?: InterfaceChainId) => { @@ -104,7 +104,7 @@ beforeEach(() => { return TEST_TOKEN_3_INFO?.currency } return COMMON_BASES[chainId ?? UniverseChainId.Mainnet]?.find((base) => - base.currency.isNative ? base.currency.symbol === 'ETH' : base.currency.address === address + base.currency.isNative ? base.currency.symbol === 'ETH' : base.currency.address === address, )?.currency }) mocked(useCurrencyInfo).mockImplementation((currency?: Currency | string) => { diff --git a/apps/web/src/theme/components/FadePresence.tsx b/apps/web/src/theme/components/FadePresence.tsx index 3dc3becb898..44e76393c36 100644 --- a/apps/web/src/theme/components/FadePresence.tsx +++ b/apps/web/src/theme/components/FadePresence.tsx @@ -119,7 +119,8 @@ const FadeWrapper = styled.div<{ $animationDelay?: string $zIndex?: number }>` - transition: display + transition: + display ${({ theme, $transitionDuration }) => `${$transitionDuration ?? theme.transition.duration.medium} ${theme.transition.timing.inOut}`}, transform diff --git a/apps/web/src/theme/components/ThemeToggle.tsx b/apps/web/src/theme/components/ThemeToggle.tsx index ba1a623366d..22dad735b83 100644 --- a/apps/web/src/theme/components/ThemeToggle.tsx +++ b/apps/web/src/theme/components/ThemeToggle.tsx @@ -36,7 +36,7 @@ const CompactOptionPill = styled.div` // Tracks the device theme const systemThemeAtom = atom( - DARKMODE_MEDIA_QUERY.matches ? ThemeMode.DARK : ThemeMode.LIGHT + DARKMODE_MEDIA_QUERY.matches ? ThemeMode.DARK : ThemeMode.LIGHT, ) // Tracks the user's selected theme mode @@ -49,7 +49,7 @@ export function SystemThemeUpdater() { (event: MediaQueryListEvent) => { setSystemTheme(event.matches ? ThemeMode.DARK : ThemeMode.LIGHT) }, - [setSystemTheme] + [setSystemTheme], ) useEffect(() => { @@ -162,7 +162,7 @@ export function ThemeSelector({ disabled, compact = false }: { disabled?: boolea // Switch feels less jittery with short delay !disabled && setTimeout(() => setMode(mode as ThemeMode), THEME_UPDATE_DELAY) }, - [disabled, setMode] + [disabled, setMode], ) return ( diff --git a/apps/web/src/theme/components/index.tsx b/apps/web/src/theme/components/index.tsx index add8b950a14..32930e27e6c 100644 --- a/apps/web/src/theme/components/index.tsx +++ b/apps/web/src/theme/components/index.tsx @@ -150,7 +150,7 @@ export function ExternalLink({ rest.onClick(event) } }, - [rest] + [rest], ) return } @@ -279,7 +279,7 @@ export const CopyHelper = forwardRef( iconColor = 'currentColor', children, }: CopyHelperProps, - ref + ref, ) => { const [isCopied, setCopied] = useCopyClipboard() const copy = useCallback(() => { @@ -323,7 +323,7 @@ export const CopyHelper = forwardRef( {iconPosition === 'right' && Icon && } ) - } + }, ) CopyHelper.displayName = 'CopyHelper' diff --git a/apps/web/src/theme/index.tsx b/apps/web/src/theme/index.tsx index a268abb8026..c2793c8ee06 100644 --- a/apps/web/src/theme/index.tsx +++ b/apps/web/src/theme/index.tsx @@ -20,7 +20,7 @@ export const MEDIA_WIDTHS = { const MAX_CONTENT_WIDTH_PX = 1200 const deprecated_mediaWidthTemplates: { [width in keyof typeof MEDIA_WIDTHS]: typeof css } = Object.keys( - MEDIA_WIDTHS + MEDIA_WIDTHS, ).reduce((acc, size) => { acc[size] = (a: any, b: any, c: any) => css` @media (max-width: ${(MEDIA_WIDTHS as any)[size]}px) { diff --git a/apps/web/src/tracing/amplitude.ts b/apps/web/src/tracing/amplitude.ts new file mode 100644 index 00000000000..af2ab97f6dc --- /dev/null +++ b/apps/web/src/tracing/amplitude.ts @@ -0,0 +1,22 @@ +import store from 'state' +import { setOriginCountry } from 'state/user/reducer' +import { uniswapUrls } from 'uniswap/src/constants/urls' +import { ApplicationTransport } from 'utilities/src/telemetry/analytics/ApplicationTransport' +// eslint-disable-next-line no-restricted-imports +import { OriginApplication } from '@uniswap/analytics' +// eslint-disable-next-line no-restricted-imports +import { analytics, getAnalyticsAtomDirect } from 'utilities/src/telemetry/analytics/analytics' + +export function setupAmplitude() { + getAnalyticsAtomDirect(true).then((allowAnalytics) => { + analytics.init( + new ApplicationTransport({ + serverUrl: uniswapUrls.amplitudeProxyUrl, + appOrigin: OriginApplication.INTERFACE, + reportOriginCountry: (country: string) => store.dispatch(setOriginCountry(country)), + }), + allowAnalytics, + process.env.REACT_APP_GIT_COMMIT_HASH, + ) + }) +} diff --git a/apps/web/src/tracing/index.ts b/apps/web/src/tracing/index.ts index e5545ef3577..11ca56bd626 100644 --- a/apps/web/src/tracing/index.ts +++ b/apps/web/src/tracing/index.ts @@ -1,58 +1,14 @@ -import 'zone.js' +import { setupAmplitude } from 'tracing/amplitude' +import { setupSentry } from 'tracing/sentry' +import { setupDatadog } from 'utilities/src/logger/Datadog' +import { isRemoteReportingEnabled } from 'utils/env' -import { BrowserTracing } from '@sentry/browser' -import * as Sentry from '@sentry/react' -// eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { OriginApplication } from '@uniswap/analytics' -import store from 'state' -import { setOriginCountry } from 'state/user/reducer' -import { beforeSend } from 'tracing/errors' -import { patchFetch } from 'tracing/request' -import { uniswapUrls } from 'uniswap/src/constants/urls' -import { ApplicationTransport } from 'utilities/src/telemetry/analytics/ApplicationTransport' -// eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { analytics, getAnalyticsAtomDirect } from 'utilities/src/telemetry/analytics/analytics' -import { getEnvName, isSentryEnabled } from 'utils/env' -import { v4 as uuidv4 } from 'uuid' +if (isRemoteReportingEnabled()) { + // Dump some metadata into the window to allow client verification. + window.GIT_COMMIT_HASH = process.env.REACT_APP_GIT_COMMIT_HASH -patchFetch(global) - -// Dump some metadata into the window to allow client verification. -window.GIT_COMMIT_HASH = process.env.REACT_APP_GIT_COMMIT_HASH - -// This is used to identify the user in Sentry. -const SENTRY_USER_ID_KEY = 'sentry-user-id' - -Sentry.init({ - dsn: process.env.REACT_APP_SENTRY_DSN, - release: process.env.REACT_APP_GIT_COMMIT_HASH, - environment: getEnvName(), - enabled: isSentryEnabled(), - tracesSampleRate: Number(process.env.REACT_APP_SENTRY_TRACES_SAMPLE_RATE ?? 0), - integrations: [ - // Instruments pageload (and any requests that it depends on): - new BrowserTracing({ - startTransactionOnLocationChange: false, - startTransactionOnPageLoad: true, - }), - ], - beforeSend, -}) - -let sentryUserId = localStorage.getItem(SENTRY_USER_ID_KEY) -if (!sentryUserId) { - localStorage.setItem(SENTRY_USER_ID_KEY, (sentryUserId = uuidv4())) + setupDatadog() + setupSentry() } -Sentry.setUser({ id: sentryUserId }) -getAnalyticsAtomDirect(true).then((allowAnalytics) => { - analytics.init( - new ApplicationTransport({ - serverUrl: uniswapUrls.amplitudeProxyUrl, - appOrigin: OriginApplication.INTERFACE, - reportOriginCountry: (country: string) => store.dispatch(setOriginCountry(country)), - }), - allowAnalytics, - process.env.REACT_APP_GIT_COMMIT_HASH - ) -}) +setupAmplitude() diff --git a/apps/web/src/tracing/sentry.ts b/apps/web/src/tracing/sentry.ts new file mode 100644 index 00000000000..d7699fdedaf --- /dev/null +++ b/apps/web/src/tracing/sentry.ts @@ -0,0 +1,34 @@ +import { BrowserTracing } from '@sentry/browser' +import * as Sentry from '@sentry/react' +import { beforeSend } from 'tracing/errors' +import { v4 as uuidv4 } from 'uuid' +// eslint-disable-next-line @typescript-eslint/no-restricted-imports +import { getEnvName } from 'utilities/src/environment' + +const SENTRY_USER_ID_KEY = 'sentry-user-id' + +export function setupSentry() { + // setup Sentry + Sentry.init({ + dsn: process.env.REACT_APP_SENTRY_DSN, + release: process.env.REACT_APP_GIT_COMMIT_HASH, + environment: getEnvName(), + enabled: true, + tracesSampleRate: Number(process.env.REACT_APP_SENTRY_TRACES_SAMPLE_RATE ?? 0), + integrations: [ + // Instruments pageload (and any requests that it depends on): + new BrowserTracing({ + startTransactionOnLocationChange: false, + startTransactionOnPageLoad: true, + }), + ], + beforeSend, + }) + + // This is used to identify the user in Sentry. + let sentryUserId = localStorage.getItem(SENTRY_USER_ID_KEY) + if (!sentryUserId) { + localStorage.setItem(SENTRY_USER_ID_KEY, (sentryUserId = uuidv4())) + } + Sentry.setUser({ id: sentryUserId }) +} diff --git a/apps/web/src/tracing/swapFlowLoggers.ts b/apps/web/src/tracing/swapFlowLoggers.ts index a1bd3d60457..06c1ac30494 100644 --- a/apps/web/src/tracing/swapFlowLoggers.ts +++ b/apps/web/src/tracing/swapFlowLoggers.ts @@ -34,7 +34,7 @@ export function maybeLogFirstSwapAction(analyticsContext: ITraceContext) { export function logSwapQuoteRequest( chainId: number, routerPreference: RouterPreference | typeof INTERNAL_ROUTER_PREFERENCE_PRICE, - isQuickRoute?: boolean + isQuickRoute?: boolean, ) { let performanceMetrics = {} if (routerPreference !== INTERNAL_ROUTER_PREFERENCE_PRICE) { diff --git a/apps/web/src/tracing/utils.ts b/apps/web/src/tracing/utils.ts index f74939ee729..1d6d20353f7 100644 --- a/apps/web/src/tracing/utils.ts +++ b/apps/web/src/tracing/utils.ts @@ -4,7 +4,7 @@ */ export function calculateElapsedTimeWithPerformanceMarkMs( markName: string, - fallbackStartTime?: number + fallbackStartTime?: number, ): number | undefined { const elapsedTime = performance.mark(markName) if (elapsedTime) { diff --git a/apps/web/src/utils/approveAmountCalldata.ts b/apps/web/src/utils/approveAmountCalldata.ts index 7440b37f0ba..5e6cd5db295 100644 --- a/apps/web/src/utils/approveAmountCalldata.ts +++ b/apps/web/src/utils/approveAmountCalldata.ts @@ -20,7 +20,7 @@ const ERC20_INTERFACE = new Interface([ export default function approveAmountCalldata( amount: CurrencyAmount, - spender: string + spender: string, ): { to: string; data: string; value: '0x0' } { if (!amount.currency.isToken) { throw new Error('Must call with an amount of token') diff --git a/apps/web/src/utils/computeFiatValuePriceImpact.tsx b/apps/web/src/utils/computeFiatValuePriceImpact.tsx index cd9df2c0555..69c619586c7 100644 --- a/apps/web/src/utils/computeFiatValuePriceImpact.tsx +++ b/apps/web/src/utils/computeFiatValuePriceImpact.tsx @@ -3,7 +3,7 @@ import { BIPS_BASE } from 'constants/misc' export function computeFiatValuePriceImpact( fiatValueInput: number | undefined | null, - fiatValueOutput: number | undefined | null + fiatValueOutput: number | undefined | null, ): Percent | undefined { if (!fiatValueOutput || !fiatValueInput) { return undefined diff --git a/apps/web/src/utils/computeSurroundingTicks.ts b/apps/web/src/utils/computeSurroundingTicks.ts index 93333fb6078..becd695a7ce 100644 --- a/apps/web/src/utils/computeSurroundingTicks.ts +++ b/apps/web/src/utils/computeSurroundingTicks.ts @@ -13,7 +13,7 @@ export default function computeSurroundingTicks( activeTickProcessed: TickProcessed, sortedTickData: Ticks, pivot: number, - ascending: boolean + ascending: boolean, ): TickProcessed[] { let previousTickProcessed: TickProcessed = { ...activeTickProcessed, @@ -39,13 +39,13 @@ export default function computeSurroundingTicks( if (ascending) { currentTickProcessed.liquidityActive = JSBI.add( previousTickProcessed.liquidityActive, - JSBI.BigInt(sortedTickData[i]?.liquidityNet ?? 0) + JSBI.BigInt(sortedTickData[i]?.liquidityNet ?? 0), ) } else if (!ascending && JSBI.notEqual(previousTickProcessed.liquidityNet, JSBI.BigInt(0))) { // We are iterating descending, so look at the previous tick and apply any net liquidity. currentTickProcessed.liquidityActive = JSBI.subtract( previousTickProcessed.liquidityActive, - previousTickProcessed.liquidityNet + previousTickProcessed.liquidityNet, ) } diff --git a/apps/web/src/utils/computeUniCirculation.ts b/apps/web/src/utils/computeUniCirculation.ts index 75ab044fd32..d63b1822795 100644 --- a/apps/web/src/utils/computeUniCirculation.ts +++ b/apps/web/src/utils/computeUniCirculation.ts @@ -45,8 +45,8 @@ function withVesting(before: JSBI, time: BigNumber, amount: number, start: numbe before, JSBI.divide( JSBI.multiply(JSBI.BigInt(amount), JSBI.BigInt(time.sub(start).toString())), - JSBI.subtract(JSBI.BigInt(end), JSBI.BigInt(start)) - ) + JSBI.subtract(JSBI.BigInt(end), JSBI.BigInt(start)), + ), ) } } @@ -57,7 +57,7 @@ function withVesting(before: JSBI, time: BigNumber, amount: number, start: numbe export function computeUniCirculation( uni: Token, blockTimestamp: BigNumber, - unclaimedUni: CurrencyAmount | undefined + unclaimedUni: CurrencyAmount | undefined, ): CurrencyAmount { let wholeAmount = JSBI.BigInt(USERS_AMOUNT) @@ -71,28 +71,28 @@ export function computeUniCirculation( TREASURY_YEAR_1_AMOUNT, TREASURY_BEGIN_YEAR_1, TREASURY_END_YEAR_1, - TREASURY_CLIFF_YEAR_1 + TREASURY_CLIFF_YEAR_1, ) wholeAmount = withVesting( wholeAmount, blockTimestamp, TREASURY_YEAR_2_AMOUNT, TREASURY_BEGIN_YEAR_2, - TREASURY_END_YEAR_2 + TREASURY_END_YEAR_2, ) wholeAmount = withVesting( wholeAmount, blockTimestamp, TREASURY_YEAR_3_AMOUNT, TREASURY_BEGIN_YEAR_3, - TREASURY_END_YEAR_3 + TREASURY_END_YEAR_3, ) wholeAmount = withVesting( wholeAmount, blockTimestamp, TREASURY_YEAR_4_AMOUNT, TREASURY_BEGIN_YEAR_4, - TREASURY_END_YEAR_4 + TREASURY_END_YEAR_4, ) // team @@ -102,7 +102,7 @@ export function computeUniCirculation( TEAM_YEAR_1_AMOUNT, TREASURY_BEGIN_YEAR_1, TREASURY_END_YEAR_1, - TREASURY_CLIFF_YEAR_1 + TREASURY_CLIFF_YEAR_1, ) wholeAmount = withVesting(wholeAmount, blockTimestamp, TEAM_YEAR_2_AMOUNT, TREASURY_BEGIN_YEAR_2, TREASURY_END_YEAR_2) wholeAmount = withVesting(wholeAmount, blockTimestamp, TEAM_YEAR_3_AMOUNT, TREASURY_BEGIN_YEAR_3, TREASURY_END_YEAR_3) @@ -110,7 +110,7 @@ export function computeUniCirculation( const total = CurrencyAmount.fromRawAmount( uni, - JSBI.multiply(wholeAmount, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))) + JSBI.multiply(wholeAmount, JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18))), ) return unclaimedUni ? total.subtract(unclaimedUni) : total } diff --git a/apps/web/src/utils/env.test.ts b/apps/web/src/utils/env.test.ts index 166720dcc56..cf749593839 100644 --- a/apps/web/src/utils/env.test.ts +++ b/apps/web/src/utils/env.test.ts @@ -1,5 +1,4 @@ -import { isBetaEnv, isDevEnv, isProdEnv } from 'uniswap/src/utils/env' -import { isTestEnv } from 'utils/env' +import { isBetaEnv, isDevEnv, isProdEnv, isTestEnv } from 'utilities/src/environment' describe('env', () => { const ENV = process.env diff --git a/apps/web/src/utils/env.ts b/apps/web/src/utils/env.ts index ea00d11c871..3e8af77808b 100644 --- a/apps/web/src/utils/env.ts +++ b/apps/web/src/utils/env.ts @@ -1,8 +1,4 @@ -import { isBetaEnv, isProdEnv } from 'uniswap/src/utils/env' - -export function isTestEnv(): boolean { - return process.env.NODE_ENV === 'test' -} +import { isBetaEnv, isProdEnv } from 'utilities/src/environment' function isAppUniswapOrg({ hostname }: { hostname: string }): boolean { return hostname === 'app.uniswap.org' @@ -30,7 +26,7 @@ function isLocalhost({ hostname }: { hostname: string }): boolean { return hostname === 'localhost' } -export function isSentryEnabled(): boolean { +export function isRemoteReportingEnabled(): boolean { // Disable in e2e test environments if (isBetaEnv() && !isAppUniswapStagingOrg(window.location)) { return false @@ -40,13 +36,3 @@ export function isSentryEnabled(): boolean { } return process.env.REACT_APP_SENTRY_ENABLED === 'true' } - -export function getEnvName(): 'production' | 'staging' | 'development' { - if (isBetaEnv()) { - return 'staging' - } - if (isProdEnv()) { - return 'production' - } - return 'development' -} diff --git a/apps/web/src/utils/fiatCurrency.ts b/apps/web/src/utils/fiatCurrency.ts index b23be4f05d6..d5187cf2fc6 100644 --- a/apps/web/src/utils/fiatCurrency.ts +++ b/apps/web/src/utils/fiatCurrency.ts @@ -9,7 +9,7 @@ import { Currency } from 'uniswap/src/data/graphql/uniswap-data-api/__generated_ */ export function getFiatCurrencyName( t: AppTFunction, - currency: SupportedLocalCurrency + currency: SupportedLocalCurrency, ): { name: string; shortName: string } { const currencyToCurrencyName: Record = { [Currency.Aud]: t('currency.aud'), diff --git a/apps/web/src/utils/formatCurrencyAmount.ts b/apps/web/src/utils/formatCurrencyAmount.ts index d63a9c74662..752df7b91ac 100644 --- a/apps/web/src/utils/formatCurrencyAmount.ts +++ b/apps/web/src/utils/formatCurrencyAmount.ts @@ -7,7 +7,7 @@ export function formatCurrencyAmount( amount: CurrencyAmount | undefined, sigFigs: number, locale: SupportedLocale = DEFAULT_LOCALE, - fixedDecimals?: number + fixedDecimals?: number, ): string { if (!amount) { return '-' diff --git a/apps/web/src/utils/formatNumbers.ts b/apps/web/src/utils/formatNumbers.ts index e231202d8f8..ce48a193621 100644 --- a/apps/web/src/utils/formatNumbers.ts +++ b/apps/web/src/utils/formatNumbers.ts @@ -721,7 +721,7 @@ const MAX_AMOUNT_STR_LENGTH = 9 function formatReviewSwapCurrencyAmount( amount: CurrencyAmount, - locale: SupportedLocale = DEFAULT_LOCALE + locale: SupportedLocale = DEFAULT_LOCALE, ): string { let formattedAmount = formatCurrencyAmount({ amount, type: NumberType.TokenTx, locale }) if (formattedAmount.length > MAX_AMOUNT_STR_LENGTH) { @@ -733,7 +733,7 @@ function formatReviewSwapCurrencyAmount( function convertToFiatAmount( amount = 1, toCurrency = DEFAULT_LOCAL_CURRENCY, - conversionRate = 1 + conversionRate = 1, ): { amount: number; currency: SupportedLocalCurrency } { const defaultResult = { amount, currency: DEFAULT_LOCAL_CURRENCY } @@ -762,7 +762,7 @@ type FiatCurrencyComponents = { */ export function getFiatCurrencyComponents( locale = DEFAULT_LOCALE, - localCurrency = DEFAULT_LOCAL_CURRENCY + localCurrency = DEFAULT_LOCAL_CURRENCY, ): FiatCurrencyComponents { const format = new Intl.NumberFormat(locale, { ...TWO_DECIMALS_CURRENCY, @@ -832,7 +832,7 @@ function handleFallbackCurrency( previousSelectedCurrency: SupportedLocalCurrency | undefined, previousConversionRate: number | undefined, shouldFallbackToUSD: boolean, - shouldFallbackToPrevious: boolean + shouldFallbackToPrevious: boolean, ) { if (shouldFallbackToUSD) { return DEFAULT_LOCAL_CURRENCY @@ -861,7 +861,7 @@ export function useFormatter() { previousSelectedCurrency, previousConversionRate, shouldFallbackToUSD, - shouldFallbackToPrevious + shouldFallbackToPrevious, ) const localCurrencyConversionRateToFormatWith = shouldFallbackToPrevious ? previousConversionRate @@ -876,7 +876,7 @@ export function useFormatter() { localCurrency: currencyToFormatWith, conversionRate: localCurrencyConversionRateToFormatWith, }), - [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith] + [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith], ) const formatCurrencyAmountWithLocales = useCallback( @@ -887,7 +887,7 @@ export function useFormatter() { localCurrency: currencyToFormatWith, conversionRate: localCurrencyConversionRateToFormatWith, }), - [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith] + [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith], ) const formatPriceWithLocales = useCallback( @@ -898,12 +898,12 @@ export function useFormatter() { localCurrency: currencyToFormatWith, conversionRate: localCurrencyConversionRateToFormatWith, }), - [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith] + [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith], ) const formatReviewSwapCurrencyAmountWithLocales = useCallback( (amount: CurrencyAmount) => formatReviewSwapCurrencyAmount(amount, formatterLocale), - [formatterLocale] + [formatterLocale], ) const formatTickPriceWithLocales = useCallback( @@ -914,7 +914,7 @@ export function useFormatter() { localCurrency: currencyToFormatWith, conversionRate: localCurrencyConversionRateToFormatWith, }), - [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith] + [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith], ) const formatNumberOrStringWithLocales = useCallback( @@ -925,7 +925,7 @@ export function useFormatter() { localCurrency: currencyToFormatWith, conversionRate: localCurrencyConversionRateToFormatWith, }), - [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith] + [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith], ) const formatFiatPriceWithLocales = useCallback( @@ -936,17 +936,17 @@ export function useFormatter() { localCurrency: currencyToFormatWith, conversionRate: localCurrencyConversionRateToFormatWith, }), - [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith] + [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith], ) const formatDeltaWithLocales = useCallback( (percent: Nullish) => formatDelta(percent, formatterLocale), - [formatterLocale] + [formatterLocale], ) const formatPercentWithLocales = useCallback( (percent: Percent | undefined) => formatPercent(percent, formatterLocale), - [formatterLocale] + [formatterLocale], ) const formatEtherwithLocales = useCallback( @@ -956,12 +956,12 @@ export function useFormatter() { locale: formatterLocale, localCurrency: currencyToFormatWith, }), - [currencyToFormatWith, formatterLocale] + [currencyToFormatWith, formatterLocale], ) const convertToFiatAmountWithLocales = useCallback( (amount?: number) => convertToFiatAmount(amount, currencyToFormatWith, localCurrencyConversionRateToFormatWith), - [currencyToFormatWith, localCurrencyConversionRateToFormatWith] + [currencyToFormatWith, localCurrencyConversionRateToFormatWith], ) const formatConvertedFiatNumberOrString = useCallback( @@ -972,7 +972,7 @@ export function useFormatter() { localCurrency: currencyToFormatWith, conversionRate: undefined, }), - [currencyToFormatWith, formatterLocale] + [currencyToFormatWith, formatterLocale], ) return useMemo( @@ -1003,6 +1003,6 @@ export function useFormatter() { formatPriceWithLocales, formatReviewSwapCurrencyAmountWithLocales, formatTickPriceWithLocales, - ] + ], ) } diff --git a/apps/web/src/utils/getInitialLogoURL.ts b/apps/web/src/utils/getInitialLogoURL.ts index 1ace0166916..728bb7b0de1 100644 --- a/apps/web/src/utils/getInitialLogoURL.ts +++ b/apps/web/src/utils/getInitialLogoURL.ts @@ -7,7 +7,7 @@ export function getInitialLogoUrl( address?: string | null, chainId?: number | null, isNative?: boolean, - backupImg?: string | null + backupImg?: string | null, ) { const networkName = isSupportedChainId(chainId) ? getChain({ chainId }).assetRepoNetworkName ?? 'ethereum' diff --git a/apps/web/src/utils/getSupportedChainIdsFromWalletConnectSession.ts b/apps/web/src/utils/getSupportedChainIdsFromWalletConnectSession.ts index 8af0e0c3553..47ce0f31860 100644 --- a/apps/web/src/utils/getSupportedChainIdsFromWalletConnectSession.ts +++ b/apps/web/src/utils/getSupportedChainIdsFromWalletConnectSession.ts @@ -8,7 +8,7 @@ function getChainIdFromFormattedString(item: string): number | null { } export function getSupportedChainIdsFromWalletConnectSession( - session?: SessionTypes.Struct + session?: SessionTypes.Struct, ): SupportedInterfaceChainId[] { if (!session?.namespaces) { return [] diff --git a/apps/web/src/utils/loggingFormatters.ts b/apps/web/src/utils/loggingFormatters.ts index f535650800a..821c0ff6955 100644 --- a/apps/web/src/utils/loggingFormatters.ts +++ b/apps/web/src/utils/loggingFormatters.ts @@ -28,16 +28,16 @@ const formatRoutesEventProperties = (routes?: RoutingDiagramEntry[]) => { routesEventProperties['routes_percentages'].push(formatPercentNumber(route.percent)) routesEventProperties['routes_protocols'].push(route.protocol) routesEventProperties[`route_${index}_input_currency_symbols`] = route.path.map( - (pathStep) => pathStep[0].symbol ?? '' + (pathStep) => pathStep[0].symbol ?? '', ) routesEventProperties[`route_${index}_output_currency_symbols`] = route.path.map( - (pathStep) => pathStep[1].symbol ?? '' + (pathStep) => pathStep[1].symbol ?? '', ) routesEventProperties[`route_${index}_input_currency_addresses`] = route.path.map((pathStep) => - getTokenAddress(pathStep[0]) + getTokenAddress(pathStep[0]), ) routesEventProperties[`route_${index}_output_currency_addresses`] = route.path.map((pathStep) => - getTokenAddress(pathStep[1]) + getTokenAddress(pathStep[1]), ) routesEventProperties[`route_${index}_fee_amounts_hundredths_of_bps`] = route.path.map((pathStep) => pathStep[2]) }) @@ -48,7 +48,7 @@ const formatRoutesEventProperties = (routes?: RoutingDiagramEntry[]) => { export const formatSwapPriceUpdatedEventProperties = ( trade: InterfaceTrade, priceUpdate: number | undefined, - response: SwapPriceUpdateUserResponse + response: SwapPriceUpdateUserResponse, ): SwapPriceUpdateActionProperties => ({ chain_id: trade.inputAmount.currency.chainId === trade.outputAmount.currency.chainId diff --git a/apps/web/src/utils/maxAmountSpend.ts b/apps/web/src/utils/maxAmountSpend.ts index 939f899c57b..4fc3f692d29 100644 --- a/apps/web/src/utils/maxAmountSpend.ts +++ b/apps/web/src/utils/maxAmountSpend.ts @@ -14,7 +14,7 @@ export function maxAmountSpend(currencyAmount?: CurrencyAmount): Curre if (JSBI.greaterThan(currencyAmount.quotient, MIN_NATIVE_CURRENCY_FOR_GAS)) { return CurrencyAmount.fromRawAmount( currencyAmount.currency, - JSBI.subtract(currencyAmount.quotient, MIN_NATIVE_CURRENCY_FOR_GAS) + JSBI.subtract(currencyAmount.quotient, MIN_NATIVE_CURRENCY_FOR_GAS), ) } else { return CurrencyAmount.fromRawAmount(currencyAmount.currency, JSBI.BigInt(0)) diff --git a/apps/web/src/utils/prices.ts b/apps/web/src/utils/prices.ts index 02348a1a4f2..eec456e7773 100644 --- a/apps/web/src/utils/prices.ts +++ b/apps/web/src/utils/prices.ts @@ -32,8 +32,8 @@ function computeRealizedLPFeePercent(trade: Trade percent = ONE_HUNDRED_PERCENT.subtract( trade.swaps.reduce( (currentFee: Percent): Percent => currentFee.multiply(INPUT_FRACTION_AFTER_FEE), - ONE_HUNDRED_PERCENT - ) + ONE_HUNDRED_PERCENT, + ), ) } else { percent = ZERO_PERCENT @@ -50,8 +50,8 @@ function computeRealizedLPFeePercent(trade: Trade FeeAmount.MEDIUM : pool.fee return currentFee.multiply(ONE_HUNDRED_PERCENT.subtract(new Fraction(fee, 1_000_000))) - }, ONE_HUNDRED_PERCENT) - ) + }, ONE_HUNDRED_PERCENT), + ), ) percent = percent.add(routeRealizedLPFeePercent) @@ -63,7 +63,7 @@ function computeRealizedLPFeePercent(trade: Trade // computes price breakdown for the trade export function computeRealizedLPFeeAmount( - trade?: Trade | null + trade?: Trade | null, ): CurrencyAmount | undefined { if (trade) { const realizedLPFee = computeRealizedLPFeePercent(trade) diff --git a/apps/web/src/utils/signing.ts b/apps/web/src/utils/signing.ts index bd79eb9a072..622e30fdbd8 100644 --- a/apps/web/src/utils/signing.ts +++ b/apps/web/src/utils/signing.ts @@ -35,7 +35,7 @@ export async function signTypedData( types: Record, // Use Record for the value to match the JsonRpcSigner._signTypedData signature. // eslint-disable-next-line @typescript-eslint/no-explicit-any - value: Record + value: Record, ) { // Populate any ENS names (in-place) const populated = await _TypedDataEncoder.resolveNames(domain, types, value, (name: string) => { @@ -60,7 +60,7 @@ export async function signTypedData( 'signing', 'signTypedData', 'signTypedData: wallet does not implement EIP-712, falling back to eth_sign', - error + error, ) const hash = _TypedDataEncoder.hash(populated.domain, types, populated.value) return await signer.provider.send('eth_sign', [address, hash]) diff --git a/apps/web/src/utils/splitHiddenTokens.tsx b/apps/web/src/utils/splitHiddenTokens.tsx index 5ecdd529dfc..1f58ff4c82a 100644 --- a/apps/web/src/utils/splitHiddenTokens.tsx +++ b/apps/web/src/utils/splitHiddenTokens.tsx @@ -12,7 +12,7 @@ export interface SplitOptions { export function splitHiddenTokens( tokenBalances: readonly (PortfolioTokenBalancePartsFragment | undefined)[], - { hideSmallBalances = true, hideSpam = true }: SplitOptions = {} + { hideSmallBalances = true, hideSpam = true }: SplitOptions = {}, ) { const visibleTokens: PortfolioTokenBalancePartsFragment[] = [] const hiddenTokens: PortfolioTokenBalancePartsFragment[] = [] diff --git a/apps/web/src/utils/swapErrorToUserReadableMessage.tsx b/apps/web/src/utils/swapErrorToUserReadableMessage.tsx index 3fba085f827..83575bc3396 100644 --- a/apps/web/src/utils/swapErrorToUserReadableMessage.tsx +++ b/apps/web/src/utils/swapErrorToUserReadableMessage.tsx @@ -75,7 +75,7 @@ export function swapErrorToUserReadableMessage(error: any): string { 'swapErrorToUserReadableMessage', 'swapErrorToUserReadableMessage', 'Undefined object error', - reason + reason, ) return t`An error occurred when trying to execute this swap. You may need to increase your slippage tolerance. If that does not work, there may be an incompatibility with the token you are trading. Note: fee-on-transfer and rebase tokens are incompatible with Uniswap V3.` } @@ -83,7 +83,7 @@ export function swapErrorToUserReadableMessage(error: any): string { `{{reason}} You may need to increase your slippage tolerance. Note: fee-on-transfer and rebase tokens are incompatible with Uniswap V3.`, { reason: reason ? reason : 'Unknown error.', - } + }, ) } } diff --git a/apps/web/src/utils/tradeMeaningFullyDiffer.ts b/apps/web/src/utils/tradeMeaningFullyDiffer.ts index 442e6ef01a7..3255e2d429a 100644 --- a/apps/web/src/utils/tradeMeaningFullyDiffer.ts +++ b/apps/web/src/utils/tradeMeaningFullyDiffer.ts @@ -9,7 +9,7 @@ import { InterfaceTrade } from 'state/routing/types' export function tradeMeaningfullyDiffers( currentTrade: InterfaceTrade, newTrade: InterfaceTrade, - slippage: Percent + slippage: Percent, ): boolean { return ( currentTrade.tradeType !== newTrade.tradeType || diff --git a/apps/web/src/utils/transfer.ts b/apps/web/src/utils/transfer.ts index 027a90de06e..70af6f4d74c 100644 --- a/apps/web/src/utils/transfer.ts +++ b/apps/web/src/utils/transfer.ts @@ -67,7 +67,7 @@ function getNativeTransferRequest(params: TransferCurrencyParams): TransactionRe } async function getTokenTransferRequest( - transferParams: TransferCurrencyParams + transferParams: TransferCurrencyParams, ): Promise { const { provider, account, chainId, toAddress, tokenAddress, amountInWei } = transferParams const tokenContract = getContract(tokenAddress, ERC20_ABI, provider, account) as Erc20 diff --git a/apps/web/src/utils/transformSwapRouteToGetQuoteResult.ts b/apps/web/src/utils/transformSwapRouteToGetQuoteResult.ts index 63efc170560..b8034705850 100644 --- a/apps/web/src/utils/transformSwapRouteToGetQuoteResult.ts +++ b/apps/web/src/utils/transformSwapRouteToGetQuoteResult.ts @@ -28,7 +28,7 @@ export function transformSwapRouteToGetQuoteResult( gasPriceWei, methodParameters, blockNumber, - }: SwapRoute + }: SwapRoute, ): QuoteResult { const routeResponse: Array<(V3PoolInRoute | V2PoolInRoute)[]> = [] diff --git a/apps/web/src/utils/urlChecks.ts b/apps/web/src/utils/urlChecks.ts index d7dccc9cc00..959b7238321 100644 --- a/apps/web/src/utils/urlChecks.ts +++ b/apps/web/src/utils/urlChecks.ts @@ -6,7 +6,7 @@ export function hasURL(str?: string): boolean { '([a-zA-Z0-9]+://)?' + // optional protocol '([a-zA-Z0-9_]+:[a-zA-Z0-9_]+@)?' + // optional username:password '([a-zA-Z0-9.-]+\\.[A-Za-z]{2,4})' + // host name and subdomain - '(:[0-9]+)?(/.*)?' // optional port and path + '(:[0-9]+)?(/.*)?', // optional port and path ) return pattern.test(str) diff --git a/dangerfile.ts b/dangerfile.ts index 157e8731cd8..afd7f3f5357 100644 --- a/dangerfile.ts +++ b/dangerfile.ts @@ -1,32 +1,36 @@ import { danger, fail, markdown, message, warn } from 'danger' function getIndicesOf(searchStr: string, str: string): number[] { - var searchStrLen = searchStr.length; + var searchStrLen = searchStr.length if (searchStrLen == 0) { - return []; + return [] } - var startIndex = 0, index, indices: number[] = []; + var startIndex = 0, + index, + indices: number[] = [] while ((index = str.indexOf(searchStr, startIndex)) > -1) { - indices.push(index); - startIndex = index + searchStrLen; + indices.push(index) + startIndex = index + searchStrLen } - return indices; + return indices } async function getLinesAddedByFile(files: string[]) { - return await Promise.all(files.flatMap(async (file) => { - const structuredDiff = await danger.git.structuredDiffForFile(file); - - return (structuredDiff?.chunks || []).flatMap((chunk) => { - return chunk.changes.filter((change) => change.type === 'add') - }) - })) + return await Promise.all( + files.flatMap(async (file) => { + const structuredDiff = await danger.git.structuredDiffForFile(file) + + return (structuredDiff?.chunks || []).flatMap((chunk) => { + return chunk.changes.filter((change) => change.type === 'add') + }) + }), + ) } async function processAddChanges() { const updatedTsFiles = danger.git.modified_files - .concat(danger.git.created_files) - .filter((file) => (file.endsWith('.ts') || file.endsWith('.tsx')) && !file.includes('dangerfile.ts')) + .concat(danger.git.created_files) + .filter((file) => (file.endsWith('.ts') || file.endsWith('.tsx')) && !file.includes('dangerfile.ts')) const updatedNonUITsFiles = updatedTsFiles.filter((file) => !file.includes('packages/ui')) @@ -67,10 +71,15 @@ async function processAddChanges() { const indices = getIndicesOf(`from 'ui/src/`, change.content) indices.forEach((idx) => { - const potentialSubstring = change.content.substring(idx, Math.min(change.content.length, idx + longestImportLength + 6 + 1)) + const potentialSubstring = change.content.substring( + idx, + Math.min(change.content.length, idx + longestImportLength + 6 + 1), + ) if (!validLongerImports.some((validImport) => potentialSubstring.includes(validImport))) { const endOfImport = change.content.indexOf(`'`, idx + 6) // skipping the "from '" - warn(`It looks like you have a longer import from 'ui/src' than needed ('${change.content.substring(idx + 6, endOfImport)}'). Please use one of [${validLongerImports.join(', ')}] when possible!`) + warn( + `It looks like you have a longer import from 'ui/src' than needed ('${change.content.substring(idx + 6, endOfImport)}'). Please use one of [${validLongerImports.join(', ')}] when possible!`, + ) } }) }) @@ -82,7 +91,9 @@ async function processAddChanges() { // Check for non-recommended sentry usage if (/logger\.error\(\s*new Error\(/.test(concatenatedAddedLines)) { - warn(`It appears you may be manually logging a Sentry error. Please log the error directly if possible. If you need to use a custom error message, ensure the error object is added to the 'cause' property.`) + warn( + `It appears you may be manually logging a Sentry error. Please log the error directly if possible. If you need to use a custom error message, ensure the error object is added to the 'cause' property.`, + ) } if (/logger\.error\(\s*['`"]/.test(concatenatedAddedLines)) { warn(`Please log an error, not a string!`) @@ -90,10 +101,14 @@ async function processAddChanges() { // Check for incorrect usage of `createSelector` if (concatenatedAddedLines.includes(`createSelector(`)) { - warn("You've added a new call to `createSelector()`. This is Ok, but please make sure you're using it correctly and you're not creating a new selector on every render. See PR #5172 for details.") + warn( + "You've added a new call to `createSelector()`. This is Ok, but please make sure you're using it correctly and you're not creating a new selector on every render. See PR #5172 for details.", + ) } if (/(useAppSelector|appSelect|select)\(\s*makeSelect/.test(concatenatedAddedLines)) { - fail(`It appears you may be creating a new selector on every render. See PR #5172 for details on how to fix this.`) + fail( + `It appears you may be creating a new selector on every render. See PR #5172 for details on how to fix this.`, + ) } }) } @@ -101,13 +116,15 @@ async function processAddChanges() { async function checkCocoaPodsVersion() { const updatedPodFileLock = danger.git.modified_files.find((file) => file.includes('ios/Podfile.lock')) if (updatedPodFileLock) { - const structuredDiff = await danger.git.structuredDiffForFile(updatedPodFileLock); + const structuredDiff = await danger.git.structuredDiffForFile(updatedPodFileLock) const changedLines = (structuredDiff?.chunks || []).flatMap((chunk) => { return chunk.changes.filter((change) => change.type === 'add') }) const changedCocoaPodsVersion = changedLines.some((change) => change.content.includes('COCOAPODS: ')) if (changedCocoaPodsVersion) { - warn(`You're changing the Podfile version! Ensure you are using the correct version. If this change is intentional, you should ignore this check and merge anyways.`) + warn( + `You're changing the Podfile version! Ensure you are using the correct version. If this change is intentional, you should ignore this check and merge anyways.`, + ) } } } @@ -115,13 +132,16 @@ async function checkCocoaPodsVersion() { async function checkApostrophes() { const updatedTranslations = danger.git.modified_files.find((file) => file.includes('en-US.json')) if (updatedTranslations) { - const structuredDiff = await danger.git.structuredDiffForFile(updatedTranslations); + const structuredDiff = await danger.git.structuredDiffForFile(updatedTranslations) const changedLines = (structuredDiff?.chunks || []).flatMap((chunk) => { return chunk.changes.filter((change) => change.type === 'add') }) changedLines.forEach((line, index) => { if (line.content.includes("'")) { - fail("You added a string to the translations file using the ' character. Please use the ’ character instead!. Issue in line: " + index) + fail( + "You added a string to the translations file using the ' character. Please use the ’ character instead!. Issue in line: " + + index, + ) } }) } @@ -130,16 +150,14 @@ async function checkApostrophes() { async function checkPRSize() { // Warn when there is a big PR const bigPRThreshold = 500 - const linesCount = await danger.git.linesOfCode('**/*'); + const linesCount = await danger.git.linesOfCode('**/*') // exclude fixtures and auto generated files - const excludeLinesCount = await danger.git.linesOfCode( - '{**/*.snap}', - ) + const excludeLinesCount = await danger.git.linesOfCode('{**/*.snap}') const totalLinesCount = (linesCount ?? 0) - (excludeLinesCount ?? 0) if (totalLinesCount > bigPRThreshold) { warn(':exclamation: Big PR') markdown( - '> Pull Request size seems relatively large. If PR contains multiple changes, split each into separate PRs for faster, easier reviews.' + '> Pull Request size seems relatively large. If PR contains multiple changes, split each into separate PRs for faster, easier reviews.', ) } } @@ -148,7 +166,7 @@ async function checkPRSize() { const envChanged = danger.git.modified_files.includes('.env.defaults') if (envChanged) { warn( - 'Changes were made to .env.defaults. Confirm that no sensitive data is in the .env.defaults file. Sensitive data must go in .env (web) or .env.defaults.local (mobile) and then run `yarn upload-env-local` to store it in 1Password.' + 'Changes were made to .env.defaults. Confirm that no sensitive data is in the .env.defaults file. Sensitive data must go in .env (web) or .env.defaults.local (mobile) and then run `yarn upload-env-local` to store it in 1Password.', ) } @@ -167,110 +185,103 @@ checkPRSize() // No PR is too small to warrant a paragraph or two of summary if (danger.github.pr.body.length < 50) { warn( - 'The PR description is looking sparse. Please consider explaining more about this PRs goal and implementation decisions.' + 'The PR description is looking sparse. Please consider explaining more about this PRs goal and implementation decisions.', ) } // Congratulate when code was deleted if (danger.github.pr.additions < danger.github.pr.deletions) { - message( - `✂️ Thanks for removing ${danger.github.pr.deletions - danger.github.pr.additions} lines!` - ) + message(`✂️ Thanks for removing ${danger.github.pr.deletions - danger.github.pr.additions} lines!`) } // GraphQL update warnings -const updatedGraphQLfile = danger.git.modified_files.find((file) => - file.includes('__generated__/types-and-hooks.ts') -) +const updatedGraphQLfile = danger.git.modified_files.find((file) => file.includes('__generated__/types-and-hooks.ts')) if (updatedGraphQLfile) { warn( 'You have updated the GraphQL schema. Please ensure that the Swift GraphQL Schema generation is valid by running `yarn mobile ios` and rebuilding for iOS. ' + - 'You may need to add or remove generated files to the project.pbxproj. For more information see `apps/mobile/ios/WidgetsCore/MobileSchema/README.md`' + 'You may need to add or remove generated files to the project.pbxproj. For more information see `apps/mobile/ios/WidgetsCore/MobileSchema/README.md`', ) } // Migrations + schema warnings -const updatedMobileSchemaFile = danger.git.modified_files.find((file) => - file.includes('mobile/src/app/schema.ts') -) +const updatedMobileSchemaFile = danger.git.modified_files.find((file) => file.includes('mobile/src/app/schema.ts')) const updatedMobileMigrationsFile = danger.git.modified_files.find((file) => - file.includes('mobile/src/app/migrations.ts') + file.includes('mobile/src/app/migrations.ts'), ) const updatedMobileMigrationsTestFile = danger.git.modified_files.find((file) => - file.includes('mobile/src/app/migrations.test.ts') + file.includes('mobile/src/app/migrations.test.ts'), ) -const updatedExtensionSchemaFile = danger.git.modified_files.find((file) => - file.includes('stretch/src/app/schema.ts') -) +const updatedExtensionSchemaFile = danger.git.modified_files.find((file) => file.includes('stretch/src/app/schema.ts')) const updatedExtensionMigrationsFile = danger.git.modified_files.find((file) => - file.includes('stretch/src/store/migrations.ts') + file.includes('stretch/src/store/migrations.ts'), ) const updatedExtensionMigrationsTestFile = danger.git.modified_files.find((file) => - file.includes('stretch/src/store/migrations.test.ts') + file.includes('stretch/src/store/migrations.test.ts'), ) -const createdSliceFile = danger.git.created_files.find((file) => - file.toLowerCase().includes('slice') -) +const createdSliceFile = danger.git.created_files.find((file) => file.toLowerCase().includes('slice')) -const modifiedSliceFile = danger.git.modified_files.find((file) => - file.toLowerCase().includes('slice') -) +const modifiedSliceFile = danger.git.modified_files.find((file) => file.toLowerCase().includes('slice')) -const deletedSliceFile = danger.git.deleted_files.find((file) => - file.toLowerCase().includes('slice') -) +const deletedSliceFile = danger.git.deleted_files.find((file) => file.toLowerCase().includes('slice')) -if (modifiedSliceFile && ((!updatedMobileSchemaFile || !updatedMobileMigrationsFile) || (!updatedExtensionSchemaFile || !updatedExtensionMigrationsFile))) { +if ( + modifiedSliceFile && + (!updatedMobileSchemaFile || + !updatedMobileMigrationsFile || + !updatedExtensionSchemaFile || + !updatedExtensionMigrationsFile) +) { warn( - 'You modified a slice file. If you added, renamed, or deleted required properties from state, then make sure to define a new schema and a create a migration.' + 'You modified a slice file. If you added, renamed, or deleted required properties from state, then make sure to define a new schema and a create a migration.', ) } if (updatedMobileSchemaFile && !updatedMobileMigrationsFile) { - warn( - 'You updated the mobile schema file but not the migrations file. Make sure to also define a migration.' - ) + warn('You updated the mobile schema file but not the migrations file. Make sure to also define a migration.') } if (updatedExtensionSchemaFile && !updatedExtensionMigrationsFile) { - warn( - 'You updated the extension schema file but not the migrations file. Make sure to also define a migration.' - ) + warn('You updated the extension schema file but not the migrations file. Make sure to also define a migration.') } if (!updatedMobileSchemaFile && updatedMobileMigrationsFile) { warn( - 'You updated the mobile migrations file but not the schema. Schema always needs to be updated when a new migration is defined.' + 'You updated the mobile migrations file but not the schema. Schema always needs to be updated when a new migration is defined.', ) } if (!updatedExtensionSchemaFile && updatedExtensionMigrationsFile) { warn( - 'You updated the extension migrations file but not the schema. Schema always needs to be updated when a new migration is defined.' + 'You updated the extension migrations file but not the schema. Schema always needs to be updated when a new migration is defined.', ) } -if ((createdSliceFile || deletedSliceFile) && (!updatedMobileSchemaFile || !updatedMobileMigrationsFile || !updatedExtensionSchemaFile || !updatedExtensionMigrationsFile)) { +if ( + (createdSliceFile || deletedSliceFile) && + (!updatedMobileSchemaFile || + !updatedMobileMigrationsFile || + !updatedExtensionSchemaFile || + !updatedExtensionMigrationsFile) +) { warn('You created or deleted a slice file. Make sure to update the schema and create migration if needed.') - - } -if ((updatedMobileMigrationsFile && !updatedMobileMigrationsTestFile) || (updatedExtensionMigrationsFile && !updatedExtensionMigrationsTestFile)) { - fail( - 'You updated the migrations file but did not write any new tests. Each migration must have a test!' - ) +if ( + (updatedMobileMigrationsFile && !updatedMobileMigrationsTestFile) || + (updatedExtensionMigrationsFile && !updatedExtensionMigrationsTestFile) +) { + fail('You updated the migrations file but did not write any new tests. Each migration must have a test!') } if (updatedMobileMigrationsFile !== updatedExtensionMigrationsFile) { warn( - 'You updated the migrations file in one app but not the other. Make sure to update both migration files if needed.' + 'You updated the migrations file in one app but not the other. Make sure to update both migration files if needed.', ) } diff --git a/package.json b/package.json index 9dc869e947d..baa8f341c16 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "husky": "^8.0.3", "i18next": "23.10.0", "i18next-parser": "8.6.0", + "prettier": "3.3.2", + "prettier-plugin-organize-imports": "3.2.4", "syncpack": "^8.5.14", "turbo": "1.10.16", "turbo-ignore": "^1.11.3" @@ -77,12 +79,11 @@ "@web3-react/metamask@8.2.4": "patch:@web3-react/metamask@npm%3A8.2.4#./.yarn/patches/@web3-react-metamask-npm-8.2.4-84b10de2d2.patch", "@web3-react/walletconnect-v2@8.5.1": "patch:@web3-react/walletconnect-v2@npm%3A8.5.1#./.yarn/patches/@web3-react-walletconnect-v2-npm-8.5.1-933cac0534.patch", "lightweight-charts@4.1.1": "patch:lightweight-charts@npm%3A4.1.1#./.yarn/patches/lightweight-charts-npm-4.1.1-01f161d9b6.patch", - "react-native-context-menu-view@1.15.0": "patch:react-native-context-menu-view@npm%3A1.15.0#./.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch", "expo-local-authentication@13.8.0": "patch:expo-local-authentication@npm%3A13.8.0#./.yarn/patches/expo-local-authentication-npm-13.8.0-3a1b5c983f.patch", "@tamagui/animations-moti@1.92.0": "patch:@tamagui/animations-moti@npm%3A1.92.0#./.yarn/patches/@tamagui-animations-moti-npm-1.92.0-a8dde990ec.patch", "cypress-hardhat@2.5.0": "patch:cypress-hardhat@npm%3A2.5.0#./.yarn/patches/cypress-hardhat-npm-2.5.0-9b9b7d7a28.patch", "react-native-wagmi-charts@2.5.1": "patch:react-native-wagmi-charts@npm%3A2.5.1#./.yarn/patches/react-native-wagmi-charts-npm-2.5.1-c9e2637be7.patch", - "@wagmi/core@2.6.16": "patch:@wagmi/core@npm%3A2.6.16#./.yarn/patches/@wagmi-core-npm-2.6.16-1baef7c190.patch" + "react-native-context-menu-view@1.15.0": "patch:react-native-context-menu-view@npm%3A1.15.0#./.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch" }, "scripts": { "g:build": "turbo run build --concurrency=100%", @@ -91,14 +92,18 @@ "g:check:deps:mismatch": "manypkg check", "g:check:circular": "turbo run check:circular", "g:format:deps": "syncpack format", + "g:format": "turbo run format --parallel", + "g:format:fix": "yarn g:format -- --write", + "g:format:changed": "./scripts/turbo-changed.sh format", + "g:fix": "yarn g:lint:fix && yarn g:format:fix", "g:lint:changed": "./scripts/turbo-changed.sh lint", "g:lint": "turbo run lint", "g:lint:fix": "turbo run lint:fix", "g:prepare": "turbo run prepare", "g:rm:local-packages": "rm -rf ./node_modules/utilities ./node_modules/wallet ./node_modules/ui ./node_modules/uniswap", "g:rm:nodemodules": "rm -rf node_modules", - "g:run-fast-checks": "turbo run typecheck lint build --filter=\"[HEAD]\"", - "g:run-all-checks": "turbo run typecheck lint test build check:circular", + "g:run-fast-checks": "turbo run typecheck lint build format --filter=\"[HEAD]\"", + "g:run-all-checks": "turbo run typecheck lint test build format check:circular", "g:test": "turbo run test", "g:test:coverage": "turbo run test -- --collectCoverage=true", "g:test:coverage:rest": "turbo run test --filter=ui --filter=uniswap --filter=utilities --filter=@uniswap/eslint-config -- --collectCoverage=true", diff --git a/packages/eslint-config/.depcheckrc b/packages/eslint-config/.depcheckrc deleted file mode 100644 index 453e712f92c..00000000000 --- a/packages/eslint-config/.depcheckrc +++ /dev/null @@ -1 +0,0 @@ -ignores: ["prettier-plugin-organize-imports"] diff --git a/packages/eslint-config/__snapshots__/preset.test.ts.snap b/packages/eslint-config/__snapshots__/preset.test.ts.snap index 8f30c338e05..43cfba8c99d 100644 --- a/packages/eslint-config/__snapshots__/preset.test.ts.snap +++ b/packages/eslint-config/__snapshots__/preset.test.ts.snap @@ -20,16 +20,9 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "import", "@typescript-eslint", "cypress", - "prettier", ], "reportUnusedDisableDirectives": undefined, "rules": { - "@babel/object-curly-spacing": [ - "off", - ], - "@babel/semi": [ - "off", - ], "@typescript-eslint/ban-ts-comment": [ "off", ], @@ -39,42 +32,12 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "@typescript-eslint/ban-types": [ "error", ], - "@typescript-eslint/block-spacing": [ - "off", - ], - "@typescript-eslint/brace-style": [ - "off", - ], - "@typescript-eslint/comma-dangle": [ - "off", - ], - "@typescript-eslint/comma-spacing": [ - "off", - ], "@typescript-eslint/explicit-function-return-type": [ "off", ], "@typescript-eslint/explicit-module-boundary-types": [ "off", ], - "@typescript-eslint/func-call-spacing": [ - "off", - ], - "@typescript-eslint/indent": [ - "off", - ], - "@typescript-eslint/key-spacing": [ - "off", - ], - "@typescript-eslint/keyword-spacing": [ - "off", - ], - "@typescript-eslint/lines-around-comment": [ - 0, - ], - "@typescript-eslint/member-delimiter-style": [ - "off", - ], "@typescript-eslint/no-array-constructor": [ "error", ], @@ -87,12 +50,6 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "@typescript-eslint/no-extra-non-null-assertion": [ "error", ], - "@typescript-eslint/no-extra-parens": [ - "off", - ], - "@typescript-eslint/no-extra-semi": [ - "off", - ], "@typescript-eslint/no-loss-of-precision": [ "error", ], @@ -120,83 +77,17 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "@typescript-eslint/no-var-requires": [ "error", ], - "@typescript-eslint/object-curly-spacing": [ - "off", - ], "@typescript-eslint/prefer-as-const": [ "error", ], - "@typescript-eslint/quotes": [ - 0, - ], - "@typescript-eslint/semi": [ - "off", - ], - "@typescript-eslint/space-before-blocks": [ - "off", - ], - "@typescript-eslint/space-before-function-paren": [ - "off", - ], - "@typescript-eslint/space-infix-ops": [ - "off", - ], "@typescript-eslint/triple-slash-reference": [ "error", ], - "@typescript-eslint/type-annotation-spacing": [ - "off", - ], - "array-bracket-newline": [ - "off", - ], - "array-bracket-spacing": [ - "off", - ], - "array-element-newline": [ - "off", - ], - "arrow-body-style": [ - "off", - ], - "arrow-parens": [ - "off", - ], - "arrow-spacing": [ - "off", - ], - "babel/object-curly-spacing": [ - "off", - ], - "babel/quotes": [ - 0, - ], - "babel/semi": [ - "off", - ], - "block-spacing": [ - "off", - ], - "brace-style": [ - "off", - ], - "comma-dangle": [ - "off", - ], - "comma-spacing": [ - "off", - ], - "comma-style": [ - "off", - ], - "computed-property-spacing": [ - "off", - ], "constructor-super": [ "off", ], "curly": [ - 0, + "error", ], "cypress/no-assigning-return-values": [ "error", @@ -210,69 +101,12 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "cypress/unsafe-to-chain-command": [ "error", ], - "dot-location": [ - "off", - ], - "eol-last": [ - "off", - ], - "flowtype/boolean-style": [ - "off", - ], - "flowtype/delimiter-dangle": [ - "off", - ], - "flowtype/generic-spacing": [ - "off", - ], - "flowtype/object-type-curly-spacing": [ - "off", - ], - "flowtype/object-type-delimiter": [ - "off", - ], - "flowtype/quotes": [ - "off", - ], - "flowtype/semi": [ - "off", - ], - "flowtype/space-after-type-colon": [ - "off", - ], - "flowtype/space-before-generic-bracket": [ - "off", - ], - "flowtype/space-before-type-colon": [ - "off", - ], - "flowtype/union-intersection-spacing": [ - "off", - ], "for-direction": [ "error", ], - "func-call-spacing": [ - "off", - ], - "function-call-argument-newline": [ - "off", - ], - "function-paren-newline": [ - "off", - ], - "generator-star": [ - "off", - ], - "generator-star-spacing": [ - "off", - ], "getter-return": [ "off", ], - "implicit-arrow-linebreak": [ - "off", - ], "import/named": [ "off", ], @@ -282,48 +116,9 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "unusedExports": true, }, ], - "indent": [ - "off", - ], - "indent-legacy": [ - "off", - ], - "jsx-quotes": [ - "off", - ], - "key-spacing": [ - "off", - ], - "keyword-spacing": [ - "off", - ], - "linebreak-style": [ - "off", - ], - "lines-around-comment": [ - 0, - ], - "max-len": [ - 0, - ], - "max-statements-per-line": [ - "off", - ], - "multiline-ternary": [ - "off", - ], - "new-parens": [ - "off", - ], - "newline-per-chained-call": [ - "off", - ], "no-array-constructor": [ "off", ], - "no-arrow-condition": [ - "off", - ], "no-async-promise-executor": [ "error", ], @@ -333,18 +128,12 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "no-class-assign": [ "error", ], - "no-comma-dangle": [ - "off", - ], "no-compare-neg-zero": [ "error", ], "no-cond-assign": [ "error", ], - "no-confusing-arrow": [ - 0, - ], "no-console": [ "error", ], @@ -393,18 +182,12 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "no-extra-boolean-cast": [ "error", ], - "no-extra-parens": [ - "off", - ], "no-extra-semi": [ - "off", + "error", ], "no-fallthrough": [ "error", ], - "no-floating-decimal": [ - "off", - ], "no-func-assign": [ "off", ], @@ -429,17 +212,8 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "no-misleading-character-class": [ "error", ], - "no-mixed-operators": [ - 0, - ], "no-mixed-spaces-and-tabs": [ - "off", - ], - "no-multi-spaces": [ - "off", - ], - "no-multiple-empty-lines": [ - "off", + "error", ], "no-new-symbol": [ "off", @@ -462,9 +236,6 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "no-regex-spaces": [ "error", ], - "no-reserved-keys": [ - "off", - ], "no-self-assign": [ "error", ], @@ -474,29 +245,17 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "no-shadow-restricted-names": [ "error", ], - "no-space-before-semi": [ - "off", - ], - "no-spaced-func": [ - "off", - ], "no-sparse-arrays": [ "error", ], - "no-tabs": [ - 0, - ], "no-this-before-super": [ "off", ], - "no-trailing-spaces": [ - "off", - ], "no-undef": [ "off", ], "no-unexpected-multiline": [ - 0, + "error", ], "no-unreachable": [ "off", @@ -528,43 +287,13 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "no-var": [ "error", ], - "no-whitespace-before-property": [ - "off", - ], "no-with": [ "error", ], - "no-wrap-func": [ - "off", - ], - "nonblock-statement-body-position": [ - "off", - ], - "object-curly-newline": [ - "off", - ], - "object-curly-spacing": [ - "off", - ], - "object-property-newline": [ - "off", - ], "object-shorthand": [ "error", "always", ], - "one-var-declaration-per-line": [ - "off", - ], - "operator-linebreak": [ - "off", - ], - "padded-blocks": [ - "off", - ], - "prefer-arrow-callback": [ - "off", - ], "prefer-const": [ "error", ], @@ -574,295 +303,29 @@ exports[`should have a correct configuration for a Cypress e2e file 1`] = ` "prefer-spread": [ "error", ], - "prettier/prettier": [ - "error", - { - "printWidth": 120, - "semi": false, - "singleQuote": true, - }, - ], - "quote-props": [ - "off", - ], - "quotes": [ - 0, - ], - "react/jsx-child-element-spacing": [ - "off", - ], - "react/jsx-closing-bracket-location": [ - "off", - ], - "react/jsx-closing-tag-location": [ - "off", - ], - "react/jsx-curly-newline": [ - "off", - ], - "react/jsx-curly-spacing": [ - "off", - ], - "react/jsx-equals-spacing": [ - "off", - ], - "react/jsx-first-prop-new-line": [ - "off", - ], - "react/jsx-indent": [ - "off", - ], - "react/jsx-indent-props": [ - "off", - ], - "react/jsx-max-props-per-line": [ - "off", - ], - "react/jsx-newline": [ - "off", - ], - "react/jsx-one-expression-per-line": [ - "off", - ], - "react/jsx-props-no-multi-spaces": [ - "off", - ], - "react/jsx-space-before-closing": [ - "off", - ], - "react/jsx-tag-spacing": [ - "off", - ], - "react/jsx-wrap-multilines": [ - "off", - ], "require-yield": [ "error", ], - "rest-spread-spacing": [ - "off", - ], - "semi": [ - "off", - ], - "semi-spacing": [ - "off", - ], - "semi-style": [ - "off", + "unused-imports/no-unused-imports": [ + "error", ], - "space-after-function-name": [ - "off", + "use-isnan": [ + "error", ], - "space-after-keywords": [ - "off", + "valid-typeof": [ + "error", ], - "space-before-blocks": [ - "off", + }, + "settings": { + "import/extensions": [ + ".ts", + ".tsx", + ".js", + ".jsx", ], - "space-before-function-paren": [ - "off", - ], - "space-before-function-parentheses": [ - "off", - ], - "space-before-keywords": [ - "off", - ], - "space-in-brackets": [ - "off", - ], - "space-in-parens": [ - "off", - ], - "space-infix-ops": [ - "off", - ], - "space-return-throw-case": [ - "off", - ], - "space-unary-ops": [ - "off", - ], - "space-unary-word-ops": [ - "off", - ], - "standard/array-bracket-even-spacing": [ - "off", - ], - "standard/computed-property-even-spacing": [ - "off", - ], - "standard/object-curly-even-spacing": [ - "off", - ], - "switch-colon-spacing": [ - "off", - ], - "template-curly-spacing": [ - "off", - ], - "template-tag-spacing": [ - "off", - ], - "unicode-bom": [ - "off", - ], - "unicorn/empty-brace-spaces": [ - "off", - ], - "unicorn/no-nested-ternary": [ - "off", - ], - "unicorn/number-literal-case": [ - "off", - ], - "unused-imports/no-unused-imports": [ - "error", - ], - "use-isnan": [ - "error", - ], - "valid-typeof": [ - "error", - ], - "vue/array-bracket-newline": [ - "off", - ], - "vue/array-bracket-spacing": [ - "off", - ], - "vue/array-element-newline": [ - "off", - ], - "vue/arrow-spacing": [ - "off", - ], - "vue/block-spacing": [ - "off", - ], - "vue/block-tag-newline": [ - "off", - ], - "vue/brace-style": [ - "off", - ], - "vue/comma-dangle": [ - "off", - ], - "vue/comma-spacing": [ - "off", - ], - "vue/comma-style": [ - "off", - ], - "vue/dot-location": [ - "off", - ], - "vue/func-call-spacing": [ - "off", - ], - "vue/html-closing-bracket-newline": [ - "off", - ], - "vue/html-closing-bracket-spacing": [ - "off", - ], - "vue/html-end-tags": [ - "off", - ], - "vue/html-indent": [ - "off", - ], - "vue/html-quotes": [ - "off", - ], - "vue/html-self-closing": [ - 0, - ], - "vue/key-spacing": [ - "off", - ], - "vue/keyword-spacing": [ - "off", - ], - "vue/max-attributes-per-line": [ - "off", - ], - "vue/max-len": [ - 0, - ], - "vue/multiline-html-element-content-newline": [ - "off", - ], - "vue/multiline-ternary": [ - "off", - ], - "vue/mustache-interpolation-spacing": [ - "off", - ], - "vue/no-extra-parens": [ - "off", - ], - "vue/no-multi-spaces": [ - "off", - ], - "vue/no-spaces-around-equal-signs-in-attribute": [ - "off", - ], - "vue/object-curly-newline": [ - "off", - ], - "vue/object-curly-spacing": [ - "off", - ], - "vue/object-property-newline": [ - "off", - ], - "vue/operator-linebreak": [ - "off", - ], - "vue/quote-props": [ - "off", - ], - "vue/script-indent": [ - "off", - ], - "vue/singleline-html-element-content-newline": [ - "off", - ], - "vue/space-in-parens": [ - "off", - ], - "vue/space-infix-ops": [ - "off", - ], - "vue/space-unary-ops": [ - "off", - ], - "vue/template-curly-spacing": [ - "off", - ], - "wrap-iife": [ - "off", - ], - "wrap-regex": [ - "off", - ], - "yield-star-spacing": [ - "off", - ], - }, - "settings": { - "import/extensions": [ - ".ts", - ".tsx", - ".js", - ".jsx", - ], - "import/external-module-folders": [ - "node_modules", - "node_modules/@types", + "import/external-module-folders": [ + "node_modules", + "node_modules/@types", ], "import/parsers": { "@typescript-eslint/parser": [ @@ -908,16 +371,9 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "import", "@typescript-eslint", "jest", - "prettier", ], "reportUnusedDisableDirectives": undefined, "rules": { - "@babel/object-curly-spacing": [ - "off", - ], - "@babel/semi": [ - "off", - ], "@typescript-eslint/ban-ts-comment": [ "off", ], @@ -927,42 +383,12 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "@typescript-eslint/ban-types": [ "error", ], - "@typescript-eslint/block-spacing": [ - "off", - ], - "@typescript-eslint/brace-style": [ - "off", - ], - "@typescript-eslint/comma-dangle": [ - "off", - ], - "@typescript-eslint/comma-spacing": [ - "off", - ], "@typescript-eslint/explicit-function-return-type": [ "off", ], "@typescript-eslint/explicit-module-boundary-types": [ "off", ], - "@typescript-eslint/func-call-spacing": [ - "off", - ], - "@typescript-eslint/indent": [ - "off", - ], - "@typescript-eslint/key-spacing": [ - "off", - ], - "@typescript-eslint/keyword-spacing": [ - "off", - ], - "@typescript-eslint/lines-around-comment": [ - 0, - ], - "@typescript-eslint/member-delimiter-style": [ - "off", - ], "@typescript-eslint/no-array-constructor": [ "error", ], @@ -975,12 +401,6 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "@typescript-eslint/no-extra-non-null-assertion": [ "error", ], - "@typescript-eslint/no-extra-parens": [ - "off", - ], - "@typescript-eslint/no-extra-semi": [ - "off", - ], "@typescript-eslint/no-loss-of-precision": [ "error", ], @@ -1008,147 +428,24 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "@typescript-eslint/no-var-requires": [ "error", ], - "@typescript-eslint/object-curly-spacing": [ - "off", - ], "@typescript-eslint/prefer-as-const": [ "error", ], - "@typescript-eslint/quotes": [ - 0, - ], - "@typescript-eslint/semi": [ - "off", - ], - "@typescript-eslint/space-before-blocks": [ - "off", - ], - "@typescript-eslint/space-before-function-paren": [ - "off", - ], - "@typescript-eslint/space-infix-ops": [ - "off", - ], "@typescript-eslint/triple-slash-reference": [ "error", ], - "@typescript-eslint/type-annotation-spacing": [ - "off", - ], - "array-bracket-newline": [ - "off", - ], - "array-bracket-spacing": [ - "off", - ], - "array-element-newline": [ - "off", - ], - "arrow-body-style": [ - "off", - ], - "arrow-parens": [ - "off", - ], - "arrow-spacing": [ - "off", - ], - "babel/object-curly-spacing": [ - "off", - ], - "babel/quotes": [ - 0, - ], - "babel/semi": [ - "off", - ], - "block-spacing": [ - "off", - ], - "brace-style": [ - "off", - ], - "comma-dangle": [ - "off", - ], - "comma-spacing": [ - "off", - ], - "comma-style": [ - "off", - ], - "computed-property-spacing": [ - "off", - ], "constructor-super": [ "off", ], "curly": [ - 0, - ], - "dot-location": [ - "off", - ], - "eol-last": [ - "off", - ], - "flowtype/boolean-style": [ - "off", - ], - "flowtype/delimiter-dangle": [ - "off", - ], - "flowtype/generic-spacing": [ - "off", - ], - "flowtype/object-type-curly-spacing": [ - "off", - ], - "flowtype/object-type-delimiter": [ - "off", - ], - "flowtype/quotes": [ - "off", - ], - "flowtype/semi": [ - "off", - ], - "flowtype/space-after-type-colon": [ - "off", - ], - "flowtype/space-before-generic-bracket": [ - "off", - ], - "flowtype/space-before-type-colon": [ - "off", - ], - "flowtype/union-intersection-spacing": [ - "off", + "error", ], "for-direction": [ "error", ], - "func-call-spacing": [ - "off", - ], - "function-call-argument-newline": [ - "off", - ], - "function-paren-newline": [ - "off", - ], - "generator-star": [ - "off", - ], - "generator-star-spacing": [ - "off", - ], "getter-return": [ "off", ], - "implicit-arrow-linebreak": [ - "off", - ], "import/named": [ "off", ], @@ -1158,12 +455,6 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "unusedExports": true, }, ], - "indent": [ - "off", - ], - "indent-legacy": [ - "off", - ], "jest/expect-expect": [ "warn", ], @@ -1221,42 +512,9 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "jest/valid-title": [ "error", ], - "jsx-quotes": [ - "off", - ], - "key-spacing": [ - "off", - ], - "keyword-spacing": [ - "off", - ], - "linebreak-style": [ - "off", - ], - "lines-around-comment": [ - 0, - ], - "max-len": [ - 0, - ], - "max-statements-per-line": [ - "off", - ], - "multiline-ternary": [ - "off", - ], - "new-parens": [ - "off", - ], - "newline-per-chained-call": [ - "off", - ], "no-array-constructor": [ "off", ], - "no-arrow-condition": [ - "off", - ], "no-async-promise-executor": [ "error", ], @@ -1266,18 +524,12 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "no-class-assign": [ "error", ], - "no-comma-dangle": [ - "off", - ], "no-compare-neg-zero": [ "error", ], "no-cond-assign": [ "error", ], - "no-confusing-arrow": [ - 0, - ], "no-const-assign": [ "off", ], @@ -1323,18 +575,12 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "no-extra-boolean-cast": [ "error", ], - "no-extra-parens": [ - "off", - ], "no-extra-semi": [ - "off", + "error", ], "no-fallthrough": [ "error", ], - "no-floating-decimal": [ - "off", - ], "no-func-assign": [ "off", ], @@ -1359,17 +605,8 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "no-misleading-character-class": [ "error", ], - "no-mixed-operators": [ - 0, - ], "no-mixed-spaces-and-tabs": [ - "off", - ], - "no-multi-spaces": [ - "off", - ], - "no-multiple-empty-lines": [ - "off", + "error", ], "no-new-symbol": [ "off", @@ -1392,9 +629,6 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "no-regex-spaces": [ "error", ], - "no-reserved-keys": [ - "off", - ], "no-self-assign": [ "error", ], @@ -1404,29 +638,17 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "no-shadow-restricted-names": [ "error", ], - "no-space-before-semi": [ - "off", - ], - "no-spaced-func": [ - "off", - ], "no-sparse-arrays": [ "error", ], - "no-tabs": [ - 0, - ], "no-this-before-super": [ "off", ], - "no-trailing-spaces": [ - "off", - ], "no-undef": [ "off", ], "no-unexpected-multiline": [ - 0, + "error", ], "no-unreachable": [ "off", @@ -1458,43 +680,13 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "no-var": [ "error", ], - "no-whitespace-before-property": [ - "off", - ], "no-with": [ "error", ], - "no-wrap-func": [ - "off", - ], - "nonblock-statement-body-position": [ - "off", - ], - "object-curly-newline": [ - "off", - ], - "object-curly-spacing": [ - "off", - ], - "object-property-newline": [ - "off", - ], "object-shorthand": [ "error", "always", ], - "one-var-declaration-per-line": [ - "off", - ], - "operator-linebreak": [ - "off", - ], - "padded-blocks": [ - "off", - ], - "prefer-arrow-callback": [ - "off", - ], "prefer-const": [ "error", ], @@ -1504,149 +696,9 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "prefer-spread": [ "error", ], - "prettier/prettier": [ - "error", - { - "printWidth": 120, - "semi": false, - "singleQuote": true, - }, - ], - "quote-props": [ - "off", - ], - "quotes": [ - 0, - ], - "react/jsx-child-element-spacing": [ - "off", - ], - "react/jsx-closing-bracket-location": [ - "off", - ], - "react/jsx-closing-tag-location": [ - "off", - ], - "react/jsx-curly-newline": [ - "off", - ], - "react/jsx-curly-spacing": [ - "off", - ], - "react/jsx-equals-spacing": [ - "off", - ], - "react/jsx-first-prop-new-line": [ - "off", - ], - "react/jsx-indent": [ - "off", - ], - "react/jsx-indent-props": [ - "off", - ], - "react/jsx-max-props-per-line": [ - "off", - ], - "react/jsx-newline": [ - "off", - ], - "react/jsx-one-expression-per-line": [ - "off", - ], - "react/jsx-props-no-multi-spaces": [ - "off", - ], - "react/jsx-space-before-closing": [ - "off", - ], - "react/jsx-tag-spacing": [ - "off", - ], - "react/jsx-wrap-multilines": [ - "off", - ], "require-yield": [ "error", ], - "rest-spread-spacing": [ - "off", - ], - "semi": [ - "off", - ], - "semi-spacing": [ - "off", - ], - "semi-style": [ - "off", - ], - "space-after-function-name": [ - "off", - ], - "space-after-keywords": [ - "off", - ], - "space-before-blocks": [ - "off", - ], - "space-before-function-paren": [ - "off", - ], - "space-before-function-parentheses": [ - "off", - ], - "space-before-keywords": [ - "off", - ], - "space-in-brackets": [ - "off", - ], - "space-in-parens": [ - "off", - ], - "space-infix-ops": [ - "off", - ], - "space-return-throw-case": [ - "off", - ], - "space-unary-ops": [ - "off", - ], - "space-unary-word-ops": [ - "off", - ], - "standard/array-bracket-even-spacing": [ - "off", - ], - "standard/computed-property-even-spacing": [ - "off", - ], - "standard/object-curly-even-spacing": [ - "off", - ], - "switch-colon-spacing": [ - "off", - ], - "template-curly-spacing": [ - "off", - ], - "template-tag-spacing": [ - "off", - ], - "unicode-bom": [ - "off", - ], - "unicorn/empty-brace-spaces": [ - "off", - ], - "unicorn/no-nested-ternary": [ - "off", - ], - "unicorn/number-literal-case": [ - "off", - ], "unused-imports/no-unused-imports": [ "error", ], @@ -1656,132 +708,6 @@ exports[`should have a correct configuration for a Jest file 1`] = ` "valid-typeof": [ "error", ], - "vue/array-bracket-newline": [ - "off", - ], - "vue/array-bracket-spacing": [ - "off", - ], - "vue/array-element-newline": [ - "off", - ], - "vue/arrow-spacing": [ - "off", - ], - "vue/block-spacing": [ - "off", - ], - "vue/block-tag-newline": [ - "off", - ], - "vue/brace-style": [ - "off", - ], - "vue/comma-dangle": [ - "off", - ], - "vue/comma-spacing": [ - "off", - ], - "vue/comma-style": [ - "off", - ], - "vue/dot-location": [ - "off", - ], - "vue/func-call-spacing": [ - "off", - ], - "vue/html-closing-bracket-newline": [ - "off", - ], - "vue/html-closing-bracket-spacing": [ - "off", - ], - "vue/html-end-tags": [ - "off", - ], - "vue/html-indent": [ - "off", - ], - "vue/html-quotes": [ - "off", - ], - "vue/html-self-closing": [ - 0, - ], - "vue/key-spacing": [ - "off", - ], - "vue/keyword-spacing": [ - "off", - ], - "vue/max-attributes-per-line": [ - "off", - ], - "vue/max-len": [ - 0, - ], - "vue/multiline-html-element-content-newline": [ - "off", - ], - "vue/multiline-ternary": [ - "off", - ], - "vue/mustache-interpolation-spacing": [ - "off", - ], - "vue/no-extra-parens": [ - "off", - ], - "vue/no-multi-spaces": [ - "off", - ], - "vue/no-spaces-around-equal-signs-in-attribute": [ - "off", - ], - "vue/object-curly-newline": [ - "off", - ], - "vue/object-curly-spacing": [ - "off", - ], - "vue/object-property-newline": [ - "off", - ], - "vue/operator-linebreak": [ - "off", - ], - "vue/quote-props": [ - "off", - ], - "vue/script-indent": [ - "off", - ], - "vue/singleline-html-element-content-newline": [ - "off", - ], - "vue/space-in-parens": [ - "off", - ], - "vue/space-infix-ops": [ - "off", - ], - "vue/space-unary-ops": [ - "off", - ], - "vue/template-curly-spacing": [ - "off", - ], - "wrap-iife": [ - "off", - ], - "wrap-regex": [ - "off", - ], - "yield-star-spacing": [ - "off", - ], }, "settings": { "import/extensions": [ @@ -1841,19 +767,12 @@ exports[`should have a correct configuration for a React file 1`] = ` "unused-imports", "import", "@typescript-eslint", - "prettier", "no-relative-import-paths", "react-hooks", "react", ], "reportUnusedDisableDirectives": undefined, "rules": { - "@babel/object-curly-spacing": [ - "off", - ], - "@babel/semi": [ - "off", - ], "@typescript-eslint/ban-ts-comment": [ "off", ], @@ -1863,40 +782,10 @@ exports[`should have a correct configuration for a React file 1`] = ` "@typescript-eslint/ban-types": [ "error", ], - "@typescript-eslint/block-spacing": [ - "off", - ], - "@typescript-eslint/brace-style": [ + "@typescript-eslint/explicit-function-return-type": [ "off", ], - "@typescript-eslint/comma-dangle": [ - "off", - ], - "@typescript-eslint/comma-spacing": [ - "off", - ], - "@typescript-eslint/explicit-function-return-type": [ - "off", - ], - "@typescript-eslint/explicit-module-boundary-types": [ - "off", - ], - "@typescript-eslint/func-call-spacing": [ - "off", - ], - "@typescript-eslint/indent": [ - "off", - ], - "@typescript-eslint/key-spacing": [ - "off", - ], - "@typescript-eslint/keyword-spacing": [ - "off", - ], - "@typescript-eslint/lines-around-comment": [ - 0, - ], - "@typescript-eslint/member-delimiter-style": [ + "@typescript-eslint/explicit-module-boundary-types": [ "off", ], "@typescript-eslint/no-array-constructor": [ @@ -1911,12 +800,6 @@ exports[`should have a correct configuration for a React file 1`] = ` "@typescript-eslint/no-extra-non-null-assertion": [ "error", ], - "@typescript-eslint/no-extra-parens": [ - "off", - ], - "@typescript-eslint/no-extra-semi": [ - "off", - ], "@typescript-eslint/no-loss-of-precision": [ "error", ], @@ -1957,17 +840,17 @@ exports[`should have a correct configuration for a React file 1`] = ` "patterns": [ { "group": [ - "*react-native*", - "!react-native-reanimated", - "!react-native-image-colors", + "**/dist", ], - "message": "React Native modules should not be imported outside of .native.ts files. If this is a .native.ts file, add an ignore comment to the top of the file. If you're trying to import a cross-platform module, add it to the whitelist in crossPlatform.js.", + "message": "Do not import from dist/ - this is an implementation detail, and breaks tree-shaking.", }, { "group": [ - "**/dist", + "*react-native*", + "!react-native-reanimated", + "!react-native-image-colors", ], - "message": "Do not import from dist/ - this is an implementation detail, and breaks tree-shaking.", + "message": "React Native modules should not be imported outside of .native.ts files. If this is a .native.ts file, add an ignore comment to the top of the file. If you're trying to import a cross-platform module, add it to the whitelist in crossPlatform.js.", }, ], }, @@ -1987,147 +870,24 @@ exports[`should have a correct configuration for a React file 1`] = ` "@typescript-eslint/no-var-requires": [ "error", ], - "@typescript-eslint/object-curly-spacing": [ - "off", - ], "@typescript-eslint/prefer-as-const": [ "error", ], - "@typescript-eslint/quotes": [ - 0, - ], - "@typescript-eslint/semi": [ - "off", - ], - "@typescript-eslint/space-before-blocks": [ - "off", - ], - "@typescript-eslint/space-before-function-paren": [ - "off", - ], - "@typescript-eslint/space-infix-ops": [ - "off", - ], "@typescript-eslint/triple-slash-reference": [ "error", ], - "@typescript-eslint/type-annotation-spacing": [ - "off", - ], - "array-bracket-newline": [ - "off", - ], - "array-bracket-spacing": [ - "off", - ], - "array-element-newline": [ - "off", - ], - "arrow-body-style": [ - "off", - ], - "arrow-parens": [ - "off", - ], - "arrow-spacing": [ - "off", - ], - "babel/object-curly-spacing": [ - "off", - ], - "babel/quotes": [ - 0, - ], - "babel/semi": [ - "off", - ], - "block-spacing": [ - "off", - ], - "brace-style": [ - "off", - ], - "comma-dangle": [ - "off", - ], - "comma-spacing": [ - "off", - ], - "comma-style": [ - "off", - ], - "computed-property-spacing": [ - "off", - ], "constructor-super": [ "off", ], "curly": [ "error", ], - "dot-location": [ - "off", - ], - "eol-last": [ - "off", - ], - "flowtype/boolean-style": [ - "off", - ], - "flowtype/delimiter-dangle": [ - "off", - ], - "flowtype/generic-spacing": [ - "off", - ], - "flowtype/object-type-curly-spacing": [ - "off", - ], - "flowtype/object-type-delimiter": [ - "off", - ], - "flowtype/quotes": [ - "off", - ], - "flowtype/semi": [ - "off", - ], - "flowtype/space-after-type-colon": [ - "off", - ], - "flowtype/space-before-generic-bracket": [ - "off", - ], - "flowtype/space-before-type-colon": [ - "off", - ], - "flowtype/union-intersection-spacing": [ - "off", - ], "for-direction": [ "error", ], - "func-call-spacing": [ - "off", - ], - "function-call-argument-newline": [ - "off", - ], - "function-paren-newline": [ - "off", - ], - "generator-star": [ - "off", - ], - "generator-star-spacing": [ - "off", - ], "getter-return": [ "off", ], - "implicit-arrow-linebreak": [ - "off", - ], "import/named": [ "off", ], @@ -2137,48 +897,9 @@ exports[`should have a correct configuration for a React file 1`] = ` "unusedExports": true, }, ], - "indent": [ - "off", - ], - "indent-legacy": [ - "off", - ], - "jsx-quotes": [ - "off", - ], - "key-spacing": [ - "off", - ], - "keyword-spacing": [ - "off", - ], - "linebreak-style": [ - "off", - ], - "lines-around-comment": [ - 0, - ], - "max-len": [ - 0, - ], - "max-statements-per-line": [ - "off", - ], - "multiline-ternary": [ - "off", - ], - "new-parens": [ - "off", - ], - "newline-per-chained-call": [ - "off", - ], "no-array-constructor": [ "off", ], - "no-arrow-condition": [ - "off", - ], "no-async-promise-executor": [ "error", ], @@ -2188,18 +909,12 @@ exports[`should have a correct configuration for a React file 1`] = ` "no-class-assign": [ "error", ], - "no-comma-dangle": [ - "off", - ], "no-compare-neg-zero": [ "error", ], "no-cond-assign": [ "error", ], - "no-confusing-arrow": [ - 0, - ], "no-console": [ "error", ], @@ -2248,18 +963,12 @@ exports[`should have a correct configuration for a React file 1`] = ` "no-extra-boolean-cast": [ "error", ], - "no-extra-parens": [ - "off", - ], "no-extra-semi": [ - "off", + "error", ], "no-fallthrough": [ "error", ], - "no-floating-decimal": [ - "off", - ], "no-func-assign": [ "off", ], @@ -2284,17 +993,8 @@ exports[`should have a correct configuration for a React file 1`] = ` "no-misleading-character-class": [ "error", ], - "no-mixed-operators": [ - 0, - ], "no-mixed-spaces-and-tabs": [ - "off", - ], - "no-multi-spaces": [ - "off", - ], - "no-multiple-empty-lines": [ - "off", + "error", ], "no-new-symbol": [ "off", @@ -2317,9 +1017,6 @@ exports[`should have a correct configuration for a React file 1`] = ` "no-regex-spaces": [ "error", ], - "no-reserved-keys": [ - "off", - ], "no-self-assign": [ "error", ], @@ -2329,29 +1026,17 @@ exports[`should have a correct configuration for a React file 1`] = ` "no-shadow-restricted-names": [ "error", ], - "no-space-before-semi": [ - "off", - ], - "no-spaced-func": [ - "off", - ], "no-sparse-arrays": [ "error", ], - "no-tabs": [ - 0, - ], "no-this-before-super": [ "off", ], - "no-trailing-spaces": [ - "off", - ], "no-undef": [ "off", ], "no-unexpected-multiline": [ - 0, + "error", ], "no-unreachable": [ "off", @@ -2383,43 +1068,13 @@ exports[`should have a correct configuration for a React file 1`] = ` "no-var": [ "error", ], - "no-whitespace-before-property": [ - "off", - ], "no-with": [ "error", ], - "no-wrap-func": [ - "off", - ], - "nonblock-statement-body-position": [ - "off", - ], - "object-curly-newline": [ - "off", - ], - "object-curly-spacing": [ - "off", - ], - "object-property-newline": [ - "off", - ], "object-shorthand": [ "error", "always", ], - "one-var-declaration-per-line": [ - "off", - ], - "operator-linebreak": [ - "off", - ], - "padded-blocks": [ - "off", - ], - "prefer-arrow-callback": [ - "off", - ], "prefer-const": [ "error", ], @@ -2429,20 +1084,6 @@ exports[`should have a correct configuration for a React file 1`] = ` "prefer-spread": [ "error", ], - "prettier/prettier": [ - "error", - { - "printWidth": 120, - "semi": false, - "singleQuote": true, - }, - ], - "quote-props": [ - "off", - ], - "quotes": [ - 0, - ], "react-hooks/exhaustive-deps": [ "warn", ], @@ -2452,15 +1093,6 @@ exports[`should have a correct configuration for a React file 1`] = ` "react/display-name": [ 2, ], - "react/jsx-child-element-spacing": [ - "off", - ], - "react/jsx-closing-bracket-location": [ - "off", - ], - "react/jsx-closing-tag-location": [ - "off", - ], "react/jsx-curly-brace-presence": [ "error", { @@ -2468,33 +1100,9 @@ exports[`should have a correct configuration for a React file 1`] = ` "props": "never", }, ], - "react/jsx-curly-newline": [ - "off", - ], - "react/jsx-curly-spacing": [ - "off", - ], - "react/jsx-equals-spacing": [ - "off", - ], - "react/jsx-first-prop-new-line": [ - "off", - ], - "react/jsx-indent": [ - "off", - ], - "react/jsx-indent-props": [ - "off", - ], "react/jsx-key": [ 2, ], - "react/jsx-max-props-per-line": [ - "off", - ], - "react/jsx-newline": [ - "off", - ], "react/jsx-no-comment-textnodes": [ 2, ], @@ -2507,27 +1115,12 @@ exports[`should have a correct configuration for a React file 1`] = ` "react/jsx-no-undef": [ 2, ], - "react/jsx-one-expression-per-line": [ - "off", - ], - "react/jsx-props-no-multi-spaces": [ - "off", - ], - "react/jsx-space-before-closing": [ - "off", - ], - "react/jsx-tag-spacing": [ - "off", - ], "react/jsx-uses-react": [ 2, ], "react/jsx-uses-vars": [ 2, ], - "react/jsx-wrap-multilines": [ - "off", - ], "react/no-children-prop": [ 2, ], @@ -2573,230 +1166,26 @@ exports[`should have a correct configuration for a React file 1`] = ` "require-yield": [ "error", ], - "rest-spread-spacing": [ - "off", + "unused-imports/no-unused-imports": [ + "error", ], - "semi": [ - "off", + "use-isnan": [ + "error", ], - "semi-spacing": [ - "off", + "valid-typeof": [ + "error", ], - "semi-style": [ - "off", + }, + "settings": { + "import/extensions": [ + ".ts", + ".tsx", + ".js", + ".jsx", ], - "space-after-function-name": [ - "off", - ], - "space-after-keywords": [ - "off", - ], - "space-before-blocks": [ - "off", - ], - "space-before-function-paren": [ - "off", - ], - "space-before-function-parentheses": [ - "off", - ], - "space-before-keywords": [ - "off", - ], - "space-in-brackets": [ - "off", - ], - "space-in-parens": [ - "off", - ], - "space-infix-ops": [ - "off", - ], - "space-return-throw-case": [ - "off", - ], - "space-unary-ops": [ - "off", - ], - "space-unary-word-ops": [ - "off", - ], - "standard/array-bracket-even-spacing": [ - "off", - ], - "standard/computed-property-even-spacing": [ - "off", - ], - "standard/object-curly-even-spacing": [ - "off", - ], - "switch-colon-spacing": [ - "off", - ], - "template-curly-spacing": [ - "off", - ], - "template-tag-spacing": [ - "off", - ], - "unicode-bom": [ - "off", - ], - "unicorn/empty-brace-spaces": [ - "off", - ], - "unicorn/no-nested-ternary": [ - "off", - ], - "unicorn/number-literal-case": [ - "off", - ], - "unused-imports/no-unused-imports": [ - "error", - ], - "use-isnan": [ - "error", - ], - "valid-typeof": [ - "error", - ], - "vue/array-bracket-newline": [ - "off", - ], - "vue/array-bracket-spacing": [ - "off", - ], - "vue/array-element-newline": [ - "off", - ], - "vue/arrow-spacing": [ - "off", - ], - "vue/block-spacing": [ - "off", - ], - "vue/block-tag-newline": [ - "off", - ], - "vue/brace-style": [ - "off", - ], - "vue/comma-dangle": [ - "off", - ], - "vue/comma-spacing": [ - "off", - ], - "vue/comma-style": [ - "off", - ], - "vue/dot-location": [ - "off", - ], - "vue/func-call-spacing": [ - "off", - ], - "vue/html-closing-bracket-newline": [ - "off", - ], - "vue/html-closing-bracket-spacing": [ - "off", - ], - "vue/html-end-tags": [ - "off", - ], - "vue/html-indent": [ - "off", - ], - "vue/html-quotes": [ - "off", - ], - "vue/html-self-closing": [ - 0, - ], - "vue/key-spacing": [ - "off", - ], - "vue/keyword-spacing": [ - "off", - ], - "vue/max-attributes-per-line": [ - "off", - ], - "vue/max-len": [ - 0, - ], - "vue/multiline-html-element-content-newline": [ - "off", - ], - "vue/multiline-ternary": [ - "off", - ], - "vue/mustache-interpolation-spacing": [ - "off", - ], - "vue/no-extra-parens": [ - "off", - ], - "vue/no-multi-spaces": [ - "off", - ], - "vue/no-spaces-around-equal-signs-in-attribute": [ - "off", - ], - "vue/object-curly-newline": [ - "off", - ], - "vue/object-curly-spacing": [ - "off", - ], - "vue/object-property-newline": [ - "off", - ], - "vue/operator-linebreak": [ - "off", - ], - "vue/quote-props": [ - "off", - ], - "vue/script-indent": [ - "off", - ], - "vue/singleline-html-element-content-newline": [ - "off", - ], - "vue/space-in-parens": [ - "off", - ], - "vue/space-infix-ops": [ - "off", - ], - "vue/space-unary-ops": [ - "off", - ], - "vue/template-curly-spacing": [ - "off", - ], - "wrap-iife": [ - "off", - ], - "wrap-regex": [ - "off", - ], - "yield-star-spacing": [ - "off", - ], - }, - "settings": { - "import/extensions": [ - ".ts", - ".tsx", - ".js", - ".jsx", - ], - "import/external-module-folders": [ - "node_modules", - "node_modules/@types", + "import/external-module-folders": [ + "node_modules", + "node_modules/@types", ], "import/parsers": { "@typescript-eslint/parser": [ @@ -2842,16 +1231,9 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "unused-imports", "import", "@typescript-eslint", - "prettier", ], "reportUnusedDisableDirectives": undefined, "rules": { - "@babel/object-curly-spacing": [ - "off", - ], - "@babel/semi": [ - "off", - ], "@typescript-eslint/ban-ts-comment": [ "off", ], @@ -2861,42 +1243,12 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "@typescript-eslint/ban-types": [ "error", ], - "@typescript-eslint/block-spacing": [ - "off", - ], - "@typescript-eslint/brace-style": [ - "off", - ], - "@typescript-eslint/comma-dangle": [ - "off", - ], - "@typescript-eslint/comma-spacing": [ - "off", - ], "@typescript-eslint/explicit-function-return-type": [ "off", ], "@typescript-eslint/explicit-module-boundary-types": [ "off", ], - "@typescript-eslint/func-call-spacing": [ - "off", - ], - "@typescript-eslint/indent": [ - "off", - ], - "@typescript-eslint/key-spacing": [ - "off", - ], - "@typescript-eslint/keyword-spacing": [ - "off", - ], - "@typescript-eslint/lines-around-comment": [ - 0, - ], - "@typescript-eslint/member-delimiter-style": [ - "off", - ], "@typescript-eslint/no-array-constructor": [ "error", ], @@ -2909,12 +1261,6 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "@typescript-eslint/no-extra-non-null-assertion": [ "error", ], - "@typescript-eslint/no-extra-parens": [ - "off", - ], - "@typescript-eslint/no-extra-semi": [ - "off", - ], "@typescript-eslint/no-loss-of-precision": [ "error", ], @@ -2936,204 +1282,42 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "@typescript-eslint/no-unsafe-declaration-merging": [ "error", ], - "@typescript-eslint/no-unused-vars": [ - "error", - ], - "@typescript-eslint/no-var-requires": [ - "error", - ], - "@typescript-eslint/object-curly-spacing": [ - "off", - ], - "@typescript-eslint/prefer-as-const": [ - "error", - ], - "@typescript-eslint/quotes": [ - 0, - ], - "@typescript-eslint/semi": [ - "off", - ], - "@typescript-eslint/space-before-blocks": [ - "off", - ], - "@typescript-eslint/space-before-function-paren": [ - "off", - ], - "@typescript-eslint/space-infix-ops": [ - "off", - ], - "@typescript-eslint/triple-slash-reference": [ - "error", - ], - "@typescript-eslint/type-annotation-spacing": [ - "off", - ], - "array-bracket-newline": [ - "off", - ], - "array-bracket-spacing": [ - "off", - ], - "array-element-newline": [ - "off", - ], - "arrow-body-style": [ - "off", - ], - "arrow-parens": [ - "off", - ], - "arrow-spacing": [ - "off", - ], - "babel/object-curly-spacing": [ - "off", - ], - "babel/quotes": [ - 0, - ], - "babel/semi": [ - "off", - ], - "block-spacing": [ - "off", - ], - "brace-style": [ - "off", - ], - "comma-dangle": [ - "off", - ], - "comma-spacing": [ - "off", - ], - "comma-style": [ - "off", - ], - "computed-property-spacing": [ - "off", - ], - "constructor-super": [ - "off", - ], - "curly": [ - 0, - ], - "dot-location": [ - "off", - ], - "eol-last": [ - "off", - ], - "flowtype/boolean-style": [ - "off", - ], - "flowtype/delimiter-dangle": [ - "off", - ], - "flowtype/generic-spacing": [ - "off", - ], - "flowtype/object-type-curly-spacing": [ - "off", - ], - "flowtype/object-type-delimiter": [ - "off", - ], - "flowtype/quotes": [ - "off", - ], - "flowtype/semi": [ - "off", - ], - "flowtype/space-after-type-colon": [ - "off", - ], - "flowtype/space-before-generic-bracket": [ - "off", - ], - "flowtype/space-before-type-colon": [ - "off", - ], - "flowtype/union-intersection-spacing": [ - "off", - ], - "for-direction": [ - "error", - ], - "func-call-spacing": [ - "off", - ], - "function-call-argument-newline": [ - "off", - ], - "function-paren-newline": [ - "off", - ], - "generator-star": [ - "off", - ], - "generator-star-spacing": [ - "off", - ], - "getter-return": [ - "off", - ], - "implicit-arrow-linebreak": [ - "off", - ], - "import/named": [ - "off", - ], - "import/no-unused-modules": [ - "error", - { - "unusedExports": true, - }, - ], - "indent": [ - "off", - ], - "indent-legacy": [ - "off", + "@typescript-eslint/no-unused-vars": [ + "error", ], - "jsx-quotes": [ - "off", + "@typescript-eslint/no-var-requires": [ + "error", ], - "key-spacing": [ - "off", + "@typescript-eslint/prefer-as-const": [ + "error", ], - "keyword-spacing": [ - "off", + "@typescript-eslint/triple-slash-reference": [ + "error", ], - "linebreak-style": [ + "constructor-super": [ "off", ], - "lines-around-comment": [ - 0, - ], - "max-len": [ - 0, + "curly": [ + "error", ], - "max-statements-per-line": [ - "off", + "for-direction": [ + "error", ], - "multiline-ternary": [ + "getter-return": [ "off", ], - "new-parens": [ + "import/named": [ "off", ], - "newline-per-chained-call": [ - "off", + "import/no-unused-modules": [ + "error", + { + "unusedExports": true, + }, ], "no-array-constructor": [ "off", ], - "no-arrow-condition": [ - "off", - ], "no-async-promise-executor": [ "error", ], @@ -3143,18 +1327,12 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "no-class-assign": [ "error", ], - "no-comma-dangle": [ - "off", - ], "no-compare-neg-zero": [ "error", ], "no-cond-assign": [ "error", ], - "no-confusing-arrow": [ - 0, - ], "no-console": [ "error", ], @@ -3203,18 +1381,12 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "no-extra-boolean-cast": [ "error", ], - "no-extra-parens": [ - "off", - ], "no-extra-semi": [ - "off", + "error", ], "no-fallthrough": [ "error", ], - "no-floating-decimal": [ - "off", - ], "no-func-assign": [ "off", ], @@ -3239,17 +1411,8 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "no-misleading-character-class": [ "error", ], - "no-mixed-operators": [ - 0, - ], "no-mixed-spaces-and-tabs": [ - "off", - ], - "no-multi-spaces": [ - "off", - ], - "no-multiple-empty-lines": [ - "off", + "error", ], "no-new-symbol": [ "off", @@ -3272,9 +1435,6 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "no-regex-spaces": [ "error", ], - "no-reserved-keys": [ - "off", - ], "no-self-assign": [ "error", ], @@ -3284,29 +1444,17 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "no-shadow-restricted-names": [ "error", ], - "no-space-before-semi": [ - "off", - ], - "no-spaced-func": [ - "off", - ], "no-sparse-arrays": [ "error", ], - "no-tabs": [ - 0, - ], "no-this-before-super": [ "off", ], - "no-trailing-spaces": [ - "off", - ], "no-undef": [ "off", ], "no-unexpected-multiline": [ - 0, + "error", ], "no-unreachable": [ "off", @@ -3338,43 +1486,13 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "no-var": [ "error", ], - "no-whitespace-before-property": [ - "off", - ], "no-with": [ "error", ], - "no-wrap-func": [ - "off", - ], - "nonblock-statement-body-position": [ - "off", - ], - "object-curly-newline": [ - "off", - ], - "object-curly-spacing": [ - "off", - ], - "object-property-newline": [ - "off", - ], "object-shorthand": [ "error", "always", ], - "one-var-declaration-per-line": [ - "off", - ], - "operator-linebreak": [ - "off", - ], - "padded-blocks": [ - "off", - ], - "prefer-arrow-callback": [ - "off", - ], "prefer-const": [ "error", ], @@ -3384,149 +1502,9 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "prefer-spread": [ "error", ], - "prettier/prettier": [ - "error", - { - "printWidth": 120, - "semi": false, - "singleQuote": true, - }, - ], - "quote-props": [ - "off", - ], - "quotes": [ - 0, - ], - "react/jsx-child-element-spacing": [ - "off", - ], - "react/jsx-closing-bracket-location": [ - "off", - ], - "react/jsx-closing-tag-location": [ - "off", - ], - "react/jsx-curly-newline": [ - "off", - ], - "react/jsx-curly-spacing": [ - "off", - ], - "react/jsx-equals-spacing": [ - "off", - ], - "react/jsx-first-prop-new-line": [ - "off", - ], - "react/jsx-indent": [ - "off", - ], - "react/jsx-indent-props": [ - "off", - ], - "react/jsx-max-props-per-line": [ - "off", - ], - "react/jsx-newline": [ - "off", - ], - "react/jsx-one-expression-per-line": [ - "off", - ], - "react/jsx-props-no-multi-spaces": [ - "off", - ], - "react/jsx-space-before-closing": [ - "off", - ], - "react/jsx-tag-spacing": [ - "off", - ], - "react/jsx-wrap-multilines": [ - "off", - ], "require-yield": [ "error", ], - "rest-spread-spacing": [ - "off", - ], - "semi": [ - "off", - ], - "semi-spacing": [ - "off", - ], - "semi-style": [ - "off", - ], - "space-after-function-name": [ - "off", - ], - "space-after-keywords": [ - "off", - ], - "space-before-blocks": [ - "off", - ], - "space-before-function-paren": [ - "off", - ], - "space-before-function-parentheses": [ - "off", - ], - "space-before-keywords": [ - "off", - ], - "space-in-brackets": [ - "off", - ], - "space-in-parens": [ - "off", - ], - "space-infix-ops": [ - "off", - ], - "space-return-throw-case": [ - "off", - ], - "space-unary-ops": [ - "off", - ], - "space-unary-word-ops": [ - "off", - ], - "standard/array-bracket-even-spacing": [ - "off", - ], - "standard/computed-property-even-spacing": [ - "off", - ], - "standard/object-curly-even-spacing": [ - "off", - ], - "switch-colon-spacing": [ - "off", - ], - "template-curly-spacing": [ - "off", - ], - "template-tag-spacing": [ - "off", - ], - "unicode-bom": [ - "off", - ], - "unicorn/empty-brace-spaces": [ - "off", - ], - "unicorn/no-nested-ternary": [ - "off", - ], - "unicorn/number-literal-case": [ - "off", - ], "unused-imports/no-unused-imports": [ "error", ], @@ -3536,132 +1514,6 @@ exports[`should have a correct configuration for a TypeScript file 1`] = ` "valid-typeof": [ "error", ], - "vue/array-bracket-newline": [ - "off", - ], - "vue/array-bracket-spacing": [ - "off", - ], - "vue/array-element-newline": [ - "off", - ], - "vue/arrow-spacing": [ - "off", - ], - "vue/block-spacing": [ - "off", - ], - "vue/block-tag-newline": [ - "off", - ], - "vue/brace-style": [ - "off", - ], - "vue/comma-dangle": [ - "off", - ], - "vue/comma-spacing": [ - "off", - ], - "vue/comma-style": [ - "off", - ], - "vue/dot-location": [ - "off", - ], - "vue/func-call-spacing": [ - "off", - ], - "vue/html-closing-bracket-newline": [ - "off", - ], - "vue/html-closing-bracket-spacing": [ - "off", - ], - "vue/html-end-tags": [ - "off", - ], - "vue/html-indent": [ - "off", - ], - "vue/html-quotes": [ - "off", - ], - "vue/html-self-closing": [ - 0, - ], - "vue/key-spacing": [ - "off", - ], - "vue/keyword-spacing": [ - "off", - ], - "vue/max-attributes-per-line": [ - "off", - ], - "vue/max-len": [ - 0, - ], - "vue/multiline-html-element-content-newline": [ - "off", - ], - "vue/multiline-ternary": [ - "off", - ], - "vue/mustache-interpolation-spacing": [ - "off", - ], - "vue/no-extra-parens": [ - "off", - ], - "vue/no-multi-spaces": [ - "off", - ], - "vue/no-spaces-around-equal-signs-in-attribute": [ - "off", - ], - "vue/object-curly-newline": [ - "off", - ], - "vue/object-curly-spacing": [ - "off", - ], - "vue/object-property-newline": [ - "off", - ], - "vue/operator-linebreak": [ - "off", - ], - "vue/quote-props": [ - "off", - ], - "vue/script-indent": [ - "off", - ], - "vue/singleline-html-element-content-newline": [ - "off", - ], - "vue/space-in-parens": [ - "off", - ], - "vue/space-infix-ops": [ - "off", - ], - "vue/space-unary-ops": [ - "off", - ], - "vue/template-curly-spacing": [ - "off", - ], - "wrap-iife": [ - "off", - ], - "wrap-regex": [ - "off", - ], - "yield-star-spacing": [ - "off", - ], }, "settings": { "import/extensions": [ diff --git a/packages/eslint-config/base.js b/packages/eslint-config/base.js index b8127489d2a..4cae5651932 100644 --- a/packages/eslint-config/base.js +++ b/packages/eslint-config/base.js @@ -10,14 +10,6 @@ module.exports = { sourceType: 'module', }, rules: { - 'prettier/prettier': [ - 'error', - { - semi: false, - singleQuote: true, - printWidth: 120, - }, - ], 'import/no-unused-modules': ['error', { unusedExports: true }], 'object-shorthand': ['error', 'always'], 'unused-imports/no-unused-imports': 'error', @@ -72,10 +64,5 @@ module.exports = { extends: ['plugin:cypress/recommended'], plugins: ['cypress'], }, - { - files: ['*'], - plugins: ['prettier'], - extends: ['plugin:prettier/recommended'], - }, ], } diff --git a/packages/eslint-config/crossPlatform.js b/packages/eslint-config/crossPlatform.js index 9c1453bb602..66895182b56 100644 --- a/packages/eslint-config/crossPlatform.js +++ b/packages/eslint-config/crossPlatform.js @@ -1,7 +1,13 @@ const { crossPlatform: restrictedImports } = require('@uniswap/eslint-config/restrictedImports') module.exports = { - rules: { - 'no-restricted-imports': ['error', restrictedImports], - }, + overrides: [ + { + files: ['*.ts', '*.tsx'], + excludedFiles: ['*.native.*', '*.ios.*', '*.android.*'], + rules: { + 'no-restricted-imports': ['error', restrictedImports], + }, + }, + ], } diff --git a/packages/eslint-config/native.js b/packages/eslint-config/native.js index afb2f644580..6c5fb3edec1 100644 --- a/packages/eslint-config/native.js +++ b/packages/eslint-config/native.js @@ -33,7 +33,6 @@ module.exports = { 'eslint:recommended', '@react-native-community', 'plugin:jest/recommended', - 'plugin:prettier/recommended', 'plugin:@typescript-eslint/recommended', ], plugins: [ @@ -50,6 +49,14 @@ module.exports = { ], rules: { ...complexityRules, + + // disable prettier linting and linting that we leave to prettier: + 'prettier/prettier': 0, + semi: 0, + quotes: 0, + 'comma-dangle': 0, + 'no-trailing-spaces': 0, + // tamagui encourages inline styles and makes them fast 'react-native/no-inline-styles': 'off', 'guard-for-in': 'error', @@ -277,16 +284,6 @@ module.exports = { 'react/no-danger': 'error', 'react/no-danger-with-children': 'error', 'react/no-unsafe': 'error', - // Overwrite default Prettier settings - https://prettier.io/docs/en/options.html - 'prettier/prettier': [ - 2, - { - bracketSameLine: true, - singleQuote: true, - printWidth: 100, - semi: false, - }, - ], }, overrides: [ { @@ -464,6 +461,19 @@ module.exports = { 'max-lines': ['off'], // cap file length }, }, + { + files: ['apps/stretch/src/contentScript/injected.ts'], + rules: { + 'no-restricted-syntax': [ + 'error', + { + selector: 'CallExpression[callee.object.name="logger"][callee.property.name!=/^(debug)$/]', + message: + 'Only logger.debug is allowed in this file. Please handle errors and info logs explicitly using ErrorLog and InfoLog message passing.', + }, + ], + }, + }, ], globals: { Address: 'readonly', diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 25550247ec4..867d06717f7 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -7,6 +7,7 @@ "bugs": "https://github.com/Uniswap/eslint-config/issues", "license": "MIT", "scripts": { + "format": "../../scripts/prettier.sh", "lint": "eslint .", "test": "jest" }, @@ -40,9 +41,7 @@ "eslint-plugin-security": "1.5.0", "eslint-plugin-spellcheck": "0.0.20", "eslint-plugin-storybook": "^0.6.10", - "eslint-plugin-unused-imports": "^2.0.0", - "prettier": "^2.8.0", - "prettier-plugin-organize-imports": "3.2.4" + "eslint-plugin-unused-imports": "^2.0.0" }, "peerDependencies": { "eslint": "^8.0.0" diff --git a/packages/eslint-config/react.js b/packages/eslint-config/react.js index 51ed8559dfc..c45deb9ed06 100644 --- a/packages/eslint-config/react.js +++ b/packages/eslint-config/react.js @@ -26,6 +26,7 @@ module.exports = { overrides: [ { files: ['*.ts', '*.tsx'], + excludedFiles: ['*.native.*', '*.ios.*', '*.android.*'], rules: { '@typescript-eslint/no-restricted-imports': ['error', restrictedImports], }, diff --git a/packages/eslint-config/restrictedImports.js b/packages/eslint-config/restrictedImports.js index 619ed8c135d..b32c3f82785 100644 --- a/packages/eslint-config/restrictedImports.js +++ b/packages/eslint-config/restrictedImports.js @@ -19,7 +19,12 @@ exports.shared = { 'Avoid using due to issue with unsupported locales. Use utilities/src/device/locales.ts getDeviceLocales instead', }, ], - patterns: [], + patterns: [ + { + group: ['**/dist'], + message: 'Do not import from dist/ - this is an implementation detail, and breaks tree-shaking.', + }, + ], } exports.crossPlatform = { @@ -42,9 +47,5 @@ exports.crossPlatform = { message: "React Native modules should not be imported outside of .native.ts files. If this is a .native.ts file, add an ignore comment to the top of the file. If you're trying to import a cross-platform module, add it to the whitelist in crossPlatform.js.", }, - { - group: ['**/dist'], - message: 'Do not import from dist/ - this is an implementation detail, and breaks tree-shaking.', - }, ], } diff --git a/packages/ui/.depcheckrc b/packages/ui/.depcheckrc index 5864867eab3..c66de7d21a2 100644 --- a/packages/ui/.depcheckrc +++ b/packages/ui/.depcheckrc @@ -11,4 +11,5 @@ ignores: [ "utilities", # used in package.json "esbuild-register", + "prettier" ] diff --git a/packages/ui/.eslintignore b/packages/ui/.eslintignore new file mode 100644 index 00000000000..8ff6498ffa4 --- /dev/null +++ b/packages/ui/.eslintignore @@ -0,0 +1,2 @@ +src/components/logos/** +src/components/icons/** diff --git a/packages/ui/.prettierignore b/packages/ui/.prettierignore deleted file mode 100644 index 958d3d0270c..00000000000 --- a/packages/ui/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -.turbo -dist -node_modules -types diff --git a/packages/ui/jest.config.js b/packages/ui/jest.config.js index 59da3fa50bf..39beb9dfc94 100644 --- a/packages/ui/jest.config.js +++ b/packages/ui/jest.config.js @@ -5,6 +5,17 @@ const preset = require('../../config/jest-presets/jest/jest-preset') module.exports = { ...preset, + transform: { + '\\.svg$': 'jest-transformer-svg', + '^.+\\.jsx?$': 'babel-jest', + '^.+\\.tsx?$': [ + 'ts-jest', + { + // this avoids type checking other modules + isolatedModules: true + }, + ], + }, displayName: 'UI Package', collectCoverageFrom: [ 'src/**/*.{js,ts,tsx}', diff --git a/packages/ui/package.json b/packages/ui/package.json index f7ace3012bf..6fc09017428 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -55,6 +55,7 @@ "build:icons:missing": "yarn build:icons --skip-existing", "check:deps:usage": "depcheck", "lint": "eslint src --max-warnings=0", + "format": "../../scripts/prettier.sh", "lint:fix": "eslint src --fix", "test": "jest && echo 'ignoring'", "typecheck": "tsc -b", diff --git a/packages/ui/src/animations/AnimateInOrder.tsx b/packages/ui/src/animations/AnimateInOrder.tsx index d0ef1a0bd37..8d32900f18d 100644 --- a/packages/ui/src/animations/AnimateInOrder.tsx +++ b/packages/ui/src/animations/AnimateInOrder.tsx @@ -21,12 +21,7 @@ export const AnimateInOrder = ({ >): JSX.Element => { return ( - + {children} diff --git a/packages/ui/src/animations/Jiggly.tsx b/packages/ui/src/animations/Jiggly.tsx index 98382b53975..259240dbd23 100644 --- a/packages/ui/src/animations/Jiggly.tsx +++ b/packages/ui/src/animations/Jiggly.tsx @@ -1,11 +1,5 @@ import { PropsWithChildren } from 'react' -import { - useAnimatedStyle, - useSharedValue, - withRepeat, - withSequence, - withTiming, -} from 'react-native-reanimated' +import { useAnimatedStyle, useSharedValue, withRepeat, withSequence, withTiming } from 'react-native-reanimated' import { AnimatedTouchableArea } from 'ui/src/components/touchable' import { HapticFeedback, ImpactFeedbackStyle } from 'ui/src/utils/haptics/HapticFeedback' @@ -22,9 +16,12 @@ export const Jiggly = ({ duration?: number }>): JSX.Element => { const rotate = useSharedValue(0) - const style = useAnimatedStyle(() => ({ - transform: [{ rotateZ: `${rotate.value}deg` }], - })) + const style = useAnimatedStyle( + () => ({ + transform: [{ rotateZ: `${rotate.value}deg` }], + }), + [rotate], + ) const onPress = async (): Promise => { if (hapticFeedback) { @@ -34,7 +31,7 @@ export const Jiggly = ({ rotate.value = withSequence( withTiming(-offset, { duration: duration / 2 }), withRepeat(withTiming(offset, { duration }), 5, true), - withTiming(0, { duration: duration / 2 }) + withTiming(0, { duration: duration / 2 }), ) } diff --git a/packages/ui/src/assets/graphics/extension-promo-modal-dark-ga.png b/packages/ui/src/assets/graphics/extension-promo-modal-dark-ga.png new file mode 100644 index 00000000000..ad76b5c4d31 Binary files /dev/null and b/packages/ui/src/assets/graphics/extension-promo-modal-dark-ga.png differ diff --git a/packages/ui/src/assets/graphics/extension-promo-modal-light-ga.png b/packages/ui/src/assets/graphics/extension-promo-modal-light-ga.png new file mode 100644 index 00000000000..00937dbff84 Binary files /dev/null and b/packages/ui/src/assets/graphics/extension-promo-modal-light-ga.png differ diff --git a/packages/ui/src/assets/icons/copy-sheets.svg b/packages/ui/src/assets/icons/copy-sheets.svg index 929849dd01b..413d0b5634b 100644 --- a/packages/ui/src/assets/icons/copy-sheets.svg +++ b/packages/ui/src/assets/icons/copy-sheets.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/edit.svg b/packages/ui/src/assets/icons/edit.svg index 34afc19479c..5a5d20ac238 100644 --- a/packages/ui/src/assets/icons/edit.svg +++ b/packages/ui/src/assets/icons/edit.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/trash-filled.svg b/packages/ui/src/assets/icons/trash-filled.svg index d9645dd2e48..1a6a2143c22 100644 --- a/packages/ui/src/assets/icons/trash-filled.svg +++ b/packages/ui/src/assets/icons/trash-filled.svg @@ -1,3 +1,3 @@ - + diff --git a/packages/ui/src/assets/index.ts b/packages/ui/src/assets/index.ts index 175eb91984d..8dbcacf0c25 100644 --- a/packages/ui/src/assets/index.ts +++ b/packages/ui/src/assets/index.ts @@ -30,6 +30,9 @@ export const EXTENSION_PROMO_BANNER_DARK = require('./graphics/extension-promo-b export const EXTENSION_PROMO_MODAL_LIGHT = require('./graphics/extension-promo-modal-light.png') export const EXTENSION_PROMO_MODAL_DARK = require('./graphics/extension-promo-modal-dark.png') +export const EXTENSION_PROMO_BANNER_LIGHT_GA = require('./graphics/extension-promo-modal-light-ga.png') +export const EXTENSION_PROMO_BANNER_DARK_GA = require('./graphics/extension-promo-modal-dark-ga.png') + export const DAI_LOGO = require('./logos/png/dai-logo.png') export const USDC_LOGO = require('./logos/png/usdc-logo.png') export const ETH_LOGO = require('./logos/png/eth-logo.png') diff --git a/packages/ui/src/components/AnimatedFlashList/AnimatedFlashList.tsx b/packages/ui/src/components/AnimatedFlashList/AnimatedFlashList.tsx index 6de8965b0d2..58f8b7b8766 100644 --- a/packages/ui/src/components/AnimatedFlashList/AnimatedFlashList.tsx +++ b/packages/ui/src/components/AnimatedFlashList/AnimatedFlashList.tsx @@ -22,19 +22,18 @@ type AnimatedFlashListProps = FlatListProps & export const AnimatedFlashList = forwardRef( function _AnimatedFlashList(props, ref) { return - } + }, ) -export const AnimatedBottomSheetFlashList = forwardRef< - typeof ReanimatedFlashList, - AnimatedFlashListProps ->(function _AnimatedBottomSheetFlashList(props, ref) { - return ( - - ) -}) +export const AnimatedBottomSheetFlashList = forwardRef( + function _AnimatedBottomSheetFlashList(props, ref) { + return ( + + ) + }, +) diff --git a/packages/ui/src/components/Unicon/index.native.tsx b/packages/ui/src/components/Unicon/index.native.tsx index ef65305236f..f1ecae312c9 100644 --- a/packages/ui/src/components/Unicon/index.native.tsx +++ b/packages/ui/src/components/Unicon/index.native.tsx @@ -2,11 +2,7 @@ import { Canvas, Circle, Group, Path } from '@shopify/react-native-skia' import { memo } from 'react' import { IconPaths, Icons } from 'ui/src/components/Unicon/UniconSVGs' import { UniconProps } from 'ui/src/components/Unicon/types' -import { - getUniconColors, - getUniconsDeterministicHash, - isValidEthAddress, -} from 'ui/src/components/Unicon/utils' +import { getUniconColors, getUniconsDeterministicHash, isValidEthAddress } from 'ui/src/components/Unicon/utils' import { Flex } from 'ui/src/components/layout' import { useIsDarkMode } from 'ui/src/hooks/useIsDarkMode' @@ -48,12 +44,7 @@ export function _Unicon({ address, size = 32 }: UniconProps): JSX.Element | null return ( - + {/* This is the shape generation code */} diff --git a/packages/ui/src/components/Unicon/index.web.tsx b/packages/ui/src/components/Unicon/index.web.tsx index 170e86d55dc..64b962e8537 100644 --- a/packages/ui/src/components/Unicon/index.web.tsx +++ b/packages/ui/src/components/Unicon/index.web.tsx @@ -1,11 +1,7 @@ import React from 'react' import { IconPaths, Icons } from 'ui/src/components/Unicon/UniconSVGs' import { UniconProps } from 'ui/src/components/Unicon/types' -import { - getUniconColors, - getUniconsDeterministicHash, - isValidEthAddress, -} from 'ui/src/components/Unicon/utils' +import { getUniconColors, getUniconsDeterministicHash, isValidEthAddress } from 'ui/src/components/Unicon/utils' import { useIsDarkMode } from 'ui/src/hooks/useIsDarkMode' const styles = { transformOrigin: 'center center' } @@ -45,14 +41,10 @@ export const Unicon: React.FC = ({ address, size = 32 }) => { height={size} // Use the size prop to control SVG dimensions viewBox={`0 0 ${size} ${size}`} width={size} - xmlns="http://www.w3.org/2000/svg"> + xmlns="http://www.w3.org/2000/svg" + > - + {selectedIconPaths.map((pathData: string, index: number) => ( diff --git a/packages/ui/src/components/Unicon/utils.ts b/packages/ui/src/components/Unicon/utils.ts index 9492f149d88..5945b2c7a54 100644 --- a/packages/ui/src/components/Unicon/utils.ts +++ b/packages/ui/src/components/Unicon/utils.ts @@ -13,16 +13,12 @@ export const getUniconsDeterministicHash = (address: string): bigint => { const ETH_ADDRESS_LENGTH = 42 // Ethereum addresses are 42 characters long including '0x' // TODO: move to a shared location in utilities or wallet package export const isValidEthAddress = (address: string): boolean => { - return Boolean( - address.startsWith('0x') && - isAddress(address.toLowerCase()) && - address.length === ETH_ADDRESS_LENGTH - ) + return Boolean(address.startsWith('0x') && isAddress(address.toLowerCase()) && address.length === ETH_ADDRESS_LENGTH) } export const getUniconColors = ( activeAddress: string, - isDark: boolean + isDark: boolean, ): { color: string } => { diff --git a/packages/ui/src/components/UniversalImage/UniversalImage.tsx b/packages/ui/src/components/UniversalImage/UniversalImage.tsx index 5cd7b334b33..a16ad9d4f13 100644 --- a/packages/ui/src/components/UniversalImage/UniversalImage.tsx +++ b/packages/ui/src/components/UniversalImage/UniversalImage.tsx @@ -27,8 +27,7 @@ export function UniversalImage({ const [errored, setErrored] = useState(false) const hasWidthAndHeight = computedSize.width !== undefined && computedSize.height !== undefined - const hasHeightAndRatio = - computedSize.height !== undefined && computedSize.aspectRatio !== undefined + const hasHeightAndRatio = computedSize.height !== undefined && computedSize.aspectRatio !== undefined const sizeKnown = hasWidthAndHeight || hasHeightAndRatio // Propagate prop updates to state @@ -51,7 +50,7 @@ export function UniversalImage({ setWidth(calculatedWidth) setHeight(calculatedHeight) }, - () => setErrored(true) + () => setErrored(true), ) }, [width, height, sizeKnown, uri, fastImage]) @@ -104,7 +103,8 @@ export function UniversalImage({ height={size.height} overflow="hidden" testID={testID ? `svg-${testID}` : undefined} - width={size.width}> + width={size.width} + > ) diff --git a/packages/ui/src/components/UniversalImage/internal/FastImageWrapper.native.tsx b/packages/ui/src/components/UniversalImage/internal/FastImageWrapper.native.tsx index ce09c5fa0dc..f3ea8b8db43 100644 --- a/packages/ui/src/components/UniversalImage/internal/FastImageWrapper.native.tsx +++ b/packages/ui/src/components/UniversalImage/internal/FastImageWrapper.native.tsx @@ -15,8 +15,7 @@ export function FastImageWrapper({ const isLoaded = useSharedValue(false) const aspectRatio = - size.aspectRatio ?? - (size.width !== undefined && size.height !== undefined ? size.width / size.height : undefined) + size.aspectRatio ?? (size.width !== undefined && size.height !== undefined ? size.width / size.height : undefined) // Ensure that the image is displayed together with styles applied // to the container only after it has been loaded (e.g. to prevent @@ -49,11 +48,7 @@ export function FastImageWrapper({ uri, cache: FastImage.cacheControl.immutable, }} - style={[ - styles.image, - [styles.fullWidth, { maxHeight: size.height ?? '100%' }, style], - { aspectRatio }, - ]} + style={[styles.image, [styles.fullWidth, { maxHeight: size.height ?? '100%' }, style], { aspectRatio }]} onError={setError} onLoad={(): void => { isLoaded.value = true diff --git a/packages/ui/src/components/UniversalImage/internal/FastImageWrapper.tsx b/packages/ui/src/components/UniversalImage/internal/FastImageWrapper.tsx index 9457cf0123b..e5cb4d79561 100644 --- a/packages/ui/src/components/UniversalImage/internal/FastImageWrapper.tsx +++ b/packages/ui/src/components/UniversalImage/internal/FastImageWrapper.tsx @@ -2,9 +2,6 @@ import { PlainImage } from 'ui/src/components/UniversalImage/internal/PlainImage import { FastImageWrapperProps } from 'ui/src/components/UniversalImage/types' // For web, fall back to plain image -export function FastImageWrapper({ - setError: _, - ...rest -}: FastImageWrapperProps): JSX.Element | null { +export function FastImageWrapper({ setError: _, ...rest }: FastImageWrapperProps): JSX.Element | null { return } diff --git a/packages/ui/src/components/UniversalImage/internal/PlainImage.native.tsx b/packages/ui/src/components/UniversalImage/internal/PlainImage.native.tsx index f5fbbf6b857..37baae28de2 100644 --- a/packages/ui/src/components/UniversalImage/internal/PlainImage.native.tsx +++ b/packages/ui/src/components/UniversalImage/internal/PlainImage.native.tsx @@ -2,14 +2,7 @@ import { useState } from 'react' import { Image } from 'react-native' import { PlainImageProps } from 'ui/src/components/UniversalImage/types' -export function PlainImage({ - uri, - size, - fallback, - resizeMode, - style, - testID, -}: PlainImageProps): JSX.Element { +export function PlainImage({ uri, size, fallback, resizeMode, style, testID }: PlainImageProps): JSX.Element { const [hasError, setHasError] = useState(false) if (hasError && fallback) { diff --git a/packages/ui/src/components/UniversalImage/internal/PlainImage.tsx b/packages/ui/src/components/UniversalImage/internal/PlainImage.tsx index f3a80357ddc..741b840fbf5 100644 --- a/packages/ui/src/components/UniversalImage/internal/PlainImage.tsx +++ b/packages/ui/src/components/UniversalImage/internal/PlainImage.tsx @@ -1,16 +1,9 @@ import { useState } from 'react' import { PlainImageProps, UniversalImageResizeMode } from 'ui/src/components/UniversalImage/types' import { Flex } from 'ui/src/components/layout/Flex' -import { isJestRun } from 'utilities/src/environment' +import { isTestEnv } from 'utilities/src/environment' -export function PlainImage({ - uri, - size, - fallback, - resizeMode, - style, - testID, -}: PlainImageProps): JSX.Element { +export function PlainImage({ uri, size, fallback, resizeMode, style, testID }: PlainImageProps): JSX.Element { const [hasError, setHasError] = useState(false) // TODO cover all cases better @@ -36,7 +29,7 @@ export function PlainImage({ } // TODO(MOB-3485): remove test run special casing - if (isJestRun) { + if (isTestEnv()) { return {imgElement} } else { return imgElement diff --git a/packages/ui/src/components/UniversalImage/utils.test.tsx b/packages/ui/src/components/UniversalImage/utils.test.tsx index 3a75a7dbdc9..54d1330beb2 100644 --- a/packages/ui/src/components/UniversalImage/utils.test.tsx +++ b/packages/ui/src/components/UniversalImage/utils.test.tsx @@ -1,17 +1,16 @@ -import { fetchSVG } from "ui/src/components/UniversalImage/utils" +import { fetchSVG } from 'ui/src/components/UniversalImage/utils' const REGULAR_SVG = '' const SVG_WITH_ANIMATES = 'hello' -const SVG_WITH_ANIMATES_STRIPPED = - 'hello' +const SVG_WITH_ANIMATES_STRIPPED = 'hello' describe(fetchSVG, () => { it('fetches SVGs', async () => { globalThis.fetch = jest.fn(() => Promise.resolve({ text: () => Promise.resolve(REGULAR_SVG), - }) + }), ) as jest.Mock const result = await fetchSVG('regular.svg', false) @@ -24,7 +23,7 @@ describe(fetchSVG, () => { globalThis.fetch = jest.fn(() => Promise.resolve({ text: () => Promise.resolve(SVG_WITH_ANIMATES), - }) + }), ) as jest.Mock const result = await fetchSVG('with-animate.svg', false) diff --git a/packages/ui/src/components/UniversalImage/utils.ts b/packages/ui/src/components/UniversalImage/utils.ts index 34c3958ef78..e1b73f85556 100644 --- a/packages/ui/src/components/UniversalImage/utils.ts +++ b/packages/ui/src/components/UniversalImage/utils.ts @@ -11,11 +11,7 @@ export type SvgData = { aspectRatio: number } -export async function fetchSVG( - uri: string, - autoplay: boolean, - signal?: AbortSignal -): Promise { +export async function fetchSVG(uri: string, autoplay: boolean, signal?: AbortSignal): Promise { const res = await fetch(uri, { signal }) const text = await res.text() @@ -32,8 +28,7 @@ export async function fetchSVG( let aspectRatio = FALLBACK_ASPECT_RATIO try { - aspectRatio = - viewboxHeight && viewboxWidth ? +viewboxWidth / +viewboxHeight : FALLBACK_ASPECT_RATIO + aspectRatio = viewboxHeight && viewboxWidth ? +viewboxWidth / +viewboxHeight : FALLBACK_ASPECT_RATIO } catch (e) { logger.debug('images/utils', 'fetchSVG', 'Could not calculate aspect ratio ' + e) } diff --git a/packages/ui/src/components/button/Button.tsx b/packages/ui/src/components/button/Button.tsx index 20bdaad56c8..b0156428908 100644 --- a/packages/ui/src/components/button/Button.tsx +++ b/packages/ui/src/components/button/Button.tsx @@ -244,7 +244,7 @@ function useButton(propsIn: Props) { size, disabled: propsActive.disabled, maxFontSizeMultiplier: 1.2, - } + }, ) const inner = spacedChildren({ @@ -260,10 +260,10 @@ function useButton(propsIn: Props) { const tag = isNested ? 'span' : // defaults to when accessibilityRole = link - // see https://github.com/tamagui/tamagui/issues/505 - propsIn.accessibilityRole === 'link' - ? 'a' - : undefined + // see https://github.com/tamagui/tamagui/issues/505 + propsIn.accessibilityRole === 'link' + ? 'a' + : undefined const props = { ...(propsActive.disabled && { diff --git a/packages/ui/src/components/factories/animated.tsx b/packages/ui/src/components/factories/animated.tsx index b4430a4d24c..858ca28b5d1 100644 --- a/packages/ui/src/components/factories/animated.tsx +++ b/packages/ui/src/components/factories/animated.tsx @@ -2,7 +2,7 @@ import React, { ComponentClass } from 'react' import Animated, { AnimateProps } from 'react-native-reanimated' export function withAnimated( - WrappedComponent: React.ComponentType + WrappedComponent: React.ComponentType, ): ComponentClass> { const displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component' diff --git a/packages/ui/src/components/factories/createIcon.tsx b/packages/ui/src/components/factories/createIcon.tsx index efb23e57063..0a7e4a380ff 100644 --- a/packages/ui/src/components/factories/createIcon.tsx +++ b/packages/ui/src/components/factories/createIcon.tsx @@ -1,15 +1,7 @@ import type { IconProps as TamaguiIconProps } from '@tamagui/helpers-icon' import { createElement, forwardRef } from 'react' import { Svg, SvgProps } from 'react-native-svg' -import { - ColorTokens, - SpecificTokens, - Stack, - ThemeKeys, - isWeb, - styled, - usePropsAndStyle, -} from 'tamagui' +import { ColorTokens, SpecificTokens, Stack, ThemeKeys, isWeb, styled, usePropsAndStyle } from 'tamagui' import { withAnimated } from 'ui/src/components/factories/animated' import { DynamicColor } from 'ui/src/hooks/useSporeColors' import { IconSizeTokens } from 'ui/src/theme' @@ -60,7 +52,7 @@ export function createIcon({ { resolveValues: 'value', forComponent: IconFrame, - } + }, ) const svgProps: SvgPropsWithRef = { diff --git a/packages/ui/src/components/icons/AaveIcon.tsx b/packages/ui/src/components/icons/AaveIcon.tsx index 6549311504a..9ec5863f7f2 100644 --- a/packages/ui/src/components/icons/AaveIcon.tsx +++ b/packages/ui/src/components/icons/AaveIcon.tsx @@ -6,20 +6,21 @@ import { createIcon } from '../factories/createIcon' export const [AaveIcon, AnimatedAaveIcon] = createIcon({ name: 'AaveIcon', getIcon: (props) => ( - - <_Circle cx="12" cy="12" fill="url(#paint0_linear_12481_2214)" r="12" /> + + <_Circle cx="12" cy="12" r="12" fill="url(#paint0_linear_12481_2214)" /> + x2="-4.42097" + y2="10.5677" + gradientUnits="userSpaceOnUse" + > diff --git a/packages/ui/src/components/icons/AddButton.tsx b/packages/ui/src/components/icons/AddButton.tsx index 46e5401dbb8..1cf3cd20253 100644 --- a/packages/ui/src/components/icons/AddButton.tsx +++ b/packages/ui/src/components/icons/AddButton.tsx @@ -6,22 +6,10 @@ import { createIcon } from '../factories/createIcon' export const [AddButton, AnimatedAddButton] = createIcon({ name: 'AddButton', getIcon: (props) => ( - - - - + + + + ), }) diff --git a/packages/ui/src/components/icons/AlertCircle.tsx b/packages/ui/src/components/icons/AlertCircle.tsx index 69f25aaff55..8aa2d5586c1 100644 --- a/packages/ui/src/components/icons/AlertCircle.tsx +++ b/packages/ui/src/components/icons/AlertCircle.tsx @@ -10,24 +10,12 @@ export const [AlertCircle, AnimatedAlertCircle] = createIcon({ - - + + ), }) diff --git a/packages/ui/src/components/icons/AlertTriangle.tsx b/packages/ui/src/components/icons/AlertTriangle.tsx index 77a2ea7de6b..1ad98135037 100644 --- a/packages/ui/src/components/icons/AlertTriangle.tsx +++ b/packages/ui/src/components/icons/AlertTriangle.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [AlertTriangle, AnimatedAlertTriangle] = createIcon({ name: 'AlertTriangle', getIcon: (props) => ( - + ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/ApproveFilled.tsx b/packages/ui/src/components/icons/ApproveFilled.tsx index d2d23e828a6..e7ff0f4ec18 100644 --- a/packages/ui/src/components/icons/ApproveFilled.tsx +++ b/packages/ui/src/components/icons/ApproveFilled.tsx @@ -8,8 +8,8 @@ export const [ApproveFilled, AnimatedApproveFilled] = createIcon({ getIcon: (props) => ( ), diff --git a/packages/ui/src/components/icons/ArrowChange.tsx b/packages/ui/src/components/icons/ArrowChange.tsx index d03bb87c040..a51e064b1c7 100644 --- a/packages/ui/src/components/icons/ArrowChange.tsx +++ b/packages/ui/src/components/icons/ArrowChange.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [ArrowChange, AnimatedArrowChange] = createIcon({ name: 'ArrowChange', getIcon: (props) => ( - + ( - - - + + + ), }) diff --git a/packages/ui/src/components/icons/ArrowDownCircle.tsx b/packages/ui/src/components/icons/ArrowDownCircle.tsx index bb6a4ddbec0..e202935f348 100644 --- a/packages/ui/src/components/icons/ArrowDownCircle.tsx +++ b/packages/ui/src/components/icons/ArrowDownCircle.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [ArrowDownCircle, AnimatedArrowDownCircle] = createIcon({ name: 'ArrowDownCircle', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/ArrowDownCircleFilled.tsx b/packages/ui/src/components/icons/ArrowDownCircleFilled.tsx index e15399365f5..3b010b1e311 100644 --- a/packages/ui/src/components/icons/ArrowDownCircleFilled.tsx +++ b/packages/ui/src/components/icons/ArrowDownCircleFilled.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [ArrowDownCircleFilled, AnimatedArrowDownCircleFilled] = createIcon({ name: 'ArrowDownCircleFilled', getIcon: (props) => ( - + ( - + ), diff --git a/packages/ui/src/components/icons/ArrowRightwardsDown.tsx b/packages/ui/src/components/icons/ArrowRightwardsDown.tsx index 6836a2be6b0..cc1a8dfdb62 100644 --- a/packages/ui/src/components/icons/ArrowRightwardsDown.tsx +++ b/packages/ui/src/components/icons/ArrowRightwardsDown.tsx @@ -6,20 +6,20 @@ import { createIcon } from '../factories/createIcon' export const [ArrowRightwardsDown, AnimatedArrowRightwardsDown] = createIcon({ name: 'ArrowRightwardsDown', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/ArrowTurnDownRight.tsx b/packages/ui/src/components/icons/ArrowTurnDownRight.tsx index acaa948929b..6a95221f0a4 100644 --- a/packages/ui/src/components/icons/ArrowTurnDownRight.tsx +++ b/packages/ui/src/components/icons/ArrowTurnDownRight.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [ArrowTurnDownRight, AnimatedArrowTurnDownRight] = createIcon({ name: 'ArrowTurnDownRight', getIcon: (props) => ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/BackArrow.tsx b/packages/ui/src/components/icons/BackArrow.tsx index ad39f7ae5e8..f7c340a8424 100644 --- a/packages/ui/src/components/icons/BackArrow.tsx +++ b/packages/ui/src/components/icons/BackArrow.tsx @@ -3,9 +3,5 @@ import { IconProps } from 'ui/src/components/factories/createIcon' import { LeftArrow, RightArrow } from 'ui/src/components/icons' export function BackArrow(props: IconProps): JSX.Element { - return I18nManager.isRTL ? ( - - ) : ( - - ) + return I18nManager.isRTL ? : } diff --git a/packages/ui/src/components/icons/Bell.tsx b/packages/ui/src/components/icons/Bell.tsx index 57ec44394be..870bcb29e8e 100644 --- a/packages/ui/src/components/icons/Bell.tsx +++ b/packages/ui/src/components/icons/Bell.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Bell, AnimatedBell] = createIcon({ name: 'Bell', getIcon: (props) => ( - + ( - + ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/CameraScanAlt.tsx b/packages/ui/src/components/icons/CameraScanAlt.tsx index fba19e20896..603a9b67fe2 100644 --- a/packages/ui/src/components/icons/CameraScanAlt.tsx +++ b/packages/ui/src/components/icons/CameraScanAlt.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [CameraScanAlt, AnimatedCameraScanAlt] = createIcon({ name: 'CameraScanAlt', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/Chart.tsx b/packages/ui/src/components/icons/Chart.tsx index 18fdd69f12a..9d5714a04d0 100644 --- a/packages/ui/src/components/icons/Chart.tsx +++ b/packages/ui/src/components/icons/Chart.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Chart, AnimatedChart] = createIcon({ name: 'Chart', getIcon: (props) => ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/CheckCircle.tsx b/packages/ui/src/components/icons/CheckCircle.tsx index f461ec2bd01..891e3f7531d 100644 --- a/packages/ui/src/components/icons/CheckCircle.tsx +++ b/packages/ui/src/components/icons/CheckCircle.tsx @@ -6,20 +6,20 @@ import { createIcon } from '../factories/createIcon' export const [CheckCircle, AnimatedCheckCircle] = createIcon({ name: 'CheckCircle', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/Checkmark.tsx b/packages/ui/src/components/icons/Checkmark.tsx index e840792a557..40f88220f36 100644 --- a/packages/ui/src/components/icons/Checkmark.tsx +++ b/packages/ui/src/components/icons/Checkmark.tsx @@ -7,12 +7,7 @@ export const [Checkmark, AnimatedCheckmark] = createIcon({ name: 'Checkmark', getIcon: (props) => ( - + ), }) diff --git a/packages/ui/src/components/icons/CheckmarkCircle.tsx b/packages/ui/src/components/icons/CheckmarkCircle.tsx index 92862776426..3cc6c0fcebd 100644 --- a/packages/ui/src/components/icons/CheckmarkCircle.tsx +++ b/packages/ui/src/components/icons/CheckmarkCircle.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [CheckmarkCircle, AnimatedCheckmarkCircle] = createIcon({ name: 'CheckmarkCircle', getIcon: (props) => ( - + ( - - + + ), }) diff --git a/packages/ui/src/components/icons/ChevronLeft.tsx b/packages/ui/src/components/icons/ChevronLeft.tsx index de9365dce86..9a627ac8673 100644 --- a/packages/ui/src/components/icons/ChevronLeft.tsx +++ b/packages/ui/src/components/icons/ChevronLeft.tsx @@ -7,13 +7,7 @@ export const [ChevronLeft, AnimatedChevronLeft] = createIcon({ name: 'ChevronLeft', getIcon: (props) => ( - + ), }) diff --git a/packages/ui/src/components/icons/CircleSpinner.tsx b/packages/ui/src/components/icons/CircleSpinner.tsx index d0d53643679..ab311d7be57 100644 --- a/packages/ui/src/components/icons/CircleSpinner.tsx +++ b/packages/ui/src/components/icons/CircleSpinner.tsx @@ -6,13 +6,8 @@ import { createIcon } from '../factories/createIcon' export const [CircleSpinner, AnimatedCircleSpinner] = createIcon({ name: 'CircleSpinner', getIcon: (props) => ( - - + + ), }) diff --git a/packages/ui/src/components/icons/Clipboard.tsx b/packages/ui/src/components/icons/Clipboard.tsx index d60fb5a6823..e08a8af6ad3 100644 --- a/packages/ui/src/components/icons/Clipboard.tsx +++ b/packages/ui/src/components/icons/Clipboard.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Clipboard, AnimatedClipboard] = createIcon({ name: 'Clipboard', getIcon: (props) => ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/Cloud.tsx b/packages/ui/src/components/icons/Cloud.tsx index d9fe0a788d3..29b0a4c2bdf 100644 --- a/packages/ui/src/components/icons/Cloud.tsx +++ b/packages/ui/src/components/icons/Cloud.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Cloud, AnimatedCloud] = createIcon({ name: 'Cloud', getIcon: (props) => ( - + ( - + ( - + - - - + + + ), }) diff --git a/packages/ui/src/components/icons/Coin.tsx b/packages/ui/src/components/icons/Coin.tsx index 70997440aec..5d24f668f76 100644 --- a/packages/ui/src/components/icons/Coin.tsx +++ b/packages/ui/src/components/icons/Coin.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Coin, AnimatedCoin] = createIcon({ name: 'Coin', getIcon: (props) => ( - + ( - + ( - + ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/CopySheets.tsx b/packages/ui/src/components/icons/CopySheets.tsx index a2f4492b353..ff8380489b2 100644 --- a/packages/ui/src/components/icons/CopySheets.tsx +++ b/packages/ui/src/components/icons/CopySheets.tsx @@ -6,9 +6,9 @@ import { createIcon } from '../factories/createIcon' export const [CopySheets, AnimatedCopySheets] = createIcon({ name: 'CopySheets', getIcon: (props) => ( - + diff --git a/packages/ui/src/components/icons/DaiIcon.tsx b/packages/ui/src/components/icons/DaiIcon.tsx index 8e06b1464c0..e21ad854229 100644 --- a/packages/ui/src/components/icons/DaiIcon.tsx +++ b/packages/ui/src/components/icons/DaiIcon.tsx @@ -6,16 +6,16 @@ import { createIcon } from '../factories/createIcon' export const [DaiIcon, AnimatedDaiIcon] = createIcon({ name: 'DaiIcon', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/Dash.tsx b/packages/ui/src/components/icons/Dash.tsx index b1c34709efa..7b2c9a11f54 100644 --- a/packages/ui/src/components/icons/Dash.tsx +++ b/packages/ui/src/components/icons/Dash.tsx @@ -7,7 +7,7 @@ export const [Dash, AnimatedDash] = createIcon({ name: 'Dash', getIcon: (props) => ( - + ), }) diff --git a/packages/ui/src/components/icons/DiamondExclamation.tsx b/packages/ui/src/components/icons/DiamondExclamation.tsx index 6aa72a7ba19..86474c6e30b 100644 --- a/packages/ui/src/components/icons/DiamondExclamation.tsx +++ b/packages/ui/src/components/icons/DiamondExclamation.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [DiamondExclamation, AnimatedDiamondExclamation] = createIcon({ name: 'DiamondExclamation', getIcon: (props) => ( - + ( - + ( - + ( - - - + + + ), }) diff --git a/packages/ui/src/components/icons/Edit.tsx b/packages/ui/src/components/icons/Edit.tsx index 277353620e3..9bfee184eb1 100644 --- a/packages/ui/src/components/icons/Edit.tsx +++ b/packages/ui/src/components/icons/Edit.tsx @@ -6,9 +6,9 @@ import { createIcon } from '../factories/createIcon' export const [Edit, AnimatedEdit] = createIcon({ name: 'Edit', getIcon: (props) => ( - + diff --git a/packages/ui/src/components/icons/Ellipsis.tsx b/packages/ui/src/components/icons/Ellipsis.tsx index e3d933f9780..34fbbec0deb 100644 --- a/packages/ui/src/components/icons/Ellipsis.tsx +++ b/packages/ui/src/components/icons/Ellipsis.tsx @@ -6,27 +6,27 @@ import { createIcon } from '../factories/createIcon' export const [Ellipsis, AnimatedEllipsis] = createIcon({ name: 'Ellipsis', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/EmptySpinner.tsx b/packages/ui/src/components/icons/EmptySpinner.tsx index a1855027cdd..b0a49391c35 100644 --- a/packages/ui/src/components/icons/EmptySpinner.tsx +++ b/packages/ui/src/components/icons/EmptySpinner.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [EmptySpinner, AnimatedEmptySpinner] = createIcon({ name: 'EmptySpinner', getIcon: (props) => ( - + <_Circle cx="10" cy="10" r="8" stroke="currentColor" strokeOpacity="0.24" strokeWidth="3" /> ), diff --git a/packages/ui/src/components/icons/EmptyStateCoin.tsx b/packages/ui/src/components/icons/EmptyStateCoin.tsx index 693cc4cac1b..1474a2e5f98 100644 --- a/packages/ui/src/components/icons/EmptyStateCoin.tsx +++ b/packages/ui/src/components/icons/EmptyStateCoin.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [EmptyStateCoin, AnimatedEmptyStateCoin] = createIcon({ name: 'EmptyStateCoin', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/EmptyStatePicture.tsx b/packages/ui/src/components/icons/EmptyStatePicture.tsx index 5b34aeab2de..0db8f9972bc 100644 --- a/packages/ui/src/components/icons/EmptyStatePicture.tsx +++ b/packages/ui/src/components/icons/EmptyStatePicture.tsx @@ -6,23 +6,15 @@ import { createIcon } from '../factories/createIcon' export const [EmptyStatePicture, AnimatedEmptyStatePicture] = createIcon({ name: 'EmptyStatePicture', getIcon: (props) => ( - - + + - <_Circle cx="11.856" cy="27.716" fill="currentColor" r="5.298" /> + <_Circle cx="11.856" cy="27.716" r="5.298" fill="currentColor" /> ( - + - + ), diff --git a/packages/ui/src/components/icons/EmptyStateTransaction.tsx b/packages/ui/src/components/icons/EmptyStateTransaction.tsx index c33a3e833b0..80d9194cc36 100644 --- a/packages/ui/src/components/icons/EmptyStateTransaction.tsx +++ b/packages/ui/src/components/icons/EmptyStateTransaction.tsx @@ -6,22 +6,22 @@ import { createIcon } from '../factories/createIcon' export const [EmptyStateTransaction, AnimatedEmptyStateTransaction] = createIcon({ name: 'EmptyStateTransaction', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/ErrorLoading.tsx b/packages/ui/src/components/icons/ErrorLoading.tsx index 316b0647c8a..296ae4946a6 100644 --- a/packages/ui/src/components/icons/ErrorLoading.tsx +++ b/packages/ui/src/components/icons/ErrorLoading.tsx @@ -6,28 +6,16 @@ import { createIcon } from '../factories/createIcon' export const [ErrorLoading, AnimatedErrorLoading] = createIcon({ name: 'ErrorLoading', getIcon: (props) => ( - + - - + + ), }) diff --git a/packages/ui/src/components/icons/EthIcon.tsx b/packages/ui/src/components/icons/EthIcon.tsx index f3ab37825ee..b9c3c3bc6fa 100644 --- a/packages/ui/src/components/icons/EthIcon.tsx +++ b/packages/ui/src/components/icons/EthIcon.tsx @@ -6,19 +6,16 @@ import { createIcon } from '../factories/createIcon' export const [EthIcon, AnimatedEthIcon] = createIcon({ name: 'EthIcon', getIcon: (props) => ( - - + + - - + + diff --git a/packages/ui/src/components/icons/EthLightIcon.tsx b/packages/ui/src/components/icons/EthLightIcon.tsx index d2f047ba554..889d070fa14 100644 --- a/packages/ui/src/components/icons/EthLightIcon.tsx +++ b/packages/ui/src/components/icons/EthLightIcon.tsx @@ -6,32 +6,20 @@ import { createIcon } from '../factories/createIcon' export const [EthLightIcon, AnimatedEthLightIcon] = createIcon({ name: 'EthLightIcon', getIcon: (props) => ( - - <_Circle cx="12" cy="12" fill={'currentColor' ?? '#EDF0F4'} r="12" /> + + <_Circle cx="12" cy="12" r="12" fill={'currentColor' ?? '#EDF0F4'} /> - + - - - + + + ), defaultFill: '#EDF0F4', diff --git a/packages/ui/src/components/icons/ExternalLink.tsx b/packages/ui/src/components/icons/ExternalLink.tsx index a70d5504ccd..d1b21648011 100644 --- a/packages/ui/src/components/icons/ExternalLink.tsx +++ b/packages/ui/src/components/icons/ExternalLink.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [ExternalLink, AnimatedExternalLink] = createIcon({ name: 'ExternalLink', getIcon: (props) => ( - + ( - + ( - + ( - + + x2="15.3182" + y2="19.8227" + gradientUnits="userSpaceOnUse" + > diff --git a/packages/ui/src/components/icons/Faceid.tsx b/packages/ui/src/components/icons/Faceid.tsx index d7d1f70ab7d..1316020c743 100644 --- a/packages/ui/src/components/icons/Faceid.tsx +++ b/packages/ui/src/components/icons/Faceid.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Faceid, AnimatedFaceid] = createIcon({ name: 'Faceid', getIcon: (props) => ( - + ( - + ( - + ( - + diff --git a/packages/ui/src/components/icons/FileListLock.tsx b/packages/ui/src/components/icons/FileListLock.tsx index d7c3e90b994..271ec3d6809 100644 --- a/packages/ui/src/components/icons/FileListLock.tsx +++ b/packages/ui/src/components/icons/FileListLock.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [FileListLock, AnimatedFileListLock] = createIcon({ name: 'FileListLock', getIcon: (props) => ( - + ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/Global.tsx b/packages/ui/src/components/icons/Global.tsx index 252ddfe19f9..b8f1755bc38 100644 --- a/packages/ui/src/components/icons/Global.tsx +++ b/packages/ui/src/components/icons/Global.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Global, AnimatedGlobal] = createIcon({ name: 'Global', getIcon: (props) => ( - + ( - + ), diff --git a/packages/ui/src/components/icons/GlobeFilled.tsx b/packages/ui/src/components/icons/GlobeFilled.tsx index 7c4aac4e43c..50a444a60cd 100644 --- a/packages/ui/src/components/icons/GlobeFilled.tsx +++ b/packages/ui/src/components/icons/GlobeFilled.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [GlobeFilled, AnimatedGlobeFilled] = createIcon({ name: 'GlobeFilled', getIcon: (props) => ( - + ( - + ( - + ( - - - - + + + + ), }) diff --git a/packages/ui/src/components/icons/Heart.tsx b/packages/ui/src/components/icons/Heart.tsx index f05698cde53..947ea7d618f 100644 --- a/packages/ui/src/components/icons/Heart.tsx +++ b/packages/ui/src/components/icons/Heart.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Heart, AnimatedHeart] = createIcon({ name: 'Heart', getIcon: (props) => ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/InfoCircle.tsx b/packages/ui/src/components/icons/InfoCircle.tsx index 737658aec0a..5231ea7423e 100644 --- a/packages/ui/src/components/icons/InfoCircle.tsx +++ b/packages/ui/src/components/icons/InfoCircle.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [InfoCircle, AnimatedInfoCircle] = createIcon({ name: 'InfoCircle', getIcon: (props) => ( - + ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/Language.tsx b/packages/ui/src/components/icons/Language.tsx index 2a49ca1a97e..93c750236be 100644 --- a/packages/ui/src/components/icons/Language.tsx +++ b/packages/ui/src/components/icons/Language.tsx @@ -6,13 +6,13 @@ import { createIcon } from '../factories/createIcon' export const [Language, AnimatedLanguage] = createIcon({ name: 'Language', getIcon: (props) => ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/Lightning.tsx b/packages/ui/src/components/icons/Lightning.tsx index b84a347aaf0..21549f885dd 100644 --- a/packages/ui/src/components/icons/Lightning.tsx +++ b/packages/ui/src/components/icons/Lightning.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Lightning, AnimatedLightning] = createIcon({ name: 'Lightning', getIcon: (props) => ( - + ( - + ( - + ( - + diff --git a/packages/ui/src/components/icons/LinkHorizontalAlt.tsx b/packages/ui/src/components/icons/LinkHorizontalAlt.tsx index 82ed08dfbc9..3b6dc77168f 100644 --- a/packages/ui/src/components/icons/LinkHorizontalAlt.tsx +++ b/packages/ui/src/components/icons/LinkHorizontalAlt.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [LinkHorizontalAlt, AnimatedLinkHorizontalAlt] = createIcon({ name: 'LinkHorizontalAlt', getIcon: (props) => ( - + ( - - - - - - - + + + + + + + ), }) diff --git a/packages/ui/src/components/icons/LoadingSpinnerInner.tsx b/packages/ui/src/components/icons/LoadingSpinnerInner.tsx index 5c49894f282..a220ce80c86 100644 --- a/packages/ui/src/components/icons/LoadingSpinnerInner.tsx +++ b/packages/ui/src/components/icons/LoadingSpinnerInner.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [LoadingSpinnerInner, AnimatedLoadingSpinnerInner] = createIcon({ name: 'LoadingSpinnerInner', getIcon: (props) => ( - + - <_Circle cx="39.9999" cy="74.7858" fill="currentColor" r="5.71429" /> + <_Circle cx="39.9999" cy="74.7858" r="5.71429" fill="currentColor" /> ), }) diff --git a/packages/ui/src/components/icons/LoadingSpinnerOuter.tsx b/packages/ui/src/components/icons/LoadingSpinnerOuter.tsx index 860a525e0ea..6cde359a12d 100644 --- a/packages/ui/src/components/icons/LoadingSpinnerOuter.tsx +++ b/packages/ui/src/components/icons/LoadingSpinnerOuter.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [LoadingSpinnerOuter, AnimatedLoadingSpinnerOuter] = createIcon({ name: 'LoadingSpinnerOuter', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/Lock.tsx b/packages/ui/src/components/icons/Lock.tsx index 1e855529fd5..676748e1ca4 100644 --- a/packages/ui/src/components/icons/Lock.tsx +++ b/packages/ui/src/components/icons/Lock.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Lock, AnimatedLock] = createIcon({ name: 'Lock', getIcon: (props) => ( - + ( - + diff --git a/packages/ui/src/components/icons/Masonry.tsx b/packages/ui/src/components/icons/Masonry.tsx index 8d9908ad4e5..39da4c65239 100644 --- a/packages/ui/src/components/icons/Masonry.tsx +++ b/packages/ui/src/components/icons/Masonry.tsx @@ -6,35 +6,17 @@ import { createIcon } from '../factories/createIcon' export const [Masonry, AnimatedMasonry] = createIcon({ name: 'Masonry', getIcon: (props) => ( - - - + + + - + ), }) diff --git a/packages/ui/src/components/icons/MessageQuestion.tsx b/packages/ui/src/components/icons/MessageQuestion.tsx index fe974ed1efb..4dce873d8a7 100644 --- a/packages/ui/src/components/icons/MessageQuestion.tsx +++ b/packages/ui/src/components/icons/MessageQuestion.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [MessageQuestion, AnimatedMessageQuestion] = createIcon({ name: 'MessageQuestion', getIcon: (props) => ( - + ( - + diff --git a/packages/ui/src/components/icons/Mobile.tsx b/packages/ui/src/components/icons/Mobile.tsx index 33443a74f4d..d3d7960c750 100644 --- a/packages/ui/src/components/icons/Mobile.tsx +++ b/packages/ui/src/components/icons/Mobile.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Mobile, AnimatedMobile] = createIcon({ name: 'Mobile', getIcon: (props) => ( - + ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/NoPools.tsx b/packages/ui/src/components/icons/NoPools.tsx index 81a73cfb707..a721af2ec56 100644 --- a/packages/ui/src/components/icons/NoPools.tsx +++ b/packages/ui/src/components/icons/NoPools.tsx @@ -6,19 +6,19 @@ import { createIcon } from '../factories/createIcon' export const [NoPools, AnimatedNoPools] = createIcon({ name: 'NoPools', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/NoTokens.tsx b/packages/ui/src/components/icons/NoTokens.tsx index 0949e68eaef..76f840b755b 100644 --- a/packages/ui/src/components/icons/NoTokens.tsx +++ b/packages/ui/src/components/icons/NoTokens.tsx @@ -6,18 +6,18 @@ import { createIcon } from '../factories/createIcon' export const [NoTokens, AnimatedNoTokens] = createIcon({ name: 'NoTokens', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/NoTransactions.tsx b/packages/ui/src/components/icons/NoTransactions.tsx index f77fba75246..008bd4a2ce1 100644 --- a/packages/ui/src/components/icons/NoTransactions.tsx +++ b/packages/ui/src/components/icons/NoTransactions.tsx @@ -6,24 +6,24 @@ import { createIcon } from '../factories/createIcon' export const [NoTransactions, AnimatedNoTransactions] = createIcon({ name: 'NoTransactions', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/PaperStack.tsx b/packages/ui/src/components/icons/PaperStack.tsx index 455d7b4873e..f769c36d174 100644 --- a/packages/ui/src/components/icons/PaperStack.tsx +++ b/packages/ui/src/components/icons/PaperStack.tsx @@ -6,17 +6,17 @@ import { createIcon } from '../factories/createIcon' export const [PaperStack, AnimatedPaperStack] = createIcon({ name: 'PaperStack', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/PapersText.tsx b/packages/ui/src/components/icons/PapersText.tsx index e02fedd855a..3ca9795446a 100644 --- a/packages/ui/src/components/icons/PapersText.tsx +++ b/packages/ui/src/components/icons/PapersText.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [PapersText, AnimatedPapersText] = createIcon({ name: 'PapersText', getIcon: (props) => ( - + ( - + ), diff --git a/packages/ui/src/components/icons/Pen.tsx b/packages/ui/src/components/icons/Pen.tsx index c714fb5fe77..d96db7e46a7 100644 --- a/packages/ui/src/components/icons/Pen.tsx +++ b/packages/ui/src/components/icons/Pen.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [Pen, AnimatedPen] = createIcon({ name: 'Pen', getIcon: (props) => ( - + diff --git a/packages/ui/src/components/icons/PenLine.tsx b/packages/ui/src/components/icons/PenLine.tsx index bddb6074562..67510447306 100644 --- a/packages/ui/src/components/icons/PenLine.tsx +++ b/packages/ui/src/components/icons/PenLine.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [PenLine, AnimatedPenLine] = createIcon({ name: 'PenLine', getIcon: (props) => ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/PencilDetailed.tsx b/packages/ui/src/components/icons/PencilDetailed.tsx index dc9b5ad1a02..567300b7725 100644 --- a/packages/ui/src/components/icons/PencilDetailed.tsx +++ b/packages/ui/src/components/icons/PencilDetailed.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [PencilDetailed, AnimatedPencilDetailed] = createIcon({ name: 'PencilDetailed', getIcon: (props) => ( - + ( - + ( - + ( - + - + diff --git a/packages/ui/src/components/icons/PlusCircle.tsx b/packages/ui/src/components/icons/PlusCircle.tsx index e9bd9c7970c..d69f697d871 100644 --- a/packages/ui/src/components/icons/PlusCircle.tsx +++ b/packages/ui/src/components/icons/PlusCircle.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [PlusCircle, AnimatedPlusCircle] = createIcon({ name: 'PlusCircle', getIcon: (props) => ( - + ( - + ), diff --git a/packages/ui/src/components/icons/Power.tsx b/packages/ui/src/components/icons/Power.tsx index b80a7a84a86..8435789e314 100644 --- a/packages/ui/src/components/icons/Power.tsx +++ b/packages/ui/src/components/icons/Power.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Power, AnimatedPower] = createIcon({ name: 'Power', getIcon: (props) => ( - + ( - + ), diff --git a/packages/ui/src/components/icons/ProfileFilled.tsx b/packages/ui/src/components/icons/ProfileFilled.tsx index 0a5bcc50d76..974f3ebf519 100644 --- a/packages/ui/src/components/icons/ProfileFilled.tsx +++ b/packages/ui/src/components/icons/ProfileFilled.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [ProfileFilled, AnimatedProfileFilled] = createIcon({ name: 'ProfileFilled', getIcon: (props) => ( - + ( - + - + ), diff --git a/packages/ui/src/components/icons/QuestionInCircle.tsx b/packages/ui/src/components/icons/QuestionInCircle.tsx index 7051ea27e4d..fa87ce85976 100644 --- a/packages/ui/src/components/icons/QuestionInCircle.tsx +++ b/packages/ui/src/components/icons/QuestionInCircle.tsx @@ -6,28 +6,22 @@ import { createIcon } from '../factories/createIcon' export const [QuestionInCircle, AnimatedQuestionInCircle] = createIcon({ name: 'QuestionInCircle', getIcon: (props) => ( - + - + ), }) diff --git a/packages/ui/src/components/icons/QuestionInCircleFilled.tsx b/packages/ui/src/components/icons/QuestionInCircleFilled.tsx index 7d4af135b8b..0be0b230261 100644 --- a/packages/ui/src/components/icons/QuestionInCircleFilled.tsx +++ b/packages/ui/src/components/icons/QuestionInCircleFilled.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [QuestionInCircleFilled, AnimatedQuestionInCircleFilled] = createIcon({ name: 'QuestionInCircleFilled', getIcon: (props) => ( - + ( - + ( - + diff --git a/packages/ui/src/components/icons/Receive.tsx b/packages/ui/src/components/icons/Receive.tsx index b346eb60732..cb4a5ff40ef 100644 --- a/packages/ui/src/components/icons/Receive.tsx +++ b/packages/ui/src/components/icons/Receive.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Receive, AnimatedReceive] = createIcon({ name: 'Receive', getIcon: (props) => ( - + ( - + ), diff --git a/packages/ui/src/components/icons/ReceiveArrow.tsx b/packages/ui/src/components/icons/ReceiveArrow.tsx index 92113593635..9283f8ad405 100644 --- a/packages/ui/src/components/icons/ReceiveArrow.tsx +++ b/packages/ui/src/components/icons/ReceiveArrow.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [ReceiveArrow, AnimatedReceiveArrow] = createIcon({ name: 'ReceiveArrow', getIcon: (props) => ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/RotatableChevron.tsx b/packages/ui/src/components/icons/RotatableChevron.tsx index 60726f7d98f..a32f75fd878 100644 --- a/packages/ui/src/components/icons/RotatableChevron.tsx +++ b/packages/ui/src/components/icons/RotatableChevron.tsx @@ -11,13 +11,7 @@ type Props = { color?: ColorTokens } & Omit -function _RotatableChevron({ - color, - width = 24, - height = 24, - direction = 'start', - ...rest -}: Props): JSX.Element { +function _RotatableChevron({ color, width = 24, height = 24, direction = 'start', ...rest }: Props): JSX.Element { let degree: string switch (direction) { case 'start': diff --git a/packages/ui/src/components/icons/Scan.tsx b/packages/ui/src/components/icons/Scan.tsx index 9bfbcc8163f..13be57b4b77 100644 --- a/packages/ui/src/components/icons/Scan.tsx +++ b/packages/ui/src/components/icons/Scan.tsx @@ -6,14 +6,14 @@ import { createIcon } from '../factories/createIcon' export const [Scan, AnimatedScan] = createIcon({ name: 'Scan', getIcon: (props) => ( - - + + diff --git a/packages/ui/src/components/icons/ScanHome.tsx b/packages/ui/src/components/icons/ScanHome.tsx index 227329048fa..262a790235f 100644 --- a/packages/ui/src/components/icons/ScanHome.tsx +++ b/packages/ui/src/components/icons/ScanHome.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [ScanHome, AnimatedScanHome] = createIcon({ name: 'ScanHome', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/ScanQr.tsx b/packages/ui/src/components/icons/ScanQr.tsx index a70327177e1..4462c7f0bdd 100644 --- a/packages/ui/src/components/icons/ScanQr.tsx +++ b/packages/ui/src/components/icons/ScanQr.tsx @@ -6,13 +6,13 @@ import { createIcon } from '../factories/createIcon' export const [ScanQr, AnimatedScanQr] = createIcon({ name: 'ScanQr', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/ScanQrWc.tsx b/packages/ui/src/components/icons/ScanQrWc.tsx index fbe07d9e251..240aeafc5c7 100644 --- a/packages/ui/src/components/icons/ScanQrWc.tsx +++ b/packages/ui/src/components/icons/ScanQrWc.tsx @@ -6,13 +6,13 @@ import { createIcon } from '../factories/createIcon' export const [ScanQrWc, AnimatedScanQrWc] = createIcon({ name: 'ScanQrWc', getIcon: (props) => ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/SearchFocused.tsx b/packages/ui/src/components/icons/SearchFocused.tsx index a8a3ba21b7c..b86f2d6b29e 100644 --- a/packages/ui/src/components/icons/SearchFocused.tsx +++ b/packages/ui/src/components/icons/SearchFocused.tsx @@ -6,20 +6,20 @@ import { createIcon } from '../factories/createIcon' export const [SearchFocused, AnimatedSearchFocused] = createIcon({ name: 'SearchFocused', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/SeedPhraseIcon.tsx b/packages/ui/src/components/icons/SeedPhraseIcon.tsx index e1910bde1c9..8d190284801 100644 --- a/packages/ui/src/components/icons/SeedPhraseIcon.tsx +++ b/packages/ui/src/components/icons/SeedPhraseIcon.tsx @@ -6,70 +6,35 @@ import { createIcon } from '../factories/createIcon' export const [SeedPhraseIcon, AnimatedSeedPhraseIcon] = createIcon({ name: 'SeedPhraseIcon', getIcon: (props) => ( - - - - - - - - - + + + + + + + + + + x2="3.41039" + y2="10.9584" + gradientUnits="userSpaceOnUse" + > + x2="12.9416" + y2="19.4584" + gradientUnits="userSpaceOnUse" + > diff --git a/packages/ui/src/components/icons/SelectIcon.tsx b/packages/ui/src/components/icons/SelectIcon.tsx index 5703e44fa7f..0dac805d489 100644 --- a/packages/ui/src/components/icons/SelectIcon.tsx +++ b/packages/ui/src/components/icons/SelectIcon.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [SelectIcon, AnimatedSelectIcon] = createIcon({ name: 'SelectIcon', getIcon: (props) => ( - + ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/ShieldCheck.tsx b/packages/ui/src/components/icons/ShieldCheck.tsx index 22f988537d2..dc85608d4ee 100644 --- a/packages/ui/src/components/icons/ShieldCheck.tsx +++ b/packages/ui/src/components/icons/ShieldCheck.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [ShieldCheck, AnimatedShieldCheck] = createIcon({ name: 'ShieldCheck', getIcon: (props) => ( - + ( - + ( - + - + diff --git a/packages/ui/src/components/icons/Sort.tsx b/packages/ui/src/components/icons/Sort.tsx index dc6c1cc43a6..c1a68d3aa24 100644 --- a/packages/ui/src/components/icons/Sort.tsx +++ b/packages/ui/src/components/icons/Sort.tsx @@ -6,20 +6,20 @@ import { createIcon } from '../factories/createIcon' export const [Sort, AnimatedSort] = createIcon({ name: 'Sort', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/Sparkle.tsx b/packages/ui/src/components/icons/Sparkle.tsx index 6f4226e39cb..be2d575ebed 100644 --- a/packages/ui/src/components/icons/Sparkle.tsx +++ b/packages/ui/src/components/icons/Sparkle.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Sparkle, AnimatedSparkle] = createIcon({ name: 'Sparkle', getIcon: (props) => ( - + ( - + ), diff --git a/packages/ui/src/components/icons/Star.tsx b/packages/ui/src/components/icons/Star.tsx index c53e5d57162..3e4bee6d19b 100644 --- a/packages/ui/src/components/icons/Star.tsx +++ b/packages/ui/src/components/icons/Star.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Star, AnimatedStar] = createIcon({ name: 'Star', getIcon: (props) => ( - + ( - + + x2="15.9626" + y2="13.0528" + gradientUnits="userSpaceOnUse" + > + x2="5.83257" + y2="14.5802" + gradientUnits="userSpaceOnUse" + > + x2="8.08687" + y2="5.71773" + gradientUnits="userSpaceOnUse" + > diff --git a/packages/ui/src/components/icons/StickyNoteSquare.tsx b/packages/ui/src/components/icons/StickyNoteSquare.tsx index c2cd20896b0..ec2232861cd 100644 --- a/packages/ui/src/components/icons/StickyNoteSquare.tsx +++ b/packages/ui/src/components/icons/StickyNoteSquare.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [StickyNoteSquare, AnimatedStickyNoteSquare] = createIcon({ name: 'StickyNoteSquare', getIcon: (props) => ( - + ( - + ( - + ( - + - + diff --git a/packages/ui/src/components/icons/SwapArrow.tsx b/packages/ui/src/components/icons/SwapArrow.tsx index 72734288037..7c83cf726bc 100644 --- a/packages/ui/src/components/icons/SwapArrow.tsx +++ b/packages/ui/src/components/icons/SwapArrow.tsx @@ -6,20 +6,20 @@ import { createIcon } from '../factories/createIcon' export const [SwapArrow, AnimatedSwapArrow] = createIcon({ name: 'SwapArrow', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/Testnets.tsx b/packages/ui/src/components/icons/Testnets.tsx index 143d16350aa..b81086bbebb 100644 --- a/packages/ui/src/components/icons/Testnets.tsx +++ b/packages/ui/src/components/icons/Testnets.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [Testnets, AnimatedTestnets] = createIcon({ name: 'Testnets', getIcon: (props) => ( - + ( - + ( - + ( - + ( - + ( - + - + ), }) diff --git a/packages/ui/src/components/icons/Trash.tsx b/packages/ui/src/components/icons/Trash.tsx index 719d76acad1..c4cb26f2bb7 100644 --- a/packages/ui/src/components/icons/Trash.tsx +++ b/packages/ui/src/components/icons/Trash.tsx @@ -6,13 +6,13 @@ import { createIcon } from '../factories/createIcon' export const [Trash, AnimatedTrash] = createIcon({ name: 'Trash', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/TrashFilled.tsx b/packages/ui/src/components/icons/TrashFilled.tsx index fd2377a9830..c1aeae49c87 100644 --- a/packages/ui/src/components/icons/TrashFilled.tsx +++ b/packages/ui/src/components/icons/TrashFilled.tsx @@ -6,11 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [TrashFilled, AnimatedTrashFilled] = createIcon({ name: 'TrashFilled', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/TrendDown.tsx b/packages/ui/src/components/icons/TrendDown.tsx index a91e8b90d13..c05b9ba67c6 100644 --- a/packages/ui/src/components/icons/TrendDown.tsx +++ b/packages/ui/src/components/icons/TrendDown.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [TrendDown, AnimatedTrendDown] = createIcon({ name: 'TrendDown', getIcon: (props) => ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/UniswapLogo.tsx b/packages/ui/src/components/icons/UniswapLogo.tsx index 91bbb82bcb3..ce91b3a4698 100644 --- a/packages/ui/src/components/icons/UniswapLogo.tsx +++ b/packages/ui/src/components/icons/UniswapLogo.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [UniswapLogo, AnimatedUniswapLogo] = createIcon({ name: 'UniswapLogo', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/icons/UniswapX.tsx b/packages/ui/src/components/icons/UniswapX.tsx index 9641e9bcc53..9d47298357a 100644 --- a/packages/ui/src/components/icons/UniswapX.tsx +++ b/packages/ui/src/components/icons/UniswapX.tsx @@ -6,15 +6,16 @@ import { createIcon } from '../factories/createIcon' export const [UniswapX, AnimatedUniswapX] = createIcon({ name: 'UniswapX', getIcon: (props) => ( - + + x2="10.6573" + y2="-11.6017" + gradientUnits="userSpaceOnUse" + > diff --git a/packages/ui/src/components/icons/UserSquare.tsx b/packages/ui/src/components/icons/UserSquare.tsx index b49f6ad5a4b..89f3d4321a2 100644 --- a/packages/ui/src/components/icons/UserSquare.tsx +++ b/packages/ui/src/components/icons/UserSquare.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [UserSquare, AnimatedUserSquare] = createIcon({ name: 'UserSquare', getIcon: (props) => ( - + ( - + - + diff --git a/packages/ui/src/components/icons/Wallet.tsx b/packages/ui/src/components/icons/Wallet.tsx index 4147b5f3ce9..4b14b2b0cb2 100644 --- a/packages/ui/src/components/icons/Wallet.tsx +++ b/packages/ui/src/components/icons/Wallet.tsx @@ -6,12 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [Wallet, AnimatedWallet] = createIcon({ name: 'Wallet', getIcon: (props) => ( - + ( - + ( - - - + + + ( - + ( - + ( - + ( - + ), diff --git a/packages/ui/src/components/icons/XTwitter.tsx b/packages/ui/src/components/icons/XTwitter.tsx index 0260b20c958..1e048fab99d 100644 --- a/packages/ui/src/components/icons/XTwitter.tsx +++ b/packages/ui/src/components/icons/XTwitter.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [XTwitter, AnimatedXTwitter] = createIcon({ name: 'XTwitter', getIcon: (props) => ( - + ), diff --git a/packages/ui/src/components/input/CheckBox.tsx b/packages/ui/src/components/input/CheckBox.tsx index 6e945eb1415..b9608a610dd 100644 --- a/packages/ui/src/components/input/CheckBox.tsx +++ b/packages/ui/src/components/input/CheckBox.tsx @@ -31,10 +31,9 @@ export function CheckBox({ text, checked, onCheckPressed }: CheckBoxProps): JSX. justifyContent="center" mt="$spacing4" p="$spacing2" - width={iconSizes.icon20}> - {checked ? ( - - ) : null} + width={iconSizes.icon20} + > + {checked ? : null} {typeof text === 'string' ? ( diff --git a/packages/ui/src/components/layout/Flex.tsx b/packages/ui/src/components/layout/Flex.tsx index ab94400f74b..9341c7a589c 100644 --- a/packages/ui/src/components/layout/Flex.tsx +++ b/packages/ui/src/components/layout/Flex.tsx @@ -1,10 +1,6 @@ import type { Insets } from 'react-native' import { SizeTokens, View, ViewProps, styled } from 'tamagui' -import { - animationsEnter, - animationsEnterExit, - animationsExit, -} from 'ui/src/animations/animationPresets' +import { animationsEnter, animationsEnterExit, animationsExit } from 'ui/src/animations/animationPresets' export const flexStyles = { fill: { flex: 1 }, @@ -40,8 +36,7 @@ export const Flex = styled(View, { flexDirection: 'column', variants: { - inset: (size: SizeOrNumber | Insets) => - size && typeof size === 'object' ? size : getInset(size), + inset: (size: SizeOrNumber | Insets) => (size && typeof size === 'object' ? size : getInset(size)), row: { true: { diff --git a/packages/ui/src/components/layout/Inset.tsx b/packages/ui/src/components/layout/Inset.tsx index 324826cacbe..dd6be3a4773 100644 --- a/packages/ui/src/components/layout/Inset.tsx +++ b/packages/ui/src/components/layout/Inset.tsx @@ -17,9 +17,6 @@ interface InsetProps { * API can be expanded to specific sides * Debug options to color bg to debug spacing */ -export function Inset({ - all = '$spacing16', - children, -}: PropsWithChildren): JSX.Element { +export function Inset({ all = '$spacing16', children }: PropsWithChildren): JSX.Element { return {children} } diff --git a/packages/ui/src/components/logos/ArbiscanLogoDark.tsx b/packages/ui/src/components/logos/ArbiscanLogoDark.tsx index f5cbf2bd3df..5c57f77f85e 100644 --- a/packages/ui/src/components/logos/ArbiscanLogoDark.tsx +++ b/packages/ui/src/components/logos/ArbiscanLogoDark.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [ArbiscanLogoDark, AnimatedArbiscanLogoDark] = createIcon({ name: 'ArbiscanLogoDark', getIcon: (props) => ( - + - + ( - + - + ( - + ( - + - - <_Circle cx="10" cy="10" fill={'currentColor' ?? '#EDF0F4'} r="10" /> + + <_Circle cx="10" cy="10" r="10" fill={'currentColor' ?? '#EDF0F4'} /> - + - - - + + + - + diff --git a/packages/ui/src/components/logos/EtherscanLogoDark.tsx b/packages/ui/src/components/logos/EtherscanLogoDark.tsx index 7edbb996582..a5d87b6f2aa 100644 --- a/packages/ui/src/components/logos/EtherscanLogoDark.tsx +++ b/packages/ui/src/components/logos/EtherscanLogoDark.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [EtherscanLogoDark, AnimatedEtherscanLogoDark] = createIcon({ name: 'EtherscanLogoDark', getIcon: (props) => ( - + ( - + ( - - + + ( - + - + diff --git a/packages/ui/src/components/logos/OpEtherscanLogoLight.tsx b/packages/ui/src/components/logos/OpEtherscanLogoLight.tsx index dd0b41aa5b9..5d8a2fbf203 100644 --- a/packages/ui/src/components/logos/OpEtherscanLogoLight.tsx +++ b/packages/ui/src/components/logos/OpEtherscanLogoLight.tsx @@ -6,34 +6,34 @@ import { createIcon } from '../factories/createIcon' export const [OpEtherscanLogoLight, AnimatedOpEtherscanLogoLight] = createIcon({ name: 'OpEtherscanLogoLight', getIcon: (props) => ( - + - + diff --git a/packages/ui/src/components/logos/PolygonPurple.tsx b/packages/ui/src/components/logos/PolygonPurple.tsx index 61d4ff67c34..0f73fa71db4 100644 --- a/packages/ui/src/components/logos/PolygonPurple.tsx +++ b/packages/ui/src/components/logos/PolygonPurple.tsx @@ -6,7 +6,7 @@ import { createIcon } from '../factories/createIcon' export const [PolygonPurple, AnimatedPolygonPurple] = createIcon({ name: 'PolygonPurple', getIcon: (props) => ( - + ( - + + x2="235.024" + y2="49.9609" + gradientUnits="userSpaceOnUse" + > diff --git a/packages/ui/src/components/logos/PolygonscanLogoLight.tsx b/packages/ui/src/components/logos/PolygonscanLogoLight.tsx index c8e965a6a34..b3cbf462247 100644 --- a/packages/ui/src/components/logos/PolygonscanLogoLight.tsx +++ b/packages/ui/src/components/logos/PolygonscanLogoLight.tsx @@ -6,19 +6,20 @@ import { createIcon } from '../factories/createIcon' export const [PolygonscanLogoLight, AnimatedPolygonscanLogoLight] = createIcon({ name: 'PolygonscanLogoLight', getIcon: (props) => ( - + + x2="235.024" + y2="49.9609" + gradientUnits="userSpaceOnUse" + > diff --git a/packages/ui/src/components/logos/exported.ts b/packages/ui/src/components/logos/exported.ts new file mode 100644 index 00000000000..d1de7e5d11f --- /dev/null +++ b/packages/ui/src/components/logos/exported.ts @@ -0,0 +1,12 @@ +export * from './ArbiscanLogoDark' +export * from './ArbiscanLogoLight' +export * from './Ethereum' +export * from './EthereumLogo' +export * from './EtherscanLogoDark' +export * from './EtherscanLogoLight' +export * from './Moonpay' +export * from './OpEtherscanLogoDark' +export * from './OpEtherscanLogoLight' +export * from './PolygonPurple' +export * from './PolygonscanLogoDark' +export * from './PolygonscanLogoLight' diff --git a/packages/ui/src/components/menu/ContextMenu.tsx b/packages/ui/src/components/menu/ContextMenu.tsx new file mode 100644 index 00000000000..19d6c059657 --- /dev/null +++ b/packages/ui/src/components/menu/ContextMenu.tsx @@ -0,0 +1,107 @@ +import { PropsWithChildren, SyntheticEvent, useEffect, useRef, useState } from 'react' +import { Popover, PopperProps } from 'tamagui' +import { FlexProps } from 'ui/src/components/layout' +import { MenuContent } from 'ui/src/components/menu/MenuContent' +import { MenuContentItem } from 'ui/src/components/menu/types' +import { useSporeColors } from 'ui/src/hooks/useSporeColors' +import { useOnClickOutside, usePrevious } from 'utilities/src/react/hooks' + +type ContextMenuProps = { + menuOptions: MenuContentItem[] + itemId: string + menuStyleProps?: FlexProps + onLeftClick?: boolean + closeOnClick?: boolean +} & PopperProps + +/** + * Base component for a context menu shown on right click. + * Expected use is to wrap a component that will trigger the context menu. + */ +export function ContextMenu({ + children, + menuOptions, + menuStyleProps, + itemId, + onLeftClick = false, + closeOnClick = false, + ...rest +}: PropsWithChildren): JSX.Element { + const lastItemId = usePrevious(itemId) + const colors = useSporeColors() + const [showMenu, setShowMenu] = useState(false) + + // Close the menu if this component was recycled to show a different item + useEffect(() => { + if (lastItemId && itemId !== lastItemId) { + setShowMenu(false) + } + }, [itemId, lastItemId]) + + const onContextMenu = (e: SyntheticEvent): void => { + e.preventDefault() + e.stopPropagation() + setShowMenu(true) + } + + const menuContainerRef = useRef(null) + // TODO(EXT-1324): prevent clicking through when clicking outside the menu. The menu should just close. + useOnClickOutside(menuContainerRef, () => setShowMenu(false)) + + // Offset the content by the height of the trigger element, so its aligned to the top + // Ignore if any values besides default are passed + const triggerContainerRef = useRef(null) + const { offset: customOffset, placement } = rest + const offset = + customOffset || (placement && placement !== 'bottom-end') + ? customOffset + : -(triggerContainerRef.current?.offsetHeight ?? 0) + + const contentShadowProps = { + shadowColor: colors.shadowColor.val, + shadowRadius: 12, + shadowOpacity: 0.1, + } + + return ( + + +
+ {children} +
+ + +
+ setShowMenu(false) : undefined} + {...menuStyleProps} + /> +
+ +
+ + ) +} diff --git a/packages/ui/src/components/menu/MenuContent.tsx b/packages/ui/src/components/menu/MenuContent.tsx new file mode 100644 index 00000000000..208773d29a2 --- /dev/null +++ b/packages/ui/src/components/menu/MenuContent.tsx @@ -0,0 +1,50 @@ +import { BaseSyntheticEvent, useCallback } from 'react' +import { Flex, FlexProps } from 'ui/src/components/layout' +import { MenuContentItem } from 'ui/src/components/menu/types' +import { Text } from 'ui/src/components/text' +import { TouchableArea } from 'ui/src/components/touchable' +import { useIsDarkMode } from 'ui/src/hooks/useIsDarkMode' + +type MenuContentProps = { + onClose?: () => void + items: MenuContentItem[] +} + +export function MenuContent({ items, onClose, ...rest }: MenuContentProps & FlexProps): JSX.Element { + const isDarkMode = useIsDarkMode() + + const handleOnPress = useCallback( + (e: BaseSyntheticEvent, onPress: (e: BaseSyntheticEvent) => void): void => { + onPress(e) + onClose?.() + }, + [onClose], + ) + + return ( + + {items.map(({ label, onPress, Icon, textProps, iconProps, destructive, ...touchableProps }, index) => ( + handleOnPress(e, onPress)} + {...touchableProps} + > + + + {label} + + {Icon && } + + + ))} + + ) +} diff --git a/packages/ui/src/components/menu/types.ts b/packages/ui/src/components/menu/types.ts new file mode 100644 index 00000000000..e0c9741d5fc --- /dev/null +++ b/packages/ui/src/components/menu/types.ts @@ -0,0 +1,13 @@ +import { BaseSyntheticEvent } from 'react' +import { GeneratedIcon, IconProps } from 'ui/src/components/factories/createIcon' +import { TextProps } from 'ui/src/components/text' +import { TouchableAreaProps } from 'ui/src/components/touchable' + +export type MenuContentItem = { + label: string + onPress: (e: BaseSyntheticEvent) => void + textProps?: TextProps + Icon?: GeneratedIcon | ((props: IconProps) => JSX.Element) + iconProps?: IconProps + destructive?: boolean +} & TouchableAreaProps diff --git a/packages/ui/src/components/text/GradientText.tsx b/packages/ui/src/components/text/GradientText.tsx index 79c80b9ba8b..5dfeab9b29e 100644 --- a/packages/ui/src/components/text/GradientText.tsx +++ b/packages/ui/src/components/text/GradientText.tsx @@ -3,9 +3,7 @@ import { GetProps } from 'tamagui' import { LinearGradientProps } from 'tamagui/linear-gradient' import { Text } from 'ui/src/components/text' -export type GradientTextProps = PropsWithChildren< - GetProps & { gradient: LinearGradientProps } -> +export type GradientTextProps = PropsWithChildren & { gradient: LinearGradientProps }> // TODO(WEB-4313): Implement GradientText for web export function GradientText({ children, ...props }: GradientTextProps): JSX.Element { diff --git a/packages/ui/src/components/text/HiddenFromScreenReaders.native.tsx b/packages/ui/src/components/text/HiddenFromScreenReaders.native.tsx index d0a72d3aee5..3442c2a3f7d 100644 --- a/packages/ui/src/components/text/HiddenFromScreenReaders.native.tsx +++ b/packages/ui/src/components/text/HiddenFromScreenReaders.native.tsx @@ -1,15 +1,9 @@ import { View } from 'react-native' import { HiddenFromScreenReadersProps } from 'ui/src/components/text/HiddenFromScreenReaders' -export function HiddenFromScreenReaders({ - children, - style, -}: HiddenFromScreenReadersProps): JSX.Element { +export function HiddenFromScreenReaders({ children, style }: HiddenFromScreenReadersProps): JSX.Element { return ( - + {children} ) diff --git a/packages/ui/src/components/text/HiddenFromScreenReaders.tsx b/packages/ui/src/components/text/HiddenFromScreenReaders.tsx index ac948907dad..49e24562ed4 100644 --- a/packages/ui/src/components/text/HiddenFromScreenReaders.tsx +++ b/packages/ui/src/components/text/HiddenFromScreenReaders.tsx @@ -6,10 +6,7 @@ export type HiddenFromScreenReadersProps = PropsWithChildren<{ style?: ViewStyle }> -export function HiddenFromScreenReaders({ - children, - style, -}: HiddenFromScreenReadersProps): JSX.Element { +export function HiddenFromScreenReaders({ children, style }: HiddenFromScreenReadersProps): JSX.Element { // TODO(MOB-1533) Make hidden from screen reader functionality work with web too return {children} } diff --git a/packages/ui/src/components/text/Text.tsx b/packages/ui/src/components/text/Text.tsx index a6b9c40299e..0446b4d9007 100644 --- a/packages/ui/src/components/text/Text.tsx +++ b/packages/ui/src/components/text/Text.tsx @@ -170,21 +170,13 @@ export const TextLoaderWrapper = ({ * @param loadingPlaceholderText - The text that the loader's size will be derived from. Pick something that's close to the same length as the final text is expected to be, e.g. if it's a ticker symbol, "XXX" might be a good placeholder text. This prop is optional and defaults to "000.00". */ export const Text = TextFrame.styleable( - ( - { loading = false, allowFontScaling, loadingPlaceholderText = '000.00', ...rest }: TextProps, - ref - ): JSX.Element => { + ({ loading = false, allowFontScaling, loadingPlaceholderText = '000.00', ...rest }: TextProps, ref): JSX.Element => { const enableFontScaling = useEnableFontScaling(allowFontScaling) if (loading) { return ( - + {/* Important that `children` isn't used or rendered by when `loading` is true, because if the child of a component is a dynamic variable that might not be finished fetching yet, it'll result in an error until it's finished loading. We use `loadingPlaceholderText` to set the size of the loading element instead. */} {loadingPlaceholderText} @@ -193,5 +185,5 @@ export const Text = TextFrame.styleable( } return - } + }, ) diff --git a/packages/ui/src/components/text/UniswapXText.native.tsx b/packages/ui/src/components/text/UniswapXText.native.tsx index 575d024589a..3ec856d97c2 100644 --- a/packages/ui/src/components/text/UniswapXText.native.tsx +++ b/packages/ui/src/components/text/UniswapXText.native.tsx @@ -11,7 +11,8 @@ export function UniswapXText({ children, ...props }: GetProps): JSX colors: [colors.uniswapXViolet, colors.uniswapXPurple], start: { x: -1.07, y: 0 }, end: { x: 1.07, y: 0 }, - }}> + }} + > {children} ) diff --git a/packages/ui/src/components/touchable/TouchableArea.tsx b/packages/ui/src/components/touchable/TouchableArea.tsx index b9d97ce59fd..b3e0580bb93 100644 --- a/packages/ui/src/components/touchable/TouchableArea.tsx +++ b/packages/ui/src/components/touchable/TouchableArea.tsx @@ -5,6 +5,7 @@ import { withAnimated } from 'ui/src/components/factories/animated' import { TouchableAreaProps } from 'ui/src/components/touchable/types' import { defaultHitslopInset } from 'ui/src/theme' import { HapticFeedback } from 'ui/src/utils/haptics/HapticFeedback' +import { isTestEnv } from 'utilities/src/environment' /** * If you are trying to implement a standard button DO NOT USE this component. Use the Button component instead with the desired size and emphasis. @@ -25,12 +26,9 @@ export const TouchableArea = forwardRef(func activeOpacity = 0.75, ...restProps }, - ref + ref, ): JSX.Element { - const touchActivationPositionRef = useRef | null>(null) + const touchActivationPositionRef = useRef | null>(null) const onPressHandler = useCallback( async (event: GestureResponderEvent) => { @@ -44,12 +42,7 @@ export const TouchableArea = forwardRef(func const isDragEvent = touchActivationPositionRef.current && - isDrag( - touchActivationPositionRef.current.pageX, - touchActivationPositionRef.current.pageY, - pageX, - pageY - ) + isDrag(touchActivationPositionRef.current.pageX, touchActivationPositionRef.current.pageY, pageX, pageY) if (isDragEvent) { return @@ -62,7 +55,7 @@ export const TouchableArea = forwardRef(func await HapticFeedback.impact(hapticStyle) } }, - [onPress, ignoreDragEvents, hapticFeedback, hapticStyle] + [onPress, ignoreDragEvents, hapticFeedback, hapticStyle], ) const onPressInHandler = useMemo(() => { @@ -75,7 +68,7 @@ export const TouchableArea = forwardRef(func (func }, })} onPress={onPressHandler} - onPressIn={onPressInHandler}> + onPressIn={onPressInHandler} + > {children} ) @@ -107,13 +101,7 @@ export const AnimatedTouchableArea = withAnimated(TouchableArea) * @link https://github.com/satya164/react-native-tab-view/issues/1241#issuecomment-1022400366 * @returns true if press was after a drag gesture */ -function isDrag( - activationX: number, - activationY: number, - releaseX: number, - releaseY: number, - threshold = 2 -): boolean { +function isDrag(activationX: number, activationY: number, releaseX: number, releaseY: number, threshold = 2): boolean { const absX = Math.abs(activationX - releaseX) const absY = Math.abs(activationY - releaseY) diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 5c5ec64bfbe..c2fb28c5ccd 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -38,6 +38,7 @@ export type { TamaguiProviderProps, ThemeKeys, Tokens, + ViewProps, } from 'tamagui' export { LinearGradient } from 'tamagui/linear-gradient' export { Unicon } from './components/Unicon' @@ -50,6 +51,9 @@ export type { GeneratedIcon, IconProps } from './components/factories/createIcon export * from './components/input/CheckBox' export * from './components/input/utils' export { Flex, Inset, Separator, flexStyles, type FlexProps } from './components/layout' +export { ContextMenu } from './components/menu/ContextMenu' +export { MenuContent } from './components/menu/MenuContent' +export type { MenuContentItem } from './components/menu/types' export * from './components/text' export { Tooltip } from './components/tooltip/Tooltip' export * from './components/touchable' diff --git a/packages/ui/src/loading/Loader.tsx b/packages/ui/src/loading/Loader.tsx index 85f375bb52c..a2c51ec3964 100644 --- a/packages/ui/src/loading/Loader.tsx +++ b/packages/ui/src/loading/Loader.tsx @@ -36,27 +36,12 @@ const TransferInstitution = memo(function _TransferInstitution({ return ( {new Array(itemsCount).fill(null).map((_, i) => ( - + - + - + ))} @@ -111,7 +96,7 @@ function NFT({ repeat = 1 }: { repeat?: number }): JSX.Element { })} ), - [repeat] + [repeat], ) return {loader} diff --git a/packages/ui/src/loading/Shine.native.tsx b/packages/ui/src/loading/Shine.native.tsx index d858ff8a896..48ca405e024 100644 --- a/packages/ui/src/loading/Shine.native.tsx +++ b/packages/ui/src/loading/Shine.native.tsx @@ -31,11 +31,7 @@ export function Shine({ children, disabled }: ShineProps): JSX.Element { const animatedStyle = useAnimatedStyle(() => ({ transform: [ { - translateX: interpolate( - xPosition.value, - [0, 1], - [layout ? -layout.width : 0, layout ? layout.width : 0] - ), + translateX: interpolate(xPosition.value, [0, 1], [layout ? -layout.width : 0, layout ? layout.width : 0]), }, ], })) @@ -52,7 +48,8 @@ export function Shine({ children, disabled }: ShineProps): JSX.Element { nativeEvent: { layout: React.SetStateAction } }): void => { setLayout(event.nativeEvent.layout) - }}> + }} + > {children} ) diff --git a/packages/ui/src/loading/Shine.web.tsx b/packages/ui/src/loading/Shine.web.tsx index 0a8e657042c..9ef89ea8886 100644 --- a/packages/ui/src/loading/Shine.web.tsx +++ b/packages/ui/src/loading/Shine.web.tsx @@ -12,7 +12,8 @@ export function Shine({ children, disabled }: ShineProps): JSX.Element { WebkitMaskSize: '200%', animation: 'shine 1s linear infinite', } - }> + } + > {children} ) diff --git a/packages/ui/src/loading/Skeleton.native.tsx b/packages/ui/src/loading/Skeleton.native.tsx index 1461152e9b2..d1cbda3e91c 100644 --- a/packages/ui/src/loading/Skeleton.native.tsx +++ b/packages/ui/src/loading/Skeleton.native.tsx @@ -30,11 +30,7 @@ export function Skeleton({ children, contrast, disabled }: SkeletonProps): JSX.E const animatedStyle = useAnimatedStyle(() => ({ transform: [ { - translateX: interpolate( - xPosition.value, - [0, 1], - [layout ? -layout.width : 0, layout ? layout.width : 0] - ), + translateX: interpolate(xPosition.value, [0, 1], [layout ? -layout.width : 0, layout ? layout.width : 0]), }, ], })) @@ -48,11 +44,10 @@ export function Skeleton({ children, contrast, disabled }: SkeletonProps): JSX.E } - }): void => { + onLayout={(event: { nativeEvent: { layout: SetStateAction } }): void => { setLayout(event.nativeEvent.layout) - }}> + }} + > {children} ) @@ -66,7 +61,8 @@ export function Skeleton({ children, contrast, disabled }: SkeletonProps): JSX.E width: layout.width, height: layout.height, }} - testID="shimmer"> + testID="shimmer" + > } - style={StyleSheet.absoluteFill}> + style={StyleSheet.absoluteFill} + > diff --git a/packages/ui/src/loading/SpinningLoader.native.tsx b/packages/ui/src/loading/SpinningLoader.native.tsx index c49c4b46e8a..e71e2a79e9d 100644 --- a/packages/ui/src/loading/SpinningLoader.native.tsx +++ b/packages/ui/src/loading/SpinningLoader.native.tsx @@ -30,7 +30,7 @@ export function SpinningLoader({ size = 20, disabled, color }: SpinningLoaderPro duration: 1000, easing: Easing.bezier(0.83, 0, 0.17, 1), }), - -1 + -1, ) return () => cancelAnimation(rotation) }, [rotation]) diff --git a/packages/ui/src/loading/SpinningLoader.tsx b/packages/ui/src/loading/SpinningLoader.tsx index 78db6ddbc19..ec53e768d2f 100644 --- a/packages/ui/src/loading/SpinningLoader.tsx +++ b/packages/ui/src/loading/SpinningLoader.tsx @@ -32,14 +32,9 @@ export function SpinningLoader({ size = 20, disabled, color }: SpinningLoaderPro justifyContent="center" marginEnd={2} marginStart={2} - width={16}> - + width={16} + > + + - + - + - + )} diff --git a/packages/ui/src/loading/TransactionLoader.tsx b/packages/ui/src/loading/TransactionLoader.tsx index 09938e7ee45..ba372f8b3b1 100644 --- a/packages/ui/src/loading/TransactionLoader.tsx +++ b/packages/ui/src/loading/TransactionLoader.tsx @@ -11,20 +11,8 @@ export const TXN_HISTORY_LOADER_ICON_SIZE = iconSizes.icon40 export function TransactionLoader({ opacity }: TransactionLoaderProps): JSX.Element { return ( - - + + - + + sentry-label="WalletLoader" + > diff --git a/packages/ui/src/scripts/componentize-icons.ts b/packages/ui/src/scripts/componentize-icons.ts index 3da70dba271..eaeeb17e88d 100644 --- a/packages/ui/src/scripts/componentize-icons.ts +++ b/packages/ui/src/scripts/componentize-icons.ts @@ -3,7 +3,6 @@ /* eslint-disable no-useless-escape */ import camelcase from 'camelcase' import { load } from 'cheerio' -import { ESLint } from 'eslint' import { ensureDirSync, existsSync, readFileSync, readdirSync, writeFileSync } from 'fs-extra' import path, { join } from 'path' @@ -73,14 +72,14 @@ async function createSVGComponents(dirs: DirectoryPair, skipExisting: boolean): // Format and write index file console.log('Writing index file...') - const formattedIndex = await eslintFormat(indexFile, 'exported.ts') + const formattedIndex = await prettierFormat(indexFile) writeFileSync(join(dirs.output, 'exported.ts'), formattedIndex, 'utf-8') } async function generateSVGComponent(svg: string, fileName: string): Promise { try { const element = generateSVGComponentString(svg, fileName) - return await eslintFormat(element, fileName) + return await prettierFormat(element) } catch (err) { console.log(`Error converting icon: ${fileName}: ${(err as any).message}`) } @@ -104,17 +103,7 @@ function generateSVGComponentString(svg: string, fileName: string): string { const attribsOfInterest: Record = {} Object.keys(svgAttribs).forEach((key) => { - if ( - ![ - 'height', - 'width', - 'viewBox', - 'fill', - 'stroke-width', - 'stroke-linecap', - 'stroke-linejoin', - ].includes(key) - ) { + if (!['height', 'width', 'viewBox', 'fill', 'stroke-width', 'stroke-linecap', 'stroke-linejoin'].includes(key)) { attribsOfInterest[key] = svgAttribs[key] } }) @@ -192,7 +181,6 @@ import PropTypes from 'prop-types' import { Svg, SvgProps, -Circle as _Circle, Ellipse, G, LinearGradient, @@ -203,11 +191,12 @@ Polygon, Polyline, Rect, Symbol, -Text as _Text, Use, Defs, Stop, ClipPath +Text as _Text, +Circle as _Circle, } from 'react-native-svg' // eslint-disable-next-line no-relative-import-paths/no-relative-import-paths @@ -232,24 +221,29 @@ function generateClassName(fileName: string): string { return uppercamelcase(path.basename(fileName, '.svg')) as string } -// Linting +// Formatting -const eslint = new ESLint({ fix: true }) +import fs from 'fs' +import { format } from 'prettier' -async function eslintFormat(source: string, name: string): Promise { - const out = await eslint.lintText(source, { - // eslint wants a file to use for determining format and it actually has to exist 🙄 - filePath: './src/scripts/componentize-icons-eslint-dummy-file.tsx', - }) +const configPath = path.resolve(__dirname, '../../../../.prettierrc') +// eslint-disable-next-line security/detect-non-literal-fs-filename +const config = JSON.parse(fs.readFileSync(configPath, 'utf-8')) - const lintedFile = out?.[0]?.output +// it was removing needed imports for some reason - if (lintedFile) { - return lintedFile - } else { - console.warn(`not linted ${name}`) - return source - } +async function prettierFormat(source: string): Promise { + // lol for some reason it removes imports it shouldnt it you run it in one pass + // running this in two passes with the second organizing imports fixes it... + const formattedOnce = await format(source, { + ...config, + organizeImportsSkipDestructiveCodeActions: true, + parser: 'typescript', + }) + return await format(formattedOnce, { + ...config, + parser: 'typescript', + }) } // This must be at the end to run all code diff --git a/packages/ui/src/theme/animations.ts b/packages/ui/src/theme/animations.ts index ba6d28f6bd6..4fda347e1bf 100644 --- a/packages/ui/src/theme/animations.ts +++ b/packages/ui/src/theme/animations.ts @@ -13,6 +13,12 @@ export const animations = createAnimations({ type: 'timing', duration: 300, }, + stiff: { + type: 'spring', + mass: 1, + damping: 200, + stiffness: 400, + }, bouncy: { type: 'spring', damping: 10, @@ -42,4 +48,14 @@ export const animations = createAnimations({ mass: 0.9, stiffness: 390, }, + // TODO(TAM-49): the animation config prop inline isn't passing delay, need to + // fix on tamagui side then we can remove this and just use quicker + add + // delay inline + quickishDelayed: { + type: 'spring', + damping: 18, + mass: 0.9, + stiffness: 200, + delay: 70, + }, }) diff --git a/packages/ui/src/theme/color/utils.ts b/packages/ui/src/theme/color/utils.ts index 7c9cdbddb4e..607d5ad656c 100644 --- a/packages/ui/src/theme/color/utils.ts +++ b/packages/ui/src/theme/color/utils.ts @@ -5,9 +5,7 @@ export function opacify(amount: number, hexColor: string): string { } if (hexColor.length !== 7) { - throw new Error( - `opacify: provided color ${hexColor} was not in hexadecimal format (e.g. #000000)` - ) + throw new Error(`opacify: provided color ${hexColor} was not in hexadecimal format (e.g. #000000)`) } if (amount < 0 || amount > 100) { diff --git a/packages/ui/src/theme/fonts.ts b/packages/ui/src/theme/fonts.ts index 74afbf0a0c6..8c6844ca5b0 100644 --- a/packages/ui/src/theme/fonts.ts +++ b/packages/ui/src/theme/fonts.ts @@ -26,9 +26,7 @@ const fontFamily = { type SansSerifFontFamilyKey = keyof typeof fontFamily.sansSerif type SansSerifFontFamilyValue = (typeof fontFamily.sansSerif)[SansSerifFontFamilyKey] -const platformFontFamily = ( - family: SansSerifFontFamilyKey -): SansSerifFontFamilyKey | SansSerifFontFamilyValue => { +const platformFontFamily = (family: SansSerifFontFamilyKey): SansSerifFontFamilyKey | SansSerifFontFamilyValue => { if (isWeb) { return family } diff --git a/packages/ui/src/theme/themes.ts b/packages/ui/src/theme/themes.ts index f7d1ed3693d..dcc7c925ba6 100644 --- a/packages/ui/src/theme/themes.ts +++ b/packages/ui/src/theme/themes.ts @@ -7,6 +7,7 @@ import { opacify } from 'ui/src/theme/color/utils' export type ThemeNames = | 'primary' | 'secondary' + | 'accentSecondary' | 'tertiary' | 'outline' | 'warning' @@ -108,6 +109,12 @@ const dark_secondary: BaseTheme = { ...dark, color: colorsDark.neutral2, } +const light_accentSecondary: BaseTheme = { + ...light, +} +const dark_accentSecondary: BaseTheme = { + ...dark, +} const light_tertiary: BaseTheme = { ...light, } @@ -172,6 +179,25 @@ const dark_secondary_Button: BaseTheme = { color: colorsDark.sporeWhite, } +// accentSecondary +// theme: light +const light_accentSecondary_Button: BaseTheme = { + ...light, + background: colorsLight.accent2, + backgroundHover: colorsLight.accent2Hovered, + backgroundPress: pressedColor(colorsLight.accent2), + color: colorsLight.accent1, +} + +//theme:dark +const dark_accentSecondary_Button: BaseTheme = { + ...dark, + background: colorsDark.accent2, + backgroundHover: colorsDark.accent2Hovered, + backgroundPress: pressedColor(colorsDark.accent2), + color: colorsDark.accent1, +} + // tertiary // theme: light const light_tertiary_Button: BaseTheme = { @@ -264,6 +290,8 @@ const allThemes = { dark_primary, light_secondary, dark_secondary, + light_accentSecondary, + dark_accentSecondary, light_tertiary, dark_tertiary, light_detrimental, @@ -274,6 +302,8 @@ const allThemes = { dark_primary_Button, light_secondary_Button, dark_secondary_Button, + light_accentSecondary_Button, + dark_accentSecondary_Button, light_tertiary_Button, dark_tertiary_Button, light_outline, diff --git a/packages/ui/src/theme/tokens.ts b/packages/ui/src/theme/tokens.ts index c7e09e4c1b2..d58d926e3e9 100644 --- a/packages/ui/src/theme/tokens.ts +++ b/packages/ui/src/theme/tokens.ts @@ -98,7 +98,7 @@ export const validColor = (value: DynamicColor | string | undefined | null): Col !value.startsWith('var(') ) { throw new Error( - `Invalid color value: ${value} this helper just does a rough check so if this error is wrong you can update this check!` + `Invalid color value: ${value} this helper just does a rough check so if this error is wrong you can update this check!`, ) } } diff --git a/packages/ui/src/utils/colors/getExtractedColors.ts b/packages/ui/src/utils/colors/getExtractedColors.ts index 22e22cd2584..7ebbfbf7b24 100644 --- a/packages/ui/src/utils/colors/getExtractedColors.ts +++ b/packages/ui/src/utils/colors/getExtractedColors.ts @@ -11,7 +11,7 @@ export type ExtractedColors = { export async function getExtractedColors( imageUrl: Maybe, fallback: ColorKeys = 'accent1', - cache = true + cache = true, ): Promise { if (!imageUrl) { return diff --git a/packages/ui/src/utils/colors/index.ts b/packages/ui/src/utils/colors/index.ts index 36eb9124acc..1ca56ef9b66 100644 --- a/packages/ui/src/utils/colors/index.ts +++ b/packages/ui/src/utils/colors/index.ts @@ -15,8 +15,7 @@ export const SPECIAL_CASE_TOKEN_COLORS: { [key: string]: string } = { 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png': '#F09241', // new WBTC - 'https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1548822744': - '#F09241', + 'https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1548822744': '#F09241', // DAI 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png': '#FAB01B', @@ -31,16 +30,14 @@ export const SPECIAL_CASE_TOKEN_COLORS: { [key: string]: string } = { // ETH 'https://token-icons.s3.amazonaws.com/eth.png': '#4970D5', // HARRYPOTTERSHIBAINUBITCOIN - 'https://assets.coingecko.com/coins/images/30323/large/hpos10i_logo_casino_night-dexview.png?1684117567': - '#DE3110', + 'https://assets.coingecko.com/coins/images/30323/large/hpos10i_logo_casino_night-dexview.png?1684117567': '#DE3110', // PEPE 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6982508145454Ce325dDbE47a25d4ec3d2311933/logo.png': '#3EAE14', // Unibot V2 'https://s2.coinmarketcap.com/static/img/coins/64x64/25436.png': '#4A0A4F', // UNIBOT v1 - 'https://assets.coingecko.com/coins/images/30462/small/logonoline_%281%29.png?1687510315': - '#4A0A4F', + 'https://assets.coingecko.com/coins/images/30462/small/logonoline_%281%29.png?1687510315': '#4A0A4F', // USDC 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png': '#0066D9', @@ -62,8 +59,7 @@ export const SPECIAL_CASE_TOKEN_COLORS: { [key: string]: string } = { 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA35923162C49cF95e6BF26623385eb431ad920D3/logo.png': '#BD6E29', // AIDOGE - 'https://assets.coingecko.com/coins/images/29852/large/photo_2023-04-18_14-25-28.jpg?1681799160': - '#29A1F1', + 'https://assets.coingecko.com/coins/images/29852/large/photo_2023-04-18_14-25-28.jpg?1681799160': '#29A1F1', // SIMPSON 'https://assets.coingecko.com/coins/images/30243/large/1111.png?1683692033': '#E88F00', // MAKER @@ -72,8 +68,7 @@ export const SPECIAL_CASE_TOKEN_COLORS: { [key: string]: string } = { // OX 'https://assets.coingecko.com/coins/images/30604/large/Logo2.png?1685522119': '#2959D9', // ANGLE - 'https://assets.coingecko.com/coins/images/19060/large/ANGLE_Token-light.png?1666774221': - '#FF5555', + 'https://assets.coingecko.com/coins/images/19060/large/ANGLE_Token-light.png?1666774221': '#FF5555', // APE 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4d224452801ACEd8B2F0aebE155379bb5D594381/logo.png': '#054AA9', @@ -100,11 +95,11 @@ const blackAndWhiteSpecialCase: Set = new Set([ export function useExtractedColors( imageUrl: Maybe, fallback: ColorKeys = 'accent1', - cache = true + cache = true, ): { colors?: ExtractedColors; colorsLoading: boolean } { const getImageColors = useCallback( async () => getExtractedColors(imageUrl, fallback, cache), - [imageUrl, fallback, cache] + [imageUrl, fallback, cache], ) const { data: colors, isLoading: colorsLoading } = useAsyncData(getImageColors) @@ -148,7 +143,7 @@ export function useExtractedTokenColor( imageUrl: Maybe, tokenName: Maybe, backgroundColor: string, - defaultColor: string + defaultColor: string, ): { tokenColor: Nullable; tokenColorLoading: boolean } { const sporeColors = useSporeColors() const { colors, colorsLoading } = useExtractedColors(imageUrl) @@ -256,17 +251,11 @@ function getLogolessColorIndex(tokenName: string, numOptions: number): number { export function useLogolessColorScheme(tokenName: string): ColorScheme { return useMemo(() => { const index = getLogolessColorIndex(tokenName, Object.keys(LOGOLESS_COLORS).length) - return logolessColorSchemes[ - LOGOLESS_COLORS[Object.keys(LOGOLESS_COLORS)[index] as keyof typeof LOGOLESS_COLORS] - ] + return logolessColorSchemes[LOGOLESS_COLORS[Object.keys(LOGOLESS_COLORS)[index] as keyof typeof LOGOLESS_COLORS]] }, [tokenName]) } -export function passesContrast( - color: string, - backgroundColor: string, - contrastThreshold: number -): boolean { +export function passesContrast(color: string, backgroundColor: string, contrastThreshold: number): boolean { // sometimes the extracted colors come back as black or white, discard those if (!color || color === '#000000' || color === '#FFFFFF') { return false @@ -285,10 +274,7 @@ export function passesContrast( * color against * @returns a hex code that will pass a contrast check against the background */ -function pickContrastPassingTokenColor( - extractedColors: ExtractedColors, - backgroundHex: string -): string { +function pickContrastPassingTokenColor(extractedColors: ExtractedColors, backgroundHex: string): string { const colorsInOrder = [ extractedColors.base, extractedColors.detail, diff --git a/packages/ui/src/utils/haptics/HapticFeedback.native.ts b/packages/ui/src/utils/haptics/HapticFeedback.native.ts index cf5eb07da02..7224b452bdd 100644 --- a/packages/ui/src/utils/haptics/HapticFeedback.native.ts +++ b/packages/ui/src/utils/haptics/HapticFeedback.native.ts @@ -16,7 +16,7 @@ function isImpactFeedbackStyle(style: HapticFeedbackStyle): style is ImpactFeedb export const HapticFeedback: THapticFeedback = { impact: async ( - style: ImpactFeedbackStyle | NotificationFeedbackType | undefined = ImpactFeedbackStyle.Light + style: ImpactFeedbackStyle | NotificationFeedbackType | undefined = ImpactFeedbackStyle.Light, ): Promise => { return isImpactFeedbackStyle(style) ? await impactAsync(style) : await notificationAsync(style) }, diff --git a/packages/uniswap/codegen.ts b/packages/uniswap/codegen.ts index d06d40210d1..4059177f1fc 100644 --- a/packages/uniswap/codegen.ts +++ b/packages/uniswap/codegen.ts @@ -19,8 +19,8 @@ const config: CodegenConfig = { withHooks: true, maybeValue: 'T | undefined', }, - } - } + }, + }, } export default config diff --git a/packages/uniswap/package.json b/packages/uniswap/package.json index 19be065cb24..f99d6db0e09 100644 --- a/packages/uniswap/package.json +++ b/packages/uniswap/package.json @@ -10,6 +10,7 @@ "graphql:generate": "graphql-codegen --config codegen.ts", "graphql:schema": "get-graphql-schema https://api.uniswap.org/v1/graphql -h Origin=https://app.uniswap.org > ./src/data/graphql/uniswap-data-api/schema.graphql", "i18n:generate": "i18next-resources-for-ts interface -i ./src/i18n/locales/source -o ./src/i18n/locales/@types/resources.d.ts", + "format": "../../scripts/prettier.sh", "lint": "eslint . --ext ts,tsx --max-warnings=0", "lint:fix": "eslint . --ext ts,tsx --fix", "test": "jest --passWithNoTests", @@ -18,6 +19,8 @@ }, "dependencies": { "@apollo/client": "3.10.4", + "@ethersproject/address": "5.7.0", + "@ethersproject/bignumber": "5.7.0", "@ethersproject/providers": "5.7.2", "@gorhom/bottom-sheet": "4.5.1", "@react-native-async-storage/async-storage": "1.17.10", @@ -29,6 +32,7 @@ "apollo-link-rest": "0.9.0", "ethers": "5.7.2", "expo-blur": "12.9.2", + "fuse.js": "6.5.3", "i18next": "23.10.0", "lodash": "4.17.21", "react": "18.2.0", @@ -38,11 +42,13 @@ "react-native-device-info": "10.0.2", "react-native-reanimated": "3.8.1", "react-test-renderer": "18.2.0", + "react-virtualized-auto-sizer": "1.0.20", + "react-window": "1.8.9", "statsig-react": "1.32.0", "statsig-react-native": "4.11.0", "ui": "workspace:^", "utilities": "workspace:^", - "wagmi": "2.5.19" + "wagmi": "2.8.4" }, "devDependencies": { "@graphql-codegen/cli": "^3.3.1", @@ -55,6 +61,7 @@ "@testing-library/react-hooks": "7.0.2", "@testing-library/react-native": "11.5.0", "@types/chrome": "0.0.254", + "@types/react-window": "1.8.2", "@uniswap/eslint-config": "workspace:^", "depcheck": "1.4.7", "eslint": "8.44.0", diff --git a/packages/uniswap/src/components/BaseCard/BaseCard.test.tsx b/packages/uniswap/src/components/BaseCard/BaseCard.test.tsx index af8d4281f6a..335205c44ff 100644 --- a/packages/uniswap/src/components/BaseCard/BaseCard.test.tsx +++ b/packages/uniswap/src/components/BaseCard/BaseCard.test.tsx @@ -48,9 +48,7 @@ describe(BaseCard.Header, () => { }) it('renders custom subtitle component if component is passed', () => { - const { queryByTestId } = render( - } title={title} /> - ) + const { queryByTestId } = render(} title={title} />) expect(queryByTestId('custom-subtitle')).toBeTruthy() }) @@ -58,9 +56,7 @@ describe(BaseCard.Header, () => { describe('icon', () => { it('renders icon if icon is passed', () => { - const { queryByTestId } = render( - } title={title} /> - ) + const { queryByTestId } = render(} title={title} />) expect(queryByTestId('custom-icon')).toBeTruthy() }) @@ -69,9 +65,7 @@ describe(BaseCard.Header, () => { describe('onPress', () => { it('calls onPress if onPress is passed and title is pressed', () => { const onPress = jest.fn() - const { getByTestId } = render( - - ) + const { getByTestId } = render() expect(onPress).toHaveBeenCalledTimes(0) @@ -97,7 +91,7 @@ describe(BaseCard.EmptyState, () => { it('renders icon when icon is passed', () => { const { queryByTestId } = render( - } /> + } />, ) expect(queryByTestId('custom-icon')).toBeTruthy() @@ -111,9 +105,7 @@ describe(BaseCard.EmptyState, () => { describe('button', () => { it('renders button when buttonLabel is passed', () => { - const { queryByText } = render( - - ) + const { queryByText } = render() expect(queryByText('buttonLabel')).toBeTruthy() }) @@ -121,11 +113,7 @@ describe(BaseCard.EmptyState, () => { it('calls onPress when button is pressed', () => { const onPress = jest.fn() const { getByText } = render( - + , ) expect(onPress).toHaveBeenCalledTimes(0) @@ -139,10 +127,7 @@ describe(BaseCard.EmptyState, () => { describe('additional button', () => { it('renders additional button when additionalButtonLabel is passed', () => { const { queryByText } = render( - + , ) expect(queryByText('additionalButtonLabel')).toBeTruthy() @@ -155,7 +140,7 @@ describe(BaseCard.EmptyState, () => { additionalButtonLabel="additionalButtonLabel" description="description" onPressAdditional={onPressAdditional} - /> + />, ) expect(onPressAdditional).toHaveBeenCalledTimes(0) @@ -189,7 +174,7 @@ describe(BaseCard.ErrorState, () => { describe('retry button', () => { it('renders retry button when retryButtonLabel is passed', () => { const { queryByText } = render( - + , ) expect(queryByText('retryButtonLabel')).toBeTruthy() @@ -198,11 +183,7 @@ describe(BaseCard.ErrorState, () => { it('calls onRetry when retry button is pressed', () => { const onRetry = jest.fn() const { getByText } = render( - + , ) expect(onRetry).toHaveBeenCalledTimes(0) @@ -215,7 +196,7 @@ describe(BaseCard.ErrorState, () => { it('renders icon when icon is passed', () => { const { queryByTestId } = render( - } /> + } />, ) expect(queryByTestId('custom-icon')).toBeTruthy() @@ -257,9 +238,7 @@ describe(BaseCard.InlineErrorState, () => { }) it('renders custom retry button when retryButtonLabel is passed', () => { - const { queryByText } = render( - - ) + const { queryByText } = render() expect(queryByText('custom-label')).toBeTruthy() }) @@ -284,9 +263,7 @@ describe(BaseCard.InlineErrorState, () => { }) it('renders custom icon when icon is passed', () => { - const { queryByTestId } = render( - } /> - ) + const { queryByTestId } = render(} />) expect(queryByTestId('custom-icon')).toBeTruthy() }) diff --git a/packages/uniswap/src/components/BaseCard/BaseCard.tsx b/packages/uniswap/src/components/BaseCard/BaseCard.tsx index 8fe27518e34..ee15631831a 100644 --- a/packages/uniswap/src/components/BaseCard/BaseCard.tsx +++ b/packages/uniswap/src/components/BaseCard/BaseCard.tsx @@ -1,14 +1,6 @@ import { ComponentProps, ReactNode } from 'react' import { useTranslation } from 'react-i18next' -import { - ColorTokens, - Flex, - FlexProps, - Text, - TouchableArea, - useIsDarkMode, - useSporeColors, -} from 'ui/src' +import { ColorTokens, Flex, FlexProps, Text, TouchableArea, useIsDarkMode, useSporeColors } from 'ui/src' import { AlertTriangle } from 'ui/src/components/icons/AlertTriangle' import { RotatableChevron } from 'ui/src/components/icons/RotatableChevron' import { opacify } from 'ui/src/theme' @@ -30,11 +22,10 @@ export function Shadow({ children, ...rest }: FlexProps): JSX.Element { shadowOpacity={0.0075} shadowRadius={10} style={ - hasBackgroundColor - ? undefined - : { backgroundColor: opacify(isDarkMode ? 10 : 100, colors.sporeWhite.val) } + hasBackgroundColor ? undefined : { backgroundColor: opacify(isDarkMode ? 10 : 100, colors.sporeWhite.val) } } - {...rest}> + {...rest} + > {children} ) @@ -56,7 +47,8 @@ function Header({ title, subtitle, onPress, icon, ...buttonProps }: HeaderProps) px="$spacing16" py="$spacing12" onPress={onPress} - {...buttonProps}> + {...buttonProps} + > @@ -69,13 +61,7 @@ function Header({ title, subtitle, onPress, icon, ...buttonProps }: HeaderProps) title )} - {subtitle ? ( - typeof subtitle === 'string' ? ( - {subtitle} - ) : ( - subtitle - ) - ) : null} + {subtitle ? typeof subtitle === 'string' ? {subtitle} : subtitle : null} @@ -149,13 +135,7 @@ type ErrorStateProps = { function ErrorState(props: ErrorStateProps): JSX.Element { const { t } = useTranslation() - const { - title, - description = t('common.card.error.description'), - retryButtonLabel, - onRetry, - icon, - } = props + const { title, description = t('common.card.error.description'), retryButtonLabel, onRetry, icon } = props return ( @@ -210,15 +190,11 @@ function InlineErrorState(props: InlineErrorStateProps): JSX.Element { gap="$spacing24" justifyContent="space-between" p="$spacing12" - width="100%"> + width="100%" + > {icon} - + {title} diff --git a/packages/uniswap/src/components/CurrencyLogo/NetworkLogo.test.tsx b/packages/uniswap/src/components/CurrencyLogo/NetworkLogo.test.tsx index e6efd1c7887..335b5d30faf 100644 --- a/packages/uniswap/src/components/CurrencyLogo/NetworkLogo.test.tsx +++ b/packages/uniswap/src/components/CurrencyLogo/NetworkLogo.test.tsx @@ -1,7 +1,4 @@ -import { - NetworkLogo, - TransactionSummaryNetworkLogo, -} from 'uniswap/src/components/CurrencyLogo/NetworkLogo' +import { NetworkLogo, TransactionSummaryNetworkLogo } from 'uniswap/src/components/CurrencyLogo/NetworkLogo' import { render } from 'uniswap/src/test/test-utils' import { UniverseChainId } from 'uniswap/src/types/chains' diff --git a/packages/uniswap/src/components/CurrencyLogo/NetworkLogo.tsx b/packages/uniswap/src/components/CurrencyLogo/NetworkLogo.tsx index 40a4fe62a75..da2b78166a4 100644 --- a/packages/uniswap/src/components/CurrencyLogo/NetworkLogo.tsx +++ b/packages/uniswap/src/components/CurrencyLogo/NetworkLogo.tsx @@ -20,24 +20,19 @@ export function TransactionSummaryNetworkLogo({ + borderWidth={2} + > ) } -function _NetworkLogo({ - chainId, - shape, - size = iconSizes.icon20, -}: NetworkLogoProps): JSX.Element | null { +function _NetworkLogo({ chainId, shape, size = iconSizes.icon20 }: NetworkLogoProps): JSX.Element | null { const logo = UNIVERSE_CHAIN_INFO[chainId].logo const colors = useSporeColors() const borderRadius = shape === 'circle' ? size / 2 : SQUARE_BORDER_RADIUS return logo ? ( - + ) : null diff --git a/packages/uniswap/src/components/CurrencyLogo/TokenLogo.test.tsx b/packages/uniswap/src/components/CurrencyLogo/TokenLogo.test.tsx index cc323b27090..ee2908fff87 100644 --- a/packages/uniswap/src/components/CurrencyLogo/TokenLogo.test.tsx +++ b/packages/uniswap/src/components/CurrencyLogo/TokenLogo.test.tsx @@ -8,9 +8,7 @@ jest.mock('utilities/src/logger/logger') describe('TokenLogo', () => { it('renders without error', () => { - const tree = render( - - ) + const tree = render() expect(tree).toMatchSnapshot() }) @@ -18,11 +16,7 @@ describe('TokenLogo', () => { describe('token image', () => { it('renders svg when url is svg', () => { const { queryByTestId } = render( - + , ) const tokenRemoteSvg = queryByTestId('svg-token-image') @@ -34,11 +28,7 @@ describe('TokenLogo', () => { it('renders image when url is valid and not svg', () => { const { queryByTestId } = render( - + , ) const tokenRemoteSvg = queryByTestId('svg-token-image') @@ -49,9 +39,7 @@ describe('TokenLogo', () => { }) it('renders fallback text when url is not specified', () => { - const { queryByText } = render( - - ) + const { queryByText } = render() const fallbackText = queryByText('DAI') @@ -59,9 +47,7 @@ describe('TokenLogo', () => { }) it('renders fallback text when url is invalid', () => { - const { queryByText } = render( - - ) + const { queryByText } = render() const fallbackText = queryByText('DAI') @@ -74,7 +60,7 @@ describe('TokenLogo', () => { chainId={UniverseChainId.ArbitrumOne} symbol="DAI" url="https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png" - /> + />, ) const fallbackText = queryByText('DAI') @@ -86,7 +72,7 @@ describe('TokenLogo', () => { describe('network logo', () => { it('renders network logo by default', () => { const { queryByTestId } = render( - + , ) const networkLogo = queryByTestId('network-logo') @@ -101,7 +87,7 @@ describe('TokenLogo', () => { hideNetworkLogo={false} symbol="DAI" url="https://example.com" - /> + />, ) const networkLogo = queryByTestId('network-logo') @@ -111,12 +97,7 @@ describe('TokenLogo', () => { it('does not render network logo when hideNetworkLogo is true', () => { const { queryByTestId } = render( - + , ) const networkLogo = queryByTestId('network-logo') @@ -134,7 +115,7 @@ describe('TokenLogo', () => { it('does not render network logo when chainId is Mainnet', () => { const { queryByTestId } = render( - + , ) const networkLogo = queryByTestId('network-logo') diff --git a/packages/uniswap/src/components/CurrencyLogo/TokenLogo.tsx b/packages/uniswap/src/components/CurrencyLogo/TokenLogo.tsx index 4d433538c92..e260bf3278e 100644 --- a/packages/uniswap/src/components/CurrencyLogo/TokenLogo.tsx +++ b/packages/uniswap/src/components/CurrencyLogo/TokenLogo.tsx @@ -31,9 +31,7 @@ export const TokenLogo = memo(function _TokenLogo({ const showNetworkLogo = !hideNetworkLogo && chainId && chainId !== UniverseChainId.Mainnet - const { foreground, background } = isDarkMode - ? logolessColorScheme.dark - : logolessColorScheme.light + const { foreground, background } = isDarkMode ? logolessColorScheme.dark : logolessColorScheme.light const fallback = ( + width={size} + > + numberOfLines={1} + > {symbol?.slice(0, 3)} @@ -74,12 +74,7 @@ export const TokenLogo = memo(function _TokenLogo({ ) return ( - + {tokenImage} {showNetworkLogo && ( + right={-3} + > )} diff --git a/packages/wallet/src/components/TokenSelector/SuggestedToken.tsx b/packages/uniswap/src/components/TokenSelector/SuggestedToken.tsx similarity index 91% rename from packages/wallet/src/components/TokenSelector/SuggestedToken.tsx rename to packages/uniswap/src/components/TokenSelector/SuggestedToken.tsx index b0cb5dac976..442804be6a8 100644 --- a/packages/wallet/src/components/TokenSelector/SuggestedToken.tsx +++ b/packages/uniswap/src/components/TokenSelector/SuggestedToken.tsx @@ -3,12 +3,8 @@ import { ImpactFeedbackStyle, isWeb, TouchableArea, useSporeColors } from 'ui/sr import { iconSizes } from 'ui/src/theme' import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' import { Pill } from 'uniswap/src/components/pill/Pill' +import { OnSelectCurrency, SuggestedTokenSection, TokenOption } from 'uniswap/src/components/TokenSelector/types' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' -import { - OnSelectCurrency, - SuggestedTokenSection, - TokenOption, -} from 'wallet/src/components/TokenSelector/types' function _SuggestedToken({ onSelectCurrency, @@ -31,7 +27,8 @@ function _SuggestedToken({ hapticFeedback hapticStyle={ImpactFeedbackStyle.Light} testID={`token-option-${currency.chainId}-${currency.symbol}`} - onPress={onPress}> + onPress={onPress} + > diff --git a/packages/wallet/src/components/TokenSelector/TokenSectionBaseList.web.tsx b/packages/uniswap/src/components/TokenSelector/TokenSectionBaseList.web.tsx similarity index 86% rename from packages/wallet/src/components/TokenSelector/TokenSectionBaseList.web.tsx rename to packages/uniswap/src/components/TokenSelector/TokenSectionBaseList.web.tsx index 6336e8a969c..d85abfcf464 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSectionBaseList.web.tsx +++ b/packages/uniswap/src/components/TokenSelector/TokenSectionBaseList.web.tsx @@ -8,25 +8,19 @@ import { ItemRowInfo, SectionRowInfo, TokenSectionBaseListProps, -} from 'wallet/src/components/TokenSelector/TokenSectionBaseList' +} from 'uniswap/src/components/TokenSelector/TokenSectionBaseList' const ITEM_ROW_HEIGHT = 72 type BaseListRowInfo = { key: Key | undefined } -type BaseListSectionRowInfo = SectionRowInfo & - BaseListRowInfo & - Pick -type BaseListItemRowInfo = ItemRowInfo & - BaseListRowInfo & - Pick +type BaseListSectionRowInfo = SectionRowInfo & BaseListRowInfo & Pick +type BaseListItemRowInfo = ItemRowInfo & BaseListRowInfo & Pick type BaseListData = BaseListItemRowInfo | BaseListSectionRowInfo -function isSectionHeader( - rowInfo: BaseListSectionRowInfo | BaseListItemRowInfo -): rowInfo is BaseListSectionRowInfo { +function isSectionHeader(rowInfo: BaseListSectionRowInfo | BaseListItemRowInfo): rowInfo is BaseListSectionRowInfo { return !('renderItem' in rowInfo) } @@ -79,7 +73,7 @@ export function TokenSectionBaseList({ renderItem, } return itemInfo - }) + }), ) }, []) }, [sections, renderSectionHeader, keyExtractor, renderItem]) @@ -111,7 +105,7 @@ export function TokenSectionBaseList({ } return ITEM_ROW_HEIGHT }, - [items] + [items], ) const ListContent = useCallback( @@ -126,7 +120,7 @@ export function TokenSectionBaseList({ /> ) }, - [updateRowHeight, windowWidth] + [updateRowHeight, windowWidth], ) return ( @@ -138,13 +132,10 @@ export function TokenSectionBaseList({ + style={{ position: 'absolute', top: 0, zIndex: zIndices.sticky, width: '100%' }} + > {activeSessionIndex >= 0 && ( - + )} { setFirstVisibleIndex(visibleStartIndex) - }}> + }} + > {ListContent} @@ -218,9 +210,7 @@ function _Row({ index, itemData, style, windowWidth, updateRowHeight }: RowProps {itemData && - (isSectionHeader(itemData) - ? itemData.renderSectionHeader?.(itemData) - : itemData.renderItem(itemData))} + (isSectionHeader(itemData) ? itemData.renderSectionHeader?.(itemData) : itemData.renderItem(itemData))} ) diff --git a/packages/wallet/src/components/TokenSelector/filter.ts b/packages/uniswap/src/components/TokenSelector/filter.ts similarity index 93% rename from packages/wallet/src/components/TokenSelector/filter.ts rename to packages/uniswap/src/components/TokenSelector/filter.ts index 639f9b7ef1d..a46bda2535d 100644 --- a/packages/wallet/src/components/TokenSelector/filter.ts +++ b/packages/uniswap/src/components/TokenSelector/filter.ts @@ -1,6 +1,6 @@ import Fuse from 'fuse.js' +import { TokenOption } from 'uniswap/src/components/TokenSelector/types' import { UniverseChainId } from 'uniswap/src/types/chains' -import { TokenOption } from 'wallet/src/components/TokenSelector/types' const searchOptions: Fuse.IFuseOptions = { includeMatches: true, @@ -17,7 +17,7 @@ const searchOptions: Fuse.IFuseOptions = { } const getChainSearchPattern = ( - chain: UniverseChainId | null + chain: UniverseChainId | null, ): { 'currencyInfo.currency.chainId': string } | null => @@ -27,7 +27,7 @@ const getChainSearchPattern = ( : null const getAddressSearchPattern = ( - addressPrefix?: string + addressPrefix?: string, ): { 'currencyInfo.currency.address': string } | null => @@ -37,7 +37,7 @@ const getAddressSearchPattern = ( : null const getSymbolSearchPattern = ( - symbol?: string + symbol?: string, ): { 'currencyInfo.currency.symbol': string } | null => @@ -47,7 +47,7 @@ const getSymbolSearchPattern = ( : null const getNameSearchPattern = ( - name?: string + name?: string, ): { 'currencyInfo.currency.name': string } | null => @@ -65,7 +65,7 @@ const getNameSearchPattern = ( export function filter( tokenOptions: TokenOption[] | null, chainFilter: UniverseChainId | null, - searchFilter?: string + searchFilter?: string, ): TokenOption[] { if (!tokenOptions || !tokenOptions.length) { return [] diff --git a/packages/wallet/src/components/TokenSelector/renderSuggestedTokenItem.tsx b/packages/uniswap/src/components/TokenSelector/renderSuggestedTokenItem.tsx similarity index 75% rename from packages/wallet/src/components/TokenSelector/renderSuggestedTokenItem.tsx rename to packages/uniswap/src/components/TokenSelector/renderSuggestedTokenItem.tsx index 21672138bc7..3ee4eec81e0 100644 --- a/packages/wallet/src/components/TokenSelector/renderSuggestedTokenItem.tsx +++ b/packages/uniswap/src/components/TokenSelector/renderSuggestedTokenItem.tsx @@ -1,10 +1,6 @@ import { Flex } from 'ui/src' -import { SuggestedToken } from 'wallet/src/components/TokenSelector/SuggestedToken' -import { - OnSelectCurrency, - SuggestedTokenSection, - TokenOption, -} from 'wallet/src/components/TokenSelector/types' +import { SuggestedToken } from 'uniswap/src/components/TokenSelector/SuggestedToken' +import { OnSelectCurrency, SuggestedTokenSection, TokenOption } from 'uniswap/src/components/TokenSelector/types' export function renderSuggestedTokenItem({ item: suggestedTokens, diff --git a/packages/wallet/src/components/TokenSelector/suggestedTokensKeyExtractor.tsx b/packages/uniswap/src/components/TokenSelector/suggestedTokensKeyExtractor.tsx similarity index 69% rename from packages/wallet/src/components/TokenSelector/suggestedTokensKeyExtractor.tsx rename to packages/uniswap/src/components/TokenSelector/suggestedTokensKeyExtractor.tsx index ed7a61d82b8..d1e8fb6273e 100644 --- a/packages/wallet/src/components/TokenSelector/suggestedTokensKeyExtractor.tsx +++ b/packages/uniswap/src/components/TokenSelector/suggestedTokensKeyExtractor.tsx @@ -1,4 +1,4 @@ -import { TokenOption } from 'wallet/src/components/TokenSelector/types' +import { TokenOption } from 'uniswap/src/components/TokenSelector/types' export function suggestedTokensKeyExtractor(suggestedTokens: TokenOption[]): string { return suggestedTokens.map((token) => token.currencyInfo.currencyId).join('-') diff --git a/packages/wallet/src/components/TokenSelector/types.ts b/packages/uniswap/src/components/TokenSelector/types.ts similarity index 97% rename from packages/wallet/src/components/TokenSelector/types.ts rename to packages/uniswap/src/components/TokenSelector/types.ts index 77620ac39bf..c92646d96ba 100644 --- a/packages/wallet/src/components/TokenSelector/types.ts +++ b/packages/uniswap/src/components/TokenSelector/types.ts @@ -9,7 +9,7 @@ export type TokenOption = { export type OnSelectCurrency = ( currency: CurrencyInfo, section: SuggestedTokenSection | TokenSection, - index: number + index: number, ) => void export type TokenSection = { diff --git a/packages/wallet/src/components/TokenSelector/utils.ts b/packages/uniswap/src/components/TokenSelector/utils.ts similarity index 81% rename from packages/wallet/src/components/TokenSelector/utils.ts rename to packages/uniswap/src/components/TokenSelector/utils.ts index 70349e8b4c6..811798f69e8 100644 --- a/packages/wallet/src/components/TokenSelector/utils.ts +++ b/packages/uniswap/src/components/TokenSelector/utils.ts @@ -1,7 +1,7 @@ +import { TokenOption, TokenSection } from 'uniswap/src/components/TokenSelector/types' import { CurrencyInfo, PortfolioBalance } from 'uniswap/src/features/dataApi/types' +import { areCurrencyIdsEqual } from 'uniswap/src/utils/currencyId' import { differenceWith } from 'utilities/src/primitives/array' -import { TokenOption, TokenSection } from 'wallet/src/components/TokenSelector/types' -import { areCurrencyIdsEqual } from 'wallet/src/utils/currencyId' export function createEmptyBalanceOption(currencyInfo: CurrencyInfo): TokenOption { return { @@ -15,7 +15,7 @@ export function createEmptyBalanceOption(currencyInfo: CurrencyInfo): TokenOptio // e.g. difference([B, C, D], [A, B, C]) would return ([D]) export function tokenOptionDifference( currencies: TokenOption[] | undefined, - without: TokenOption[] | undefined + without: TokenOption[] | undefined, ): TokenOption[] | undefined { if (!currencies) { return undefined @@ -24,25 +24,20 @@ export function tokenOptionDifference( } function tokenOptionComparator(tokenOption: TokenOption, otherTokenOption: TokenOption): boolean { - return areCurrencyIdsEqual( - tokenOption.currencyInfo.currencyId, - otherTokenOption.currencyInfo.currencyId - ) + return areCurrencyIdsEqual(tokenOption.currencyInfo.currencyId, otherTokenOption.currencyInfo.currencyId) } export function formatSearchResults( searchResultCurrencies: CurrencyInfo[] | undefined, portfolioBalancesById: Record | undefined, - searchFilter: string | null + searchFilter: string | null, ): TokenOption[] | undefined { if (!searchResultCurrencies) { return } const formattedOptions = searchResultCurrencies.map((currencyInfo): TokenOption => { - return ( - portfolioBalancesById?.[currencyInfo.currencyId] ?? createEmptyBalanceOption(currencyInfo) - ) + return portfolioBalancesById?.[currencyInfo.currencyId] ?? createEmptyBalanceOption(currencyInfo) }) // Sort to bring exact matches to the top @@ -72,7 +67,7 @@ function isExactTokenOptionMatch(searchResult: TokenOption, query: string): bool export function getTokenOptionsSection( title: string, tokenOptions?: TokenOption[], - rightElement?: JSX.Element + rightElement?: JSX.Element, ): TokenSection[] | undefined { return tokenOptions?.length ? [ diff --git a/packages/uniswap/src/components/dropdowns/ActionSheetDropdown.test.tsx b/packages/uniswap/src/components/dropdowns/ActionSheetDropdown.test.tsx index b188ddc34eb..b04ea14bb06 100644 --- a/packages/uniswap/src/components/dropdowns/ActionSheetDropdown.test.tsx +++ b/packages/uniswap/src/components/dropdowns/ActionSheetDropdown.test.tsx @@ -14,7 +14,7 @@ jest.mock('react-native', () => { const MockedView = actualReactNative.View MockedView.prototype.measureInWindow = ( - callback: (x: number, y: number, width: number, height: number) => void + callback: (x: number, y: number, width: number, height: number) => void, ): void => { // Provide mock measurements const mockX = 0 diff --git a/packages/uniswap/src/components/dropdowns/ActionSheetDropdown.tsx b/packages/uniswap/src/components/dropdowns/ActionSheetDropdown.tsx index 1582035f152..4c2c3b24d89 100644 --- a/packages/uniswap/src/components/dropdowns/ActionSheetDropdown.tsx +++ b/packages/uniswap/src/components/dropdowns/ActionSheetDropdown.tsx @@ -1,6 +1,6 @@ -/* eslint-disable no-restricted-imports */ import { PropsWithChildren, useMemo, useRef, useState } from 'react' -import { Platform, type View } from 'react-native' +/* eslint-disable-next-line no-restricted-imports */ +import { type View } from 'react-native' import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated' import { AnimatePresence, @@ -19,6 +19,7 @@ import { spacing, zIndices } from 'ui/src/theme' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' import { Scrollbar } from 'uniswap/src/components/misc/Scrollbar' import { MenuItemProp } from 'uniswap/src/components/modals/ActionSheetModal' +import { isAndroid } from 'utilities/src/platform' const DEFAULT_MIN_WIDTH = 225 @@ -38,12 +39,14 @@ type ActionSheetDropdownProps = PropsWithChildren<{ options: MenuItemProp[] alignment?: 'left' | 'right' backdropOpacity?: number + testID?: string onDismiss?: () => void }> export function ActionSheetDropdown({ children, backdropOpacity, + testID, onDismiss, ...contentProps }: ActionSheetDropdownProps): JSX.Element { @@ -65,7 +68,7 @@ export function ActionSheetDropdown({ isOpen: true, toggleMeasurements: { x, - y: y + (Platform.OS === 'android' ? insets.top : 0), + y: y + (isAndroid ? insets.top : 0), width, height, }, @@ -80,14 +83,10 @@ export function ActionSheetDropdown({ return ( <> - + {/* collapsable property prevents removing view on Android. Without this property we were getting undefined in measureInWindow callback. (https://reactnative.dev/docs/view.html#collapsable-android) */} - + {children} @@ -98,11 +97,7 @@ export function ActionSheetDropdown({ {isOpen && toggleMeasurements && ( <> - + )} @@ -167,10 +162,7 @@ function DropdownContent({ }, [alignment, fullWidth, toggleMeasurements]) const bottomOffset = insets.bottom + spacing.spacing12 - const maxHeight = Math.max( - fullHeight - toggleMeasurements.y - toggleMeasurements.height - bottomOffset, - 0 - ) + const maxHeight = Math.max(fullHeight - toggleMeasurements.y - toggleMeasurements.height - bottomOffset, 0) const overflowsContainer = contentHeight > maxHeight return ( @@ -196,7 +188,8 @@ function DropdownContent({ position="absolute" testID="dropdown-content" top={toggleMeasurements.y + toggleMeasurements.height} - {...containerProps}> + {...containerProps} + > + onScroll={scrollHandler} + > { setContentHight(height) - }}> + }} + > {options.map(({ key, onPress, render }: MenuItemProp) => ( { onPress() handleClose?.() - }}> - {render()} + }} + > + {render()} ))} diff --git a/packages/wallet/src/components/icons/WarningIcon.tsx b/packages/uniswap/src/components/icons/WarningIcon.tsx similarity index 91% rename from packages/wallet/src/components/icons/WarningIcon.tsx rename to packages/uniswap/src/components/icons/WarningIcon.tsx index 20aed8022ab..3d97586cd58 100644 --- a/packages/wallet/src/components/icons/WarningIcon.tsx +++ b/packages/uniswap/src/components/icons/WarningIcon.tsx @@ -1,7 +1,7 @@ import { IconProps, useSporeColors } from 'ui/src' import { AlertTriangle, XOctagon } from 'ui/src/components/icons' import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { useTokenSafetyLevelColors } from 'wallet/src/features/tokens/safetyHooks' +import { useTokenSafetyLevelColors } from 'uniswap/src/features/tokens/safetyHooks' interface Props { safetyLevel: Maybe diff --git a/packages/uniswap/src/components/input/TextInput.tsx b/packages/uniswap/src/components/input/TextInput.tsx index 9081df418e1..4a8ef4b1a90 100644 --- a/packages/uniswap/src/components/input/TextInput.tsx +++ b/packages/uniswap/src/components/input/TextInput.tsx @@ -7,7 +7,7 @@ export type TextInputProps = InputProps export const TextInput = forwardRef(function _TextInput( { onChangeText, onBlur, ...rest }, - ref + ref, ) { const colors = useSporeColors() return ( diff --git a/packages/uniswap/src/components/misc/ActionCard.tsx b/packages/uniswap/src/components/misc/ActionCard.tsx new file mode 100644 index 00000000000..19e2a82b100 --- /dev/null +++ b/packages/uniswap/src/components/misc/ActionCard.tsx @@ -0,0 +1,60 @@ +import { Flex, Text, TouchableArea } from 'ui/src' +import Trace from 'uniswap/src/features/telemetry/Trace' +import { ElementNameType } from 'uniswap/src/features/telemetry/constants' + +export interface ActionCardItem { + title: string + blurb: string + icon: JSX.Element + elementName: ElementNameType + badgeText?: string + onPress?: () => void + BackgroundImageWrapperCallback?: React.FC<{ children: React.ReactNode }> +} + +export const ActionCard = ({ + title, + blurb, + onPress, + icon, + elementName, + BackgroundImageWrapperCallback, +}: ActionCardItem): JSX.Element => ( + + + + + {icon} + + + {title} + + + {blurb} + + + + + + +) + +const BackgroundWrapper = ({ + children, + BackgroundImageWrapper, +}: { + children: React.ReactNode + BackgroundImageWrapper?: React.FC<{ children: React.ReactNode }> +}): JSX.Element => { + return BackgroundImageWrapper !== undefined ? ( + {children} + ) : ( + {children} + ) +} diff --git a/packages/uniswap/src/components/misc/Scrollbar.tsx b/packages/uniswap/src/components/misc/Scrollbar.tsx index 0cd4727a8f6..2104e0976a7 100644 --- a/packages/uniswap/src/components/misc/Scrollbar.tsx +++ b/packages/uniswap/src/components/misc/Scrollbar.tsx @@ -13,12 +13,7 @@ type ScrollbarProps = FlexProps & { scrollOffset: SharedValue } -export function Scrollbar({ - visibleHeight, - contentHeight, - scrollOffset, - ...rest -}: ScrollbarProps): JSX.Element { +export function Scrollbar({ visibleHeight, contentHeight, scrollOffset, ...rest }: ScrollbarProps): JSX.Element { const scrollbarHeight = useSharedValue(0) const animatedThumbStyle = useAnimatedStyle(() => { @@ -29,7 +24,7 @@ export function Scrollbar({ scrollOffset.value, [0, contentHeight - visibleHeight], [0, scrollbarHeight.value - thumbHeight], - Extrapolate.CLAMP + Extrapolate.CLAMP, ), height: thumbHeight, } @@ -43,7 +38,8 @@ export function Scrollbar({ width: 0, }} width={6} - {...rest}> + {...rest} + > { scrollbarHeight.value = height - }}> + }} + > diff --git a/packages/uniswap/src/components/modals/ActionSheetModal.tsx b/packages/uniswap/src/components/modals/ActionSheetModal.tsx index 7d1874e528b..840819c7b19 100644 --- a/packages/uniswap/src/components/modals/ActionSheetModal.tsx +++ b/packages/uniswap/src/components/modals/ActionSheetModal.tsx @@ -65,22 +65,13 @@ interface ActionSheetModalProps extends ActionSheetModalContentProps { name: ModalNameType } -export function ActionSheetModal({ - isVisible, - onClose, - name, - ...rest -}: ActionSheetModalProps): JSX.Element | null { +export function ActionSheetModal({ isVisible, onClose, name, ...rest }: ActionSheetModalProps): JSX.Element | null { if (!isVisible) { return null } return ( - + ) diff --git a/packages/uniswap/src/components/modals/BottomSheetContext.tsx b/packages/uniswap/src/components/modals/BottomSheetContext.tsx index 2b6f1ef0f72..ed0a433f160 100644 --- a/packages/uniswap/src/components/modals/BottomSheetContext.tsx +++ b/packages/uniswap/src/components/modals/BottomSheetContext.tsx @@ -21,7 +21,7 @@ export function BottomSheetContextProvider({ (): BottomSheetContextState => ({ isSheetReady, }), - [isSheetReady] + [isSheetReady], ) return {children} diff --git a/packages/uniswap/src/components/modals/BottomSheetModal.native.tsx b/packages/uniswap/src/components/modals/BottomSheetModal.native.tsx index 923c23eb717..bcff3ff75d2 100644 --- a/packages/uniswap/src/components/modals/BottomSheetModal.native.tsx +++ b/packages/uniswap/src/components/modals/BottomSheetModal.native.tsx @@ -10,12 +10,7 @@ import { import { BlurView } from 'expo-blur' import React, { ComponentProps, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { BackHandler, Keyboard, StyleProp, StyleSheet, ViewStyle } from 'react-native' -import Animated, { - Extrapolate, - interpolate, - useAnimatedStyle, - useSharedValue, -} from 'react-native-reanimated' +import Animated, { Extrapolate, interpolate, useAnimatedStyle, useSharedValue } from 'react-native-reanimated' import { Flex, useDeviceInsets, useIsDarkMode, useMedia, useSporeColors } from 'ui/src' import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' import { borderRadii, spacing } from 'ui/src/theme' @@ -24,7 +19,7 @@ import { BottomSheetModalProps } from 'uniswap/src/components/modals/BottomSheet import { HandleBar } from 'uniswap/src/components/modals/HandleBar' import Trace from 'uniswap/src/features/telemetry/Trace' import { useKeyboardLayout } from 'uniswap/src/utils/useKeyboardLayout' -import { isAndroid, isIOS } from 'utilities/src/platform' +import { isIOS } from 'utilities/src/platform' /** * (android only) @@ -48,7 +43,6 @@ function useModalBackHandler(modalRef: React.RefObject, enabled: bool const BACKDROP_APPEARS_ON_INDEX = 0 const DISAPPEARS_ON_INDEX = -1 -const DRAG_ACTIVATION_OFFSET = 40 const Backdrop = (props: BottomSheetBackdropProps): JSX.Element => { return ( @@ -112,7 +106,7 @@ function BottomSheetModalContents({ const snapPoints = useMemo( () => providedSnapPoints ?? (fullScreen ? ['100%'] : undefined), - [providedSnapPoints, fullScreen] + [providedSnapPoints, fullScreen], ) useModalBackHandler(modalRef, isDismissible && dismissOnBackPress) @@ -131,9 +125,7 @@ function BottomSheetModalContents({ const animatedPosition = providedAnimatedPosition ?? internalAnimatedPosition - const backgroundColorValue = blurredBackground - ? colors.transparent.val - : backgroundColor ?? colors.surface1.get() + const backgroundColorValue = blurredBackground ? colors.transparent.val : backgroundColor ?? colors.surface1.get() const renderBackdrop = useCallback( (props: BottomSheetBackdropProps) => ( @@ -145,7 +137,7 @@ function BottomSheetModalContents({ pressBehavior={isDismissible ? 'close' : 'none'} /> ), - [blurredBackground, hideScrim, isDismissible] + [blurredBackground, hideScrim, isDismissible], ) const renderHandleBar = useCallback( @@ -166,7 +158,7 @@ function BottomSheetModalContents({ /> ) }, - [backgroundColorValue, hideHandlebar, renderBehindTopInset] + [backgroundColorValue, hideHandlebar, renderBehindTopInset], ) const animatedBorderRadius = useAnimatedStyle(() => { @@ -174,7 +166,7 @@ function BottomSheetModalContents({ animatedPosition.value, [0, insets.top], [0, borderRadius ?? borderRadii.rounded24], - Extrapolate.CLAMP + Extrapolate.CLAMP, ) return { borderTopLeftRadius: interpolatedRadius, borderTopRightRadius: interpolatedRadius } }) @@ -183,17 +175,13 @@ function BottomSheetModalContents({ () => ( {isIOS ? ( - + ) : ( )} ), - [isDarkMode, animatedBorderRadius] + [isDarkMode, animatedBorderRadius], ) // onAnimate is called when the sheet is about to animate to a new position. @@ -216,7 +204,7 @@ function BottomSheetModalContents({ setTimeout(() => setIsSheetReady(true), 50) } }, - [hideKeyboardOnDismiss, hideKeyboardOnSwipeDown, isSheetReady] + [hideKeyboardOnDismiss, hideKeyboardOnSwipeDown, isSheetReady], ) // on screens < xs (iPhone SE), assume no rounded corners on screen and remove rounded corners from fullscreen modal @@ -236,9 +224,7 @@ function BottomSheetModalContents({ const bottomSheetViewStyles: StyleProp = [{ backgroundColor: backgroundColorValue }] - const handleBarHeight = hideHandlebar - ? 0 - : spacing.spacing12 + spacing.spacing16 + spacing.spacing4 + const handleBarHeight = hideHandlebar ? 0 : spacing.spacing12 + spacing.spacing16 + spacing.spacing4 let fullContentHeight = dimensions.fullHeight - insets.top - handleBarHeight if (renderBehindTopInset) { @@ -264,10 +250,6 @@ function BottomSheetModalContents({ {...background} {...backdrop} ref={modalRef} - // This is required for android to make scrollable containers work - // and allow closing the modal by dragging the content - // (adding this property on iOS breaks closing the modal by dragging the content) - activeOffsetY={isAndroid ? [-DRAG_ACTIVATION_OFFSET, DRAG_ACTIVATION_OFFSET] : undefined} animatedPosition={animatedPosition} backgroundStyle={backgroundStyle} containerComponent={containerComponent} @@ -280,7 +262,8 @@ function BottomSheetModalContents({ stackBehavior={stackBehavior} topInset={renderBehindTopInset ? 0 : insets.top} onAnimate={onAnimate} - onDismiss={onClose}> + onDismiss={onClose} + > {overrideInnerContainer ? ( @@ -323,7 +306,7 @@ export function BottomSheetDetachedModal({ (props: BottomSheetHandleProps) => { return } label="My Second Pill Label" />) expect(tree).toMatchSnapshot() }) diff --git a/packages/uniswap/src/components/pill/Pill.tsx b/packages/uniswap/src/components/pill/Pill.tsx index 7eddc217340..3566b8ed600 100644 --- a/packages/uniswap/src/components/pill/Pill.tsx +++ b/packages/uniswap/src/components/pill/Pill.tsx @@ -40,7 +40,8 @@ export function Pill({ ...(customBackgroundColor ? { backgroundColor: customBackgroundColor } : {}), ...(customBorderColor ? { borderColor: customBorderColor } : {}), }} - {...rest}> + {...rest} + > {icon ?? null} {label ? ( diff --git a/packages/uniswap/src/config.ts b/packages/uniswap/src/config.ts index b371555709a..13d6208a6a0 100644 --- a/packages/uniswap/src/config.ts +++ b/packages/uniswap/src/config.ts @@ -22,7 +22,7 @@ import { UNISWAP_API_KEY, WALLETCONNECT_PROJECT_ID, } from 'react-native-dotenv' -import { isNonJestDev } from 'utilities/src/environment' +import { isNonJestDev } from 'utilities/src/environment/constants' export interface Config { appsflyerApiKey: string @@ -48,13 +48,21 @@ export interface Config { firebaseAppCheckDebugToken: string } +/** + * Naming requirements for different environments: + * - Web ENV vars: must have process.env.REACT_APP_ + * - Extension ENV vars: must have process.env. + * - Mobile ENV vars: must have BOTH process.env. and + * + * The CI requires web vars to have the required 'REACT_APP_' prefix. The react-dot-env library doesnt integrate with CI correctly, + * so we pull from github secrets directly with process.env. for both extension and mobile. is used for local mobile builds. + */ + const _config: Config = { appsflyerApiKey: process.env.APPSFLYER_API_KEY || APPSFLYER_API_KEY, appsflyerAppId: process.env.APPSFLYER_APP_ID || APPSFLYER_APP_ID, - moonpayApiKey: - process.env.REACT_APP_MOONPAY_PUBLISHABLE_KEY || process.env.MOONPAY_API_KEY || MOONPAY_API_KEY, - moonpayApiUrl: - process.env.REACT_APP_MOONPAY_API || process.env.MOONPAY_API_URL || MOONPAY_API_URL, + moonpayApiKey: process.env.REACT_APP_MOONPAY_PUBLISHABLE_KEY || process.env.MOONPAY_API_KEY || MOONPAY_API_KEY, + moonpayApiUrl: process.env.REACT_APP_MOONPAY_API || process.env.MOONPAY_API_URL || MOONPAY_API_URL, moonpayWidgetApiUrl: process.env.MOONPAY_WIDGET_API_URL || MOONPAY_WIDGET_API_URL, uniswapApiKey: process.env.UNISWAP_API_KEY || UNISWAP_API_KEY, infuraKey: process.env.REACT_APP_INFURA_KEY || INFURA_KEY, @@ -63,22 +71,19 @@ const _config: Config = { sentryDsn: process.env.REACT_APP_SENTRY_DSN || process.env.SENTRY_DSN || SENTRY_DSN, simpleHashApiKey: process.env.SIMPLEHASH_API_KEY || SIMPLEHASH_API_KEY, simpleHashApiUrl: process.env.SIMPLEHASH_API_URL || SIMPLEHASH_API_URL, - statSigProxyUrl: - process.env.REACT_APP_STATSIG_PROXY_URL || process.env.STATSIG_PROXY_URL || STATSIG_PROXY_URL, + statSigProxyUrl: process.env.REACT_APP_STATSIG_PROXY_URL || process.env.STATSIG_PROXY_URL || STATSIG_PROXY_URL, walletConnectProjectId: - process.env.REACT_APP_WALLET_CONNECT_PROJECT_ID || - process.env.WALLETCONNECT_PROJECT_ID || - WALLETCONNECT_PROJECT_ID, - quicknodeArbitrumRpcUrl: - process.env.REACT_APP_QUICKNODE_ARBITRUM_RPC_URL || QUICKNODE_ARBITRUM_RPC_URL, - quicknodeBnbRpcUrl: process.env.REACT_APP_QUICKNODE_BNB_RPC_URL || QUICKNODE_BNB_RPC_URL, - quicknodeZoraRpcUrl: process.env.REACT_APP_QUICKNODE_ZORA_RPC_URL || QUICKNODE_ZORA_RPC_URL, - quicknodeZkSyncRpcUrl: process.env.REACT_APP_QUICKNODE_ZKSYNC_RPC_URL || QUICKNODE_ZKSYNC_RPC_URL, - quicknodeMainnetRpcUrl: - process.env.REACT_APP_QUICKNODE_MAINNET_RPC_URL || QUICKNODE_MAINNET_RPC_URL, + process.env.REACT_APP_WALLET_CONNECT_PROJECT_ID || process.env.WALLETCONNECT_PROJECT_ID || WALLETCONNECT_PROJECT_ID, + quicknodeArbitrumRpcUrl: process.env.REACT_APP_QUICKNODE_ARBITRUM_RPC_URL || QUICKNODE_ARBITRUM_RPC_URL, + quicknodeBnbRpcUrl: + process.env.REACT_APP_QUICKNODE_BNB_RPC_URL || process.env.QUICKNODE_BNB_RPC_URL || QUICKNODE_BNB_RPC_URL, + quicknodeZoraRpcUrl: + process.env.REACT_APP_QUICKNODE_ZORA_RPC_URL || process.env.QUICKNODE_ZORA_RPC_URL || QUICKNODE_ZORA_RPC_URL, + quicknodeZkSyncRpcUrl: + process.env.REACT_APP_QUICKNODE_ZKSYNC_RPC_URL || process.env.QUICKNODE_ZKSYNC_RPC_URL || QUICKNODE_ZKSYNC_RPC_URL, + quicknodeMainnetRpcUrl: process.env.REACT_APP_QUICKNODE_MAINNET_RPC_URL || QUICKNODE_MAINNET_RPC_URL, tradingApiKey: process.env.TRADING_API_KEY || TRADING_API_KEY, - firebaseAppCheckDebugToken: - process.env.FIREBASE_APP_CHECK_DEBUG_TOKEN || FIREBASE_APP_CHECK_DEBUG_TOKEN, + firebaseAppCheckDebugToken: process.env.FIREBASE_APP_CHECK_DEBUG_TOKEN || FIREBASE_APP_CHECK_DEBUG_TOKEN, } export const config = Object.freeze(_config) diff --git a/packages/wallet/src/constants/addresses.ts b/packages/uniswap/src/constants/addresses.ts similarity index 100% rename from packages/wallet/src/constants/addresses.ts rename to packages/uniswap/src/constants/addresses.ts diff --git a/packages/uniswap/src/constants/chains.ts b/packages/uniswap/src/constants/chains.ts index 7d6bcddd540..47381d1e77d 100644 --- a/packages/uniswap/src/constants/chains.ts +++ b/packages/uniswap/src/constants/chains.ts @@ -80,11 +80,7 @@ export const DEFAULT_RETRY_OPTIONS: RetryOptions = { n: 10, minWait: 250, maxWai export const DEFAULT_MS_BEFORE_WARNING = ONE_MINUTE_MS * 10 -export const ETHEREUM_CHAIN_IDS = [ - UniverseChainId.Mainnet, - UniverseChainId.Goerli, - UniverseChainId.Sepolia, -] as const +export const ETHEREUM_CHAIN_IDS = [UniverseChainId.Mainnet, UniverseChainId.Goerli, UniverseChainId.Sepolia] as const type EthereumChainId = (typeof ETHEREUM_CHAIN_IDS)[number] @@ -353,10 +349,7 @@ export const UNIVERSE_CHAIN_INFO: ChainInfo = { default: { http: ['https://arb1.arbitrum.io/rpc'] }, fallback: { http: ['https://arbitrum.public-rpc.com'] }, appOnly: { - http: [ - `https://arbitrum-mainnet.infura.io/v3/${config.infuraKey}`, - config.quicknodeArbitrumRpcUrl, - ], + http: [`https://arbitrum-mainnet.infura.io/v3/${config.infuraKey}`, config.quicknodeArbitrumRpcUrl], }, [RPCType.PublicAlt]: { http: ['https://arb1.arbitrum.io/rpc'] }, }, @@ -442,8 +435,7 @@ export const UNIVERSE_CHAIN_INFO: ChainInfo = { name: 'OP Etherscan', url: 'https://optimistic.etherscan.io/', }, - helpCenterUrl: - 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ', + helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ', infoLink: 'https://app.uniswap.org/explore/tokens/optimism', infuraPrefix: 'optimism-mainnet', interfaceName: 'optimism', @@ -553,8 +545,7 @@ export const UNIVERSE_CHAIN_INFO: ChainInfo = { name: 'OP Etherscan', url: 'https://goerli-optimism.etherscan.io/', }, - helpCenterUrl: - 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ', + helpCenterUrl: 'https://help.uniswap.org/en/collections/3137778-uniswap-on-optimistic-ethereum-oξ', infoLink: 'https://app.uniswap.org/explore/tokens/optimism', infuraPrefix: 'optimism-goerli', interfaceName: 'optimism_goerli', diff --git a/packages/wallet/src/constants/misc.ts b/packages/uniswap/src/constants/misc.ts similarity index 100% rename from packages/wallet/src/constants/misc.ts rename to packages/uniswap/src/constants/misc.ts diff --git a/packages/uniswap/src/constants/tokens.ts b/packages/uniswap/src/constants/tokens.ts index 970090ea309..887f06f3c40 100644 --- a/packages/uniswap/src/constants/tokens.ts +++ b/packages/uniswap/src/constants/tokens.ts @@ -6,7 +6,7 @@ export const USDC_MAINNET = new Token( '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_GOERLI = new Token( @@ -14,14 +14,14 @@ export const USDC_GOERLI = new Token( '0x07865c6e87b9f70255377e024ace6630c1eaa37f', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_SEPOLIA = new Token( UniverseChainId.Sepolia, '0x6f14C02Fc1F78322cFd7d707aB90f18baD3B54f5', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const DAI = new Token( @@ -29,14 +29,14 @@ export const DAI = new Token( '0x6B175474E89094C44Da98b954EedeAC495271d0F', 18, 'DAI', - 'Dai Stablecoin' + 'Dai Stablecoin', ) export const USDT = new Token( UniverseChainId.Mainnet, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', - 'Tether USD' + 'Tether USD', ) export const DAI_OPTIMISM = new Token( @@ -44,21 +44,21 @@ export const DAI_OPTIMISM = new Token( '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', 18, 'DAI', - 'Dai stable coin' + 'Dai stable coin', ) export const USDC_OPTIMISM = new Token( UniverseChainId.Optimism, '0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_OPTIMISM_GOERLI = new Token( UniverseChainId.OptimismGoerli, '0xe05606174bac4A6364B31bd0eCA4bf4dD368f8C6', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_BASE = new Token( @@ -66,51 +66,39 @@ export const USDC_BASE = new Token( '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) -export const USDC_BSC = new Token( - UniverseChainId.Bnb, - '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', - 18, - 'USDC', - 'USDC' -) -export const USDT_BSC = new Token( - UniverseChainId.Bnb, - '0x55d398326f99059fF775485246999027B3197955', - 18, - 'USDT', - 'USDT' -) +export const USDC_BSC = new Token(UniverseChainId.Bnb, '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d', 18, 'USDC', 'USDC') +export const USDT_BSC = new Token(UniverseChainId.Bnb, '0x55d398326f99059fF775485246999027B3197955', 18, 'USDT', 'USDT') export const MATIC_POLYGON = new Token( UniverseChainId.Polygon, '0x0000000000000000000000000000000000001010', 18, 'MATIC', - 'Matic' + 'Matic', ) export const DAI_POLYGON = new Token( UniverseChainId.Polygon, '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', 18, 'DAI', - 'Dai Stablecoin' + 'Dai Stablecoin', ) export const USDC_POLYGON = new Token( UniverseChainId.Polygon, '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) export const USDC_POLYGON_MUMBAI = new Token( UniverseChainId.PolygonMumbai, '0x0fa8781a83e46826621b3bc094ea2a0212e71b23', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) export const USDB_BLAST = new Token( @@ -118,7 +106,7 @@ export const USDB_BLAST = new Token( '0x4300000000000000000000000000000000000003', 18, 'USDB', - 'USDB' + 'USDB', ) export const USDC_ARBITRUM = new Token( @@ -126,21 +114,21 @@ export const USDC_ARBITRUM = new Token( '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const DAI_ARBITRUM_ONE = new Token( UniverseChainId.ArbitrumOne, '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1', 18, 'DAI', - 'Dai stable coin' + 'Dai stable coin', ) export const USDC_ARBITRUM_GOERLI = new Token( UniverseChainId.ArbitrumGoerli, '0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_AVALANCHE = new Token( @@ -148,7 +136,7 @@ export const USDC_AVALANCHE = new Token( '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', 6, 'USDC', - 'USDC Token' + 'USDC Token', ) export const USDC_CELO = new Token( @@ -156,21 +144,21 @@ export const USDC_CELO = new Token( '0xceba9300f2b948710d2653dd7b07f33a8b32118c', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) export const CUSD_CELO = new Token( UniverseChainId.Celo, '0x765DE816845861e75A25fCA122bb6898B8B1282a', 18, 'cUSD', - 'Celo Dollar' + 'Celo Dollar', ) export const CUSD_CELO_ALFAJORES = new Token( UniverseChainId.CeloAlfajores, '0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1', 18, 'CUSD', - 'Celo Dollar' + 'Celo Dollar', ) export const USDC_ZORA = new Token( @@ -178,12 +166,12 @@ export const USDC_ZORA = new Token( '0xCccCCccc7021b32EBb4e8C08314bD62F7c653EC4', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) export const USDC_ZKSYNC = new Token( UniverseChainId.Zksync, '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4', 6, 'USDC', - 'USD Coin' + 'USD Coin', ) diff --git a/packages/uniswap/src/constants/urls.ts b/packages/uniswap/src/constants/urls.ts index 61eb06754b1..0cf3bed0560 100644 --- a/packages/uniswap/src/constants/urls.ts +++ b/packages/uniswap/src/constants/urls.ts @@ -1,5 +1,4 @@ -import { isDevEnv } from 'uniswap/src/utils/env' -import { isJestRun } from 'utilities/src/environment' +import { isDevEnv, isTestEnv } from 'utilities/src/environment' import { isAndroid, isExtension, isInterface, isMobileApp } from 'utilities/src/platform' enum TrafficFlows { @@ -37,6 +36,7 @@ export const uniswapUrls = { limitsInfo: `${helpUrl}/sections/24372644881293`, limitsNetworkSupport: `${helpUrl}/articles/24470251716237-What-networks-do-limits-support`, moonpayHelp: `${helpUrl}/articles/11306574799117-How-to-use-Moon-Pay-on-the-Uniswap-web-app-`, + fiatOnRampHelp: `${helpUrl}/articles/11306574799117`, moonpayRegionalAvailability: `${helpUrl}/articles/11306664890381-Why-isn-t-MoonPay-available-in-my-region-`, networkFeeInfo: `${helpUrl}/articles/8370337377805-What-is-a-network-fee-`, recoveryPhraseHowToImport: `${helpUrl}/articles/11380692567949-How-to-import-a-recovery-phrase-into-the-Uniswap-Wallet`, @@ -116,7 +116,7 @@ function getCloudflarePrefix(flow?: TrafficFlows): string { return 'interface' } - if (isJestRun) { + if (isTestEnv()) { return 'wallet' } diff --git a/packages/uniswap/src/data/cache.ts b/packages/uniswap/src/data/cache.ts index a7f9cedd381..c71431d316a 100644 --- a/packages/uniswap/src/data/cache.ts +++ b/packages/uniswap/src/data/cache.ts @@ -1,13 +1,11 @@ import { InMemoryCache, InMemoryCacheConfig } from '@apollo/client' import { Reference, relayStylePagination } from '@apollo/client/utilities' +import { isTestEnv } from 'utilities/src/environment' const injectTimestampForRestQueries = (config: InMemoryCacheConfig = {}): InMemoryCacheConfig => { - if ( - config.typePolicies?.Query?.fields?.data && - 'merge' in (config.typePolicies?.Query?.fields?.data ?? {}) - ) { + if (config.typePolicies?.Query?.fields?.data && 'merge' in (config.typePolicies?.Query?.fields?.data ?? {})) { throw new Error( - 'Invalid cache config: `data` field already has a `merge` function that conflicts with the injected `merge` function' + 'Invalid cache config: `data` field already has a `merge` function that conflicts with the injected `merge` function', ) } @@ -64,7 +62,7 @@ export function setupWalletCache(): InMemoryCache { */ // Cache redirects don't work in test environment, so we don't use them in tests - ...(process.env.NODE_ENV !== 'test' + ...(!isTestEnv() ? { // simply use chain / address pair as id instead for tokens token: { diff --git a/packages/uniswap/src/data/constants.ts b/packages/uniswap/src/data/constants.ts index afb501c1a81..30ceebf226c 100644 --- a/packages/uniswap/src/data/constants.ts +++ b/packages/uniswap/src/data/constants.ts @@ -1,7 +1,20 @@ -import { isAndroid, isIOS } from 'utilities/src/platform' +import { isAndroid, isExtension, isIOS } from 'utilities/src/platform' export const ROUTING_API_PATH = '/v2/quote' -export const REQUEST_SOURCE = isIOS ? 'uniswap-ios' : isAndroid ? 'uniswap-android' : 'uniswap-web' +export const REQUEST_SOURCE = getRequestSource() + +function getRequestSource(): string { + if (isIOS) { + return 'uniswap-ios' + } + if (isAndroid) { + return 'uniswap-android' + } + if (isExtension) { + return 'uniswap-extension' + } + return 'uniswap-web' +} export { getVersionHeader } from './getVersionHeader' diff --git a/packages/uniswap/src/data/getVersionHeader.native.ts b/packages/uniswap/src/data/getVersionHeader.native.ts index b06e8ca198a..67c6a794e25 100644 --- a/packages/uniswap/src/data/getVersionHeader.native.ts +++ b/packages/uniswap/src/data/getVersionHeader.native.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line no-restricted-imports import DeviceInfo from 'react-native-device-info' export const getVersionHeader = (): string => { diff --git a/packages/uniswap/src/data/graphql/uniswap-data-api/queries.graphql b/packages/uniswap/src/data/graphql/uniswap-data-api/queries.graphql index 55d3a0ad470..e995d51cedb 100644 --- a/packages/uniswap/src/data/graphql/uniswap-data-api/queries.graphql +++ b/packages/uniswap/src/data/graphql/uniswap-data-api/queries.graphql @@ -653,6 +653,7 @@ query TransactionList($address: String!, $onRampAuth: OnRampTransactionsAuth) { assetActivities( pageSize: 100 page: 1 + includeOffChain: true chains: [ ETHEREUM POLYGON @@ -715,6 +716,13 @@ query TransactionList($address: String!, $onRampAuth: OnRampTransactionsAuth) { hash from status + application { + name + address + icon { + url + } + } assetChanges { __typename ... on TokenTransfer { @@ -813,6 +821,37 @@ query TransactionList($address: String!, $onRampAuth: OnRampTransactionsAuth) { quantity } } + networkFee { + quantity + tokenSymbol + tokenAddress + tokenChain + } + } + ... on SwapOrderDetails { + id + offerer + hash + orderStatus: swapOrderStatus + expiry + swapOrderType + encodedOrder + inputToken { + id + symbol + address + decimals + chain + } + inputTokenQuantity + outputToken { + id + symbol + address + decimals + chain + } + outputTokenQuantity } } } diff --git a/packages/uniswap/src/data/graphql/uniswap-data-api/schema.graphql b/packages/uniswap/src/data/graphql/uniswap-data-api/schema.graphql index c0c267bc534..bc49540a27c 100644 --- a/packages/uniswap/src/data/graphql/uniswap-data-api/schema.graphql +++ b/packages/uniswap/src/data/graphql/uniswap-data-api/schema.graphql @@ -1,6 +1,11 @@ """This directive allows results to be deferred during execution""" directive @defer on FIELD +""" +Tells the service this field/object has access authorized by an OIDC token. +""" +directive @aws_oidc on OBJECT | FIELD_DEFINITION + """Directs the schema to enforce authorization on a field""" directive @aws_auth( """List of cognito user pool groups which have access on this field""" @@ -13,25 +18,22 @@ Tells the service this field/object has access authorized by an API key. directive @aws_api_key on OBJECT | FIELD_DEFINITION """ -Tells the service which subscriptions will be published to when this mutation is -called. This directive is deprecated use @aws_susbscribe directive instead. +Tells the service this field/object has access authorized by a Lambda Authorizer. """ -directive @aws_publish( - """ - List of subscriptions which will be published to when this mutation is called. - """ - subscriptions: [String] -) on FIELD_DEFINITION +directive @aws_lambda on OBJECT | FIELD_DEFINITION """ -Tells the service this field/object has access authorized by an OIDC token. +Tells the service this field/object has access authorized by sigv4 signing. """ -directive @aws_oidc on OBJECT | FIELD_DEFINITION +directive @aws_iam on OBJECT | FIELD_DEFINITION """ -Tells the service this field/object has access authorized by sigv4 signing. +Tells the service this field/object has access authorized by a Cognito User Pools token. """ -directive @aws_iam on OBJECT | FIELD_DEFINITION +directive @aws_cognito_user_pools( + """List of cognito user pool groups which have access on this field""" + cognito_groups: [String] +) on OBJECT | FIELD_DEFINITION """Tells the service which mutation triggers this subscription.""" directive @aws_subscribe( @@ -42,17 +44,15 @@ directive @aws_subscribe( ) on FIELD_DEFINITION """ -Tells the service this field/object has access authorized by a Lambda Authorizer. -""" -directive @aws_lambda on OBJECT | FIELD_DEFINITION - -""" -Tells the service this field/object has access authorized by a Cognito User Pools token. +Tells the service which subscriptions will be published to when this mutation is +called. This directive is deprecated use @aws_susbscribe directive instead. """ -directive @aws_cognito_user_pools( - """List of cognito user pool groups which have access on this field""" - cognito_groups: [String] -) on OBJECT | FIELD_DEFINITION +directive @aws_publish( + """ + List of subscriptions which will be published to when this mutation is called. + """ + subscriptions: [String] +) on FIELD_DEFINITION """ Types, unions, and inputs (alphabetized): diff --git a/packages/uniswap/src/data/graphql/uniswap-data-api/web/nft/NftBalance.graphql b/packages/uniswap/src/data/graphql/uniswap-data-api/web/nft/NftBalance.graphql index d80039ca244..f88b4192aa9 100644 --- a/packages/uniswap/src/data/graphql/uniswap-data-api/web/nft/NftBalance.graphql +++ b/packages/uniswap/src/data/graphql/uniswap-data-api/web/nft/NftBalance.graphql @@ -1,6 +1,7 @@ query NftBalance( $ownerAddress: String! $filter: NftBalancesFilterInput + $chains: [Chain!] = [ETHEREUM] $first: Int $after: String $last: Int @@ -8,6 +9,7 @@ query NftBalance( ) { nftBalances( ownerAddress: $ownerAddress + chains: $chains filter: $filter first: $first after: $after @@ -45,6 +47,7 @@ query NftBalance( } } } + chain description flaggedBy image { diff --git a/packages/uniswap/src/data/graphql/uniswap-data-api/web/portfolios.graphql b/packages/uniswap/src/data/graphql/uniswap-data-api/web/portfolios.graphql index 8287e6eae83..5d78ea56bf8 100644 --- a/packages/uniswap/src/data/graphql/uniswap-data-api/web/portfolios.graphql +++ b/packages/uniswap/src/data/graphql/uniswap-data-api/web/portfolios.graphql @@ -1,3 +1,18 @@ +fragment QuickTokenBalanceParts on TokenBalance { + id + quantity + denominatedValue { + id + value + } + token { + id + address + chain + standard + } +} + fragment PortfolioTokenBalanceParts on TokenBalance { id quantity @@ -78,3 +93,13 @@ query PortfolioBalancesWeb( } } } + +# Query only returns basic token, quantity, and denominated value. +query QuickTokenBalancesWeb($ownerAddress: String!, $chains: [Chain!]!) { + portfolios(ownerAddresses: [$ownerAddress], chains: $chains) { + id + tokenBalances { + ...QuickTokenBalanceParts + } + } +} diff --git a/packages/uniswap/src/data/rest.ts b/packages/uniswap/src/data/rest.ts index 2f4e2fa7bc6..5124c8312a8 100644 --- a/packages/uniswap/src/data/rest.ts +++ b/packages/uniswap/src/data/rest.ts @@ -15,10 +15,7 @@ import { useEffect, useMemo } from 'react' import { GqlResult } from 'uniswap/src/data/types' /** Wrapper around Apollo client `useQuery` that calls REST APIs */ -export function useRestQuery< - TData = unknown, - TVariables extends OperationVariables = OperationVariables ->( +export function useRestQuery( // Relative URL path of the endpoint path: string, // Variables required by the endpoint @@ -45,7 +42,7 @@ export function useRestQuery< clearIfStale?: undefined }), method: 'GET' | 'POST' = 'POST', - client?: ApolloClient + client?: ApolloClient, ): GqlResult { const document = gql` query Query($input: REST!) { @@ -68,8 +65,7 @@ export function useRestQuery< const queryResult = useQuery<{ data: TData }, { input: TVariables }>(document, queryOptions) // timestamp is injected by our `InMemoryCache` config (cache.ts) - const lastFetchedTimestamp = (queryResult.data?.data as undefined | { timestamp: number }) - ?.timestamp + const lastFetchedTimestamp = (queryResult.data?.data as undefined | { timestamp: number })?.timestamp const cacheExpired = getCacheExpired(lastFetchedTimestamp, options.ttlMs) // re-export query result with easier data access @@ -78,7 +74,7 @@ export function useRestQuery< ...queryResult, data: cacheExpired && options.clearIfStale ? undefined : queryResult.data?.data, }), - [queryResult, cacheExpired, options.clearIfStale] + [queryResult, cacheExpired, options.clearIfStale], ) useEffect(() => { @@ -95,10 +91,7 @@ export function useRestQuery< return result } -const getCacheExpired = ( - lastFetchedTimestamp: number | undefined, - ttl: number | undefined -): boolean => { +const getCacheExpired = (lastFetchedTimestamp: number | undefined, ttl: number | undefined): boolean => { // if there is no timestamp, then it's the first ever query and there is no cache to expire if (!lastFetchedTimestamp) { return false @@ -128,15 +121,12 @@ const getCacheExpired = ( * * Example usage: `wallet/src/unitags/api.ts` and `mobile/src/feature/unitags/ChooseProfilePictureScreen.tsx` */ -export function useRestMutation< - TData = unknown, - TVariables extends OperationVariables = OperationVariables ->( +export function useRestMutation( path: string, fields: string[], options: Omit, 'variables'>, method: 'POST' | 'PUT' | 'DELETE', - client?: ApolloClient + client?: ApolloClient, ): [(variables: TVariables) => Promise>, MutationResult] { const document = gql` mutation Mutation($input: REST!) { @@ -156,7 +146,7 @@ export function useRestMutation< } const [mutateFunction, mutationResult] = useMutation<{ data: TData }, { input: TVariables }>( document, - mutationOptions + mutationOptions, ) // Wrapper for the mutate function to simplify its usage diff --git a/packages/uniswap/src/features/chains/utils.test.ts b/packages/uniswap/src/features/chains/utils.test.ts index 0f81e19b505..51922191222 100644 --- a/packages/uniswap/src/features/chains/utils.test.ts +++ b/packages/uniswap/src/features/chains/utils.test.ts @@ -1,13 +1,131 @@ -import { ChainId } from '@uniswap/sdk-core' +import { BigNumber } from '@ethersproject/bignumber' +import { PollingInterval } from 'uniswap/src/constants/misc' import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { toGraphQLChain } from 'uniswap/src/features/chains/utils' +import { + chainIdToHexadecimalString, + fromGraphQLChain, + fromMoonpayNetwork, + fromUniswapWebAppLink, + getPollingIntervalByBlocktime, + hexadecimalStringToInt, + toSupportedChainId, + toUniswapWebAppLink, +} from 'uniswap/src/features/chains/utils' +import { UniverseChainId } from 'uniswap/src/types/chains' + +describe(toSupportedChainId, () => { + it('handles undefined input', () => { + expect(toSupportedChainId(undefined)).toEqual(null) + }) + + it('handles unsupported chain ID', () => { + expect(toSupportedChainId(BigNumber.from(6767))).toEqual(null) + }) + + it('handles supported chain ID', () => { + expect(toSupportedChainId(UniverseChainId.Polygon)).toEqual(137) + }) +}) + +describe(fromGraphQLChain, () => { + it('handles undefined', () => { + expect(fromGraphQLChain(undefined)).toEqual(null) + }) -describe(toGraphQLChain, () => { it('handles supported chain', () => { - expect(toGraphQLChain(ChainId.MAINNET)).toEqual(Chain.Ethereum) + expect(fromGraphQLChain(Chain.Arbitrum)).toEqual(UniverseChainId.ArbitrumOne) + }) + + it('handles unsupported chain', () => { + expect(fromGraphQLChain(Chain.UnknownChain)).toEqual(null) + }) +}) + +describe(fromMoonpayNetwork, () => { + it('handles supported chain', () => { + expect(fromMoonpayNetwork(undefined)).toEqual(UniverseChainId.Mainnet) + expect(fromMoonpayNetwork(Chain.Arbitrum.toLowerCase())).toEqual(UniverseChainId.ArbitrumOne) + expect(fromMoonpayNetwork(Chain.Optimism.toLowerCase())).toEqual(UniverseChainId.Optimism) + expect(fromMoonpayNetwork(Chain.Polygon.toLowerCase())).toEqual(UniverseChainId.Polygon) + expect(fromMoonpayNetwork(Chain.Base.toLowerCase())).toEqual(UniverseChainId.Base) }) it('handle unsupported chain', () => { - expect(toGraphQLChain(7)).toEqual(undefined) + expect(fromMoonpayNetwork('unknown')).toBeUndefined() + }) +}) + +describe(getPollingIntervalByBlocktime, () => { + it('returns the correct value for L1', () => { + expect(getPollingIntervalByBlocktime(UniverseChainId.Mainnet)).toEqual(PollingInterval.Fast) + }) + + it('returns the correct value for L2', () => { + expect(getPollingIntervalByBlocktime(UniverseChainId.Polygon)).toEqual( + PollingInterval.LightningMcQueen + ) + }) +}) + +describe(fromUniswapWebAppLink, () => { + it('handles supported chain', () => { + expect(fromUniswapWebAppLink(Chain.Ethereum.toLowerCase())).toEqual(UniverseChainId.Mainnet) + expect(fromUniswapWebAppLink(Chain.Arbitrum.toLowerCase())).toEqual(UniverseChainId.ArbitrumOne) + expect(fromUniswapWebAppLink(Chain.Optimism.toLowerCase())).toEqual(UniverseChainId.Optimism) + expect(fromUniswapWebAppLink(Chain.Polygon.toLowerCase())).toEqual(UniverseChainId.Polygon) + // TODO: add Base test once Chain includes Base (GQL reliant) + }) + + it('handle unsupported chain', () => { + expect(() => fromUniswapWebAppLink('unkwnown')).toThrow('Network "unkwnown" can not be mapped') + }) +}) + +describe(toUniswapWebAppLink, () => { + it('handles supported chain', () => { + expect(toUniswapWebAppLink(UniverseChainId.Mainnet)).toEqual(Chain.Ethereum.toLowerCase()) + expect(toUniswapWebAppLink(UniverseChainId.ArbitrumOne)).toEqual(Chain.Arbitrum.toLowerCase()) + expect(toUniswapWebAppLink(UniverseChainId.Optimism)).toEqual(Chain.Optimism.toLowerCase()) + expect(toUniswapWebAppLink(UniverseChainId.Polygon)).toEqual(Chain.Polygon.toLowerCase()) + // TODO: add Base test once Chain includes Base (GQL reliant) + }) + + it('handle unsupported chain', () => { + expect(() => fromUniswapWebAppLink('unkwnown')).toThrow('Network "unkwnown" can not be mapped') + }) +}) + +describe(chainIdToHexadecimalString, () => { + it('handles supported chain', () => { + expect(chainIdToHexadecimalString(UniverseChainId.ArbitrumOne)).toEqual('0xa4b1') + }) +}) + +describe('hexadecimalStringToInt', () => { + it('converts valid hexadecimal strings to integers', () => { + expect(hexadecimalStringToInt('1')).toEqual(1) + expect(hexadecimalStringToInt('a')).toEqual(10) + expect(hexadecimalStringToInt('A')).toEqual(10) + expect(hexadecimalStringToInt('10')).toEqual(16) + expect(hexadecimalStringToInt('FF')).toEqual(255) + expect(hexadecimalStringToInt('ff')).toEqual(255) + expect(hexadecimalStringToInt('100')).toEqual(256) + }) + + it('converts hexadecimal strings with prefix to integers', () => { + expect(hexadecimalStringToInt('0x1')).toEqual(1) + expect(hexadecimalStringToInt('0xa')).toEqual(10) + expect(hexadecimalStringToInt('0xA')).toEqual(10) + expect(hexadecimalStringToInt('0x10')).toEqual(16) + expect(hexadecimalStringToInt('0xFF')).toEqual(255) + expect(hexadecimalStringToInt('0xff')).toEqual(255) + expect(hexadecimalStringToInt('0x100')).toEqual(256) + }) + + it('handles invalid hexadecimal strings', () => { + expect(hexadecimalStringToInt('')).toBeNaN() + expect(hexadecimalStringToInt('g')).toBeNaN() + expect(hexadecimalStringToInt('0x')).toBeNaN() + expect(hexadecimalStringToInt('0xg')).toBeNaN() }) }) diff --git a/packages/uniswap/src/features/chains/utils.ts b/packages/uniswap/src/features/chains/utils.ts index 7f05b335aa2..73d5c9b4510 100644 --- a/packages/uniswap/src/features/chains/utils.ts +++ b/packages/uniswap/src/features/chains/utils.ts @@ -1,5 +1,10 @@ +import { BigNumber, BigNumberish } from '@ethersproject/bignumber' import { ChainId } from '@uniswap/sdk-core' +import { L2ChainId, L2_CHAIN_IDS } from 'uniswap/src/constants/chains' +import { PollingInterval } from 'uniswap/src/constants/misc' import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { UniverseChainId, WALLET_SUPPORTED_CHAIN_IDS, WalletChainId } from 'uniswap/src/types/chains' +import { isTestEnv } from 'utilities/src/environment' export function toGraphQLChain(chainId: ChainId | number): Chain | undefined { switch (chainId) { @@ -40,3 +45,145 @@ export function toGraphQLChain(chainId: ChainId | number): Chain | undefined { } return undefined } + +// Some code from the web app uses chainId types as numbers +// This validates them as coerces into SupportedChainId +export function toSupportedChainId(chainId?: BigNumberish): WalletChainId | null { + // Support Goerli for testing + const ids = isTestEnv() ? [UniverseChainId.Goerli, ...WALLET_SUPPORTED_CHAIN_IDS] : WALLET_SUPPORTED_CHAIN_IDS + + if (!chainId || !ids.map((c) => c.toString()).includes(chainId.toString())) { + return null + } + return parseInt(chainId.toString(), 10) as WalletChainId +} + +export function chainIdToHexadecimalString(chainId: WalletChainId): string { + return BigNumber.from(chainId).toHexString() +} + +export function hexadecimalStringToInt(hex: string): number { + return parseInt(hex, 16) +} + +export const isL2Chain = (chainId?: UniverseChainId): boolean => + Boolean(chainId && L2_CHAIN_IDS.includes(chainId as L2ChainId)) + +export function fromGraphQLChain(chain: Chain | undefined): WalletChainId | null { + switch (chain) { + case Chain.Ethereum: + return UniverseChainId.Mainnet + case Chain.Arbitrum: + return UniverseChainId.ArbitrumOne + case Chain.EthereumGoerli: + return UniverseChainId.Goerli + case Chain.Optimism: + return UniverseChainId.Optimism + case Chain.Polygon: + return UniverseChainId.Polygon + case Chain.Base: + return UniverseChainId.Base + case Chain.Bnb: + return UniverseChainId.Bnb + case Chain.Blast: + return UniverseChainId.Blast + case Chain.Avalanche: + return UniverseChainId.Avalanche + case Chain.Celo: + return UniverseChainId.Celo + case Chain.Zora: + return UniverseChainId.Zora + case Chain.Zksync: + return UniverseChainId.Zksync + } + + return null +} + +export function getPollingIntervalByBlocktime(chainId?: WalletChainId): PollingInterval { + return isL2Chain(chainId) ? PollingInterval.LightningMcQueen : PollingInterval.Fast +} + +export function fromMoonpayNetwork(moonpayNetwork: string | undefined): WalletChainId | undefined { + switch (moonpayNetwork) { + case Chain.Arbitrum.toLowerCase(): + return UniverseChainId.ArbitrumOne + case Chain.Optimism.toLowerCase(): + return UniverseChainId.Optimism + case Chain.Polygon.toLowerCase(): + return UniverseChainId.Polygon + case Chain.Bnb.toLowerCase(): + return UniverseChainId.Bnb + // Moonpay still refers to BNB chain as BSC so including both BNB and BSC cases + case 'bsc': + return UniverseChainId.Bnb + case Chain.Base.toLowerCase(): + return UniverseChainId.Base + case Chain.Avalanche.toLowerCase(): + return UniverseChainId.Avalanche + case Chain.Celo.toLowerCase(): + return UniverseChainId.Celo + case undefined: + return UniverseChainId.Mainnet + default: + return undefined + } +} + +export function fromUniswapWebAppLink(network: string | null): WalletChainId | null { + switch (network) { + case Chain.Ethereum.toLowerCase(): + return UniverseChainId.Mainnet + case Chain.Arbitrum.toLowerCase(): + return UniverseChainId.ArbitrumOne + case Chain.Optimism.toLowerCase(): + return UniverseChainId.Optimism + case Chain.Polygon.toLowerCase(): + return UniverseChainId.Polygon + case Chain.Base.toLowerCase(): + return UniverseChainId.Base + case Chain.Bnb.toLowerCase(): + return UniverseChainId.Bnb + case Chain.Blast.toLowerCase(): + return UniverseChainId.Blast + case Chain.Avalanche.toLowerCase(): + return UniverseChainId.Avalanche + case Chain.Celo.toLowerCase(): + return UniverseChainId.Celo + case Chain.Zora.toLowerCase(): + return UniverseChainId.Zora + case Chain.Zksync.toLowerCase(): + return UniverseChainId.Zksync + default: + throw new Error(`Network "${network}" can not be mapped`) + } +} + +export function toUniswapWebAppLink(chainId: WalletChainId): string | null { + switch (chainId) { + case UniverseChainId.Mainnet: + return Chain.Ethereum.toLowerCase() + case UniverseChainId.ArbitrumOne: + return Chain.Arbitrum.toLowerCase() + case UniverseChainId.Optimism: + return Chain.Optimism.toLowerCase() + case UniverseChainId.Polygon: + return Chain.Polygon.toLowerCase() + case UniverseChainId.Base: + return Chain.Base.toLowerCase() + case UniverseChainId.Bnb: + return Chain.Bnb.toLowerCase() + case UniverseChainId.Blast: + return Chain.Blast.toLowerCase() + case UniverseChainId.Avalanche: + return Chain.Avalanche.toLowerCase() + case UniverseChainId.Celo: + return Chain.Celo.toLowerCase() + case UniverseChainId.Zora: + return Chain.Zora.toLowerCase() + case UniverseChainId.Zksync: + return Chain.Zksync.toLowerCase() + default: + throw new Error(`ChainID "${chainId}" can not be mapped`) + } +} diff --git a/apps/mobile/src/components/fiatOnRamp/QuoteItem.tsx b/packages/uniswap/src/features/fiatOnRamp/FORQuoteItem.tsx similarity index 65% rename from apps/mobile/src/components/fiatOnRamp/QuoteItem.tsx rename to packages/uniswap/src/features/fiatOnRamp/FORQuoteItem.tsx index 12a51e242be..43043458ece 100644 --- a/apps/mobile/src/components/fiatOnRamp/QuoteItem.tsx +++ b/packages/uniswap/src/features/fiatOnRamp/FORQuoteItem.tsx @@ -1,18 +1,12 @@ -import React from 'react' import { useTranslation } from 'react-i18next' -import { StyleSheet } from 'react-native' -import { Loader } from 'src/components/loading' -import { Flex, Text, TouchableArea, useIsDarkMode } from 'ui/src' +import { Flex, Loader, Text, TouchableArea, UniversalImage, useIsDarkMode } from 'ui/src' import { iconSizes } from 'ui/src/theme' import { FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types' +import { getOptionalServiceProviderLogo } from 'uniswap/src/features/fiatOnRamp/utils' import { concatStrings } from 'utilities/src/primitives/string' -import { getOptionalServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils' -import { ImageUri } from 'wallet/src/features/images/ImageUri' function LogoLoader(): JSX.Element { - return ( - - ) + return } export function FORQuoteItem({ @@ -35,9 +29,9 @@ export function FORQuoteItem({ ? concatStrings( [ serviceProvider.paymentMethods.slice(0, 3).join(', ') + ',', // oxford comma - t('fiatOnRamp.quote.others'), + t('fiatOnRamp.quote.type.other'), ], - t('common.endAdornment') + t('common.endAdornment'), ) : serviceProvider.paymentMethods.join(', ') @@ -53,13 +47,17 @@ export function FORQuoteItem({ py="$spacing16" shadowColor="$neutral3" shadowOpacity={0.4} - shadowRadius={!isDarkMode ? '$spacing4' : undefined}> + shadowRadius={!isDarkMode ? '$spacing4' : undefined} + > {logoUrl ? ( - } - imageStyle={ServiceProviderLogoStyles.icon} + size={{ + height: iconSizes.icon40, + width: iconSizes.icon40, + }} uri={logoUrl} /> ) : ( @@ -70,19 +68,14 @@ export function FORQuoteItem({ {serviceProvider.name} - - {paymentMethods} - + {paymentMethods && ( + + {paymentMethods} + + )} ) } - -const ServiceProviderLogoStyles = StyleSheet.create({ - icon: { - height: iconSizes.icon40, - width: iconSizes.icon40, - }, -}) diff --git a/apps/mobile/src/features/fiatOnRamp/FiatOnRampConnecting.tsx b/packages/uniswap/src/features/fiatOnRamp/FiatOnRampConnectingView.native.tsx similarity index 85% rename from apps/mobile/src/features/fiatOnRamp/FiatOnRampConnecting.tsx rename to packages/uniswap/src/features/fiatOnRamp/FiatOnRampConnectingView.native.tsx index 002923490f9..6373453a435 100644 --- a/apps/mobile/src/features/fiatOnRamp/FiatOnRampConnecting.tsx +++ b/packages/uniswap/src/features/fiatOnRamp/FiatOnRampConnectingView.native.tsx @@ -1,19 +1,14 @@ -import React from 'react' import { useTranslation } from 'react-i18next' import { Image, ImageBackground, StyleSheet } from 'react-native' import { FadeIn, FadeOut } from 'react-native-reanimated' -import { - SERVICE_PROVIDER_ICON_BORDER_RADIUS, - ServiceProviderLogoStyles, -} from 'src/features/fiatOnRamp/constants' import { Flex, Text, useDeviceInsets, useIsDarkMode } from 'ui/src' -import { - FOR_CONNECTING_BACKGROUND_DARK, - FOR_CONNECTING_BACKGROUND_LIGHT, - UNISWAP_LOGO_LARGE, -} from 'ui/src/assets' +import { FOR_CONNECTING_BACKGROUND_DARK, FOR_CONNECTING_BACKGROUND_LIGHT, UNISWAP_LOGO_LARGE } from 'ui/src/assets' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { iconSizes } from 'ui/src/theme' +import { + SERVICE_PROVIDER_ICON_BORDER_RADIUS, + ServiceProviderLogoStyles, +} from 'uniswap/src/features/fiatOnRamp/constants' export function FiatOnRampConnectingView({ amount, @@ -34,13 +29,9 @@ export function FiatOnRampConnectingView({ - + style={styles.background} + > + diff --git a/packages/uniswap/src/features/fiatOnRamp/FiatOnRampConnectingView.tsx b/packages/uniswap/src/features/fiatOnRamp/FiatOnRampConnectingView.tsx new file mode 100644 index 00000000000..0d2fc2e22b6 --- /dev/null +++ b/packages/uniswap/src/features/fiatOnRamp/FiatOnRampConnectingView.tsx @@ -0,0 +1,48 @@ +import { useTranslation } from 'react-i18next' +import { AnimatePresence, Flex, Image, Text, useDeviceInsets } from 'ui/src' +import { UNISWAP_LOGO_LARGE } from 'ui/src/assets' +import { iconSizes } from 'ui/src/theme' +import { ServiceProviderLogoStyles } from 'uniswap/src/features/fiatOnRamp/constants' + +export function FiatOnRampConnectingView({ + amount, + quoteCurrencyCode, + serviceProviderName, + serviceProviderLogo, +}: { + amount?: string + quoteCurrencyCode?: string + serviceProviderName: string + serviceProviderLogo?: JSX.Element +}): JSX.Element { + const insets = useDeviceInsets() + const { t } = useTranslation() + + return ( + + + + + + + + {serviceProviderLogo} + + + + {t('fiatOnRamp.connection.message', { serviceProvider: serviceProviderName })} + + {quoteCurrencyCode && amount && ( + + {t('fiatOnRamp.connection.quote', { + amount, + currencySymbol: quoteCurrencyCode, + })} + + )} + + + + + ) +} diff --git a/packages/uniswap/src/features/fiatOnRamp/FiatOnRampCountryPicker.tsx b/packages/uniswap/src/features/fiatOnRamp/FiatOnRampCountryPicker.tsx index 75840925b77..9a80563f044 100644 --- a/packages/uniswap/src/features/fiatOnRamp/FiatOnRampCountryPicker.tsx +++ b/packages/uniswap/src/features/fiatOnRamp/FiatOnRampCountryPicker.tsx @@ -31,7 +31,8 @@ export function FiatOnRampCountryPicker({ pl="$spacing8" pr="$spacing4" py="$spacing2" - onPress={onPress}> + onPress={onPress} + > {isWeb ? ( diff --git a/packages/uniswap/src/features/fiatOnRamp/SelectTokenButton.tsx b/packages/uniswap/src/features/fiatOnRamp/SelectTokenButton.tsx index 89954b7314a..b6edf043784 100644 --- a/packages/uniswap/src/features/fiatOnRamp/SelectTokenButton.tsx +++ b/packages/uniswap/src/features/fiatOnRamp/SelectTokenButton.tsx @@ -1,3 +1,4 @@ +import { ComponentProps } from 'react' import { Flex, SpinningLoader, Text, TouchableArea } from 'ui/src' import { RotatableChevron } from 'ui/src/components/icons/RotatableChevron' import { iconSizes, spacing } from 'ui/src/theme' @@ -13,7 +14,9 @@ interface SelectTokenButtonProps { amountReady?: boolean disabled?: boolean loading?: boolean - showCaret?: boolean + iconSize?: number + backgroundColor?: ComponentProps['backgroundColor'] + chevronDirection?: ComponentProps['direction'] } export function SelectTokenButton({ @@ -23,36 +26,36 @@ export function SelectTokenButton({ amountReady, disabled, loading, - showCaret = true, + iconSize = iconSizes.icon24, + chevronDirection = 'end', + backgroundColor, }: SelectTokenButtonProps): JSX.Element { const textColor = !amountReady || disabled || loading ? '$neutral3' : '$neutral2' return ( + onPress={onPress} + > {loading ? ( ) : ( - + )} {formattedAmount} - + {getSymbolDisplayText(selectedCurrencyInfo.currency.symbol)} - {showCaret && ( - - )} + ) diff --git a/packages/uniswap/src/features/fiatOnRamp/api.ts b/packages/uniswap/src/features/fiatOnRamp/api.ts index 86008a4c568..c47c8bc3c11 100644 --- a/packages/uniswap/src/features/fiatOnRamp/api.ts +++ b/packages/uniswap/src/features/fiatOnRamp/api.ts @@ -42,10 +42,7 @@ export const fiatOnRampAggregatorApi = createApi({ }), keepUnusedDataFor: 0, }), - fiatOnRampAggregatorServiceProviders: builder.query< - FORServiceProvidersResponse, - FORServiceProvidersRequest - >({ + fiatOnRampAggregatorServiceProviders: builder.query({ query: (request) => `/service-providers?${new URLSearchParams(request).toString()}`, transformResponse: (response: FORServiceProvidersResponse) => ({ serviceProviders: response.serviceProviders.map((sp) => ({ @@ -58,10 +55,7 @@ export const fiatOnRampAggregatorApi = createApi({ query: () => '/transfer-service-providers', keepUnusedDataFor: 60 * 60, // 1 hour }), - fiatOnRampAggregatorSupportedTokens: builder.query< - FORSupportedTokensResponse, - FORSupportedTokensRequest - >({ + fiatOnRampAggregatorSupportedTokens: builder.query({ query: (request) => `/supported-tokens?${new URLSearchParams(request).toString()}`, }), fiatOnRampAggregatorSupportedFiatCurrencies: builder.query< @@ -77,10 +71,7 @@ export const fiatOnRampAggregatorApi = createApi({ method: 'POST', }), }), - fiatOnRampAggregatorTransferWidget: builder.query< - FORWidgetUrlResponse, - FORTransferWidgetUrlRequest - >({ + fiatOnRampAggregatorTransferWidget: builder.query({ query: (request) => ({ url: '/transfer-widget-url', body: request, diff --git a/packages/uniswap/src/features/fiatOnRamp/constants.ts b/packages/uniswap/src/features/fiatOnRamp/constants.ts index 4dd2c4871f6..ae958c98f79 100644 --- a/packages/uniswap/src/features/fiatOnRamp/constants.ts +++ b/packages/uniswap/src/features/fiatOnRamp/constants.ts @@ -9,3 +9,20 @@ export const FOR_API_HEADERS = { 'x-app-version': getVersionHeader(), Origin: uniswapUrls.requestOriginUrl, } + +export const FOR_MODAL_SNAP_POINTS = ['70%', '100%'] +export const SERVICE_PROVIDER_ICON_SIZE = 90 +export const SERVICE_PROVIDER_ICON_BORDER_RADIUS = 20 + +export const ServiceProviderLogoStyles = { + icon: { + height: SERVICE_PROVIDER_ICON_SIZE, + width: SERVICE_PROVIDER_ICON_SIZE, + }, + uniswapLogoWrapper: { + backgroundColor: '#FFEFF8', // #FFD8EF with 40% opacity on a white background + borderRadius: SERVICE_PROVIDER_ICON_BORDER_RADIUS, + height: SERVICE_PROVIDER_ICON_SIZE, + width: SERVICE_PROVIDER_ICON_SIZE, + }, +} diff --git a/packages/uniswap/src/features/fiatOnRamp/types.ts b/packages/uniswap/src/features/fiatOnRamp/types.ts index 925f30905e1..3e2fa18c3ed 100644 --- a/packages/uniswap/src/features/fiatOnRamp/types.ts +++ b/packages/uniswap/src/features/fiatOnRamp/types.ts @@ -35,6 +35,7 @@ export type FORQuote = { destinationAmount: number destinationCurrencyCode: string serviceProvider: string + serviceProviderDetails: FORServiceProvider totalFee: number } diff --git a/packages/uniswap/src/features/fiatOnRamp/utils.test.ts b/packages/uniswap/src/features/fiatOnRamp/utils.test.ts index dfec7ce20c0..f08f23733f5 100644 --- a/packages/uniswap/src/features/fiatOnRamp/utils.test.ts +++ b/packages/uniswap/src/features/fiatOnRamp/utils.test.ts @@ -1,4 +1,9 @@ -import { getCountryFlagSvgUrl } from 'uniswap/src/features/fiatOnRamp/utils' +import { + getCountryFlagSvgUrl, + isFiatOnRampApiError, + isInvalidRequestAmountTooHigh, + isInvalidRequestAmountTooLow, +} from 'uniswap/src/features/fiatOnRamp/utils' describe('getCountryFlagSvgUrl', () => { test('should return the correct SVG URL for a given country code', () => { @@ -8,3 +13,151 @@ describe('getCountryFlagSvgUrl', () => { expect(result).toBe(expectedUrl) }) }) + +describe('isFiatOnRampApiError', () => { + test('returns true', () => { + const error = { + data: { + statusCode: 400, + errorName: 'InvalidRequestAmountTooLow', + message: 'Source amount is below the minimum allowed, which is 50.00 USD', + }, + } + const result = isFiatOnRampApiError(error) + expect(result).toBe(true) + }) + + test('returns false', () => { + const error = { + data: { + message: 'Source amount is below the minimum allowed, which is 50.00 USD', + }, + } + const result = isFiatOnRampApiError(error) + expect(result).toBe(false) + }) +}) + +describe('isInvalidRequestAmountTooHigh', () => { + test('returns true', () => { + const error = { + data: { + statusCode: 400, + errorName: 'InvalidRequestAmountTooHigh', + message: 'Source amount is above the maximum allowed, which is 50000.00 USD', + context: { + maximumAllowed: 50000, + }, + }, + } + const result = isInvalidRequestAmountTooHigh(error) + expect(result).toBe(true) + }) + + test('returns false when context has unexpected type', () => { + const error = { + data: { + statusCode: 400, + errorName: 'InvalidRequestAmountTooHigh', + message: 'Source amount is above the maximum allowed, which is 50000.00 USD', + context: { + randomProperty: 50000, + }, + }, + } + const result = isInvalidRequestAmountTooHigh(error) + expect(result).toBe(false) + }) + + test('returns false when statusCode is not 400', () => { + const error = { + data: { + statusCode: 404, + errorName: 'InvalidRequestAmountTooHigh', + message: 'Source amount is above the maximum allowed, which is 50000.00 USD', + context: { + maximumAllowed: 50000, + }, + }, + } + const result = isInvalidRequestAmountTooHigh(error) + expect(result).toBe(false) + }) + + test('returns false when errorName is not InvalidRequestAmountTooHigh', () => { + const error = { + data: { + statusCode: 400, + errorName: 'InvalidRequestAmountTooBig', + message: 'Source amount is above the maximum allowed, which is 50000.00 USD', + context: { + maximumAllowed: 50000, + }, + }, + } + const result = isInvalidRequestAmountTooHigh(error) + expect(result).toBe(false) + }) +}) + +describe('isInvalidRequestAmountTooLow', () => { + test('returns true', () => { + const error = { + data: { + statusCode: 400, + errorName: 'InvalidRequestAmountTooLow', + message: 'Source amount is below the minimum allowed, which is 50.00 USD', + context: { + minimumAllowed: 50, + }, + }, + } + const result = isInvalidRequestAmountTooLow(error) + expect(result).toBe(true) + }) + + test('returns false when context has unexpected type', () => { + const error = { + data: { + statusCode: 400, + errorName: 'InvalidRequestAmountTooLow', + message: 'Source amount is below the minimum allowed, which is 50.00 USD', + context: { + randomProperty: 50, + }, + }, + } + const result = isInvalidRequestAmountTooLow(error) + expect(result).toBe(false) + }) + + test('returns false when statusCode is not 400', () => { + const error = { + data: { + statusCode: 404, + errorName: 'InvalidRequestAmountTooLow', + message: 'Source amount is below the minimum allowed, which is 50.00 USD', + context: { + minimumAllowed: 50, + }, + }, + } + const result = isInvalidRequestAmountTooLow(error) + expect(result).toBe(false) + }) + + test('returns false when errorName is not InvalidRequestAmountTooLow', () => { + const error = { + data: { + statusCode: 400, + errorName: 'InvalidRequestAmountTooSmall', + message: 'Source amount is below the minimum allowed, which is 50.00 USD', + context: { + minimumAllowed: 50, + }, + }, + } + const result = isInvalidRequestAmountTooLow(error) + expect(result).toBe(false) + }) +}) diff --git a/packages/uniswap/src/features/fiatOnRamp/utils.ts b/packages/uniswap/src/features/fiatOnRamp/utils.ts index 853d112c0bb..967da4c19cd 100644 --- a/packages/uniswap/src/features/fiatOnRamp/utils.ts +++ b/packages/uniswap/src/features/fiatOnRamp/utils.ts @@ -1,14 +1,100 @@ +import { FORLogo, FORQuote, FORServiceProvider } from 'uniswap/src/features/fiatOnRamp/types' import { isAndroid, isIOS } from 'utilities/src/platform' const APPLE_PAY = 'Apple Pay' const GOOGLE_PAY = 'Google Pay' export function transformPaymentMethods(paymentMethods: string[]): string[] { - return paymentMethods.filter( - (pm) => !(pm === APPLE_PAY && isAndroid) && !(pm === GOOGLE_PAY && isIOS) - ) + return paymentMethods.filter((pm) => !(pm === APPLE_PAY && isAndroid) && !(pm === GOOGLE_PAY && isIOS)) } export function getCountryFlagSvgUrl(countryCode: string): string { return `https://images-country.meld.io/${countryCode}/flag.svg` } + +export interface FORApiError { + data: { + statusCode: number + errorName: string + message: string + context: object | undefined + } +} + +export interface InvalidRequestAmountTooLow extends FORApiError { + data: FORApiError['data'] & { + statusCode: 400 + errorName: 'InvalidRequestAmountTooLow' + context: { + minimumAllowed: number + } + } +} + +export function isInvalidRequestAmountTooLow(error: FORApiError): error is InvalidRequestAmountTooLow { + const e = error as InvalidRequestAmountTooLow + return ( + e.data.statusCode === 400 && + e.data.errorName === 'InvalidRequestAmountTooLow' && + typeof e.data.context?.minimumAllowed === 'number' + ) +} + +export interface InvalidRequestAmountTooHigh extends FORApiError { + data: FORApiError['data'] & { + statusCode: 400 + errorName: 'InvalidRequestAmountTooHigh' + context: { + maximumAllowed: number + } + } +} + +export function isInvalidRequestAmountTooHigh(error: FORApiError): error is InvalidRequestAmountTooHigh { + const e = error as InvalidRequestAmountTooHigh + return ( + e.data.statusCode === 400 && + e.data.errorName === 'InvalidRequestAmountTooHigh' && + typeof e.data.context?.maximumAllowed === 'number' + ) +} + +export interface NoQuotesError extends FORApiError { + data: FORApiError['data'] & { + statusCode: 400 + errorName: 'NoQuotes' + } +} + +export function isNoQuotesError(error: FORApiError): error is InvalidRequestAmountTooHigh { + const e = error as NoQuotesError + return e.data.statusCode === 400 && e.data.errorName === 'NoQuotes' +} + +export function isFiatOnRampApiError(error: unknown): error is FORApiError { + if (typeof error === 'object' && error !== null) { + const e = error as FORApiError + return ( + typeof e.data === 'object' && + e.data !== null && + typeof e.data.statusCode === 'number' && + typeof e.data.errorName === 'string' + ) + } + return false +} + +export function getOptionalServiceProviderLogo(logos: FORLogo | undefined, isDarkMode: boolean): string | undefined { + return isDarkMode ? logos?.darkLogo : logos?.lightLogo +} + +export function getServiceProviderForQuote( + quote: FORQuote | undefined, + serviceProviders: FORServiceProvider[] | undefined, +): FORServiceProvider | undefined { + return serviceProviders?.find((sp) => sp.serviceProvider === quote?.serviceProvider) +} + +export function getServiceProviderLogo(logos: FORLogo, isDarkMode: boolean): string { + return isDarkMode ? logos.darkLogo : logos.lightLogo +} diff --git a/packages/uniswap/src/features/gating/configs.ts b/packages/uniswap/src/features/gating/configs.ts index a240edfab03..1fb8d02186c 100644 --- a/packages/uniswap/src/features/gating/configs.ts +++ b/packages/uniswap/src/features/gating/configs.ts @@ -8,6 +8,7 @@ import { isInterface } from 'utilities/src/platform' export enum DynamicConfigs { // Wallet MobileForceUpgrade, + OnDeviceRecovery, Slippage, UwuLink, @@ -21,6 +22,7 @@ export const WEB_CONFIG_NAMES = new Map([ export const WALLET_CONFIG_NAMES = new Map([ [DynamicConfigs.MobileForceUpgrade, 'force_upgrade'], + [DynamicConfigs.OnDeviceRecovery, 'on_device_recovery'], [DynamicConfigs.UwuLink, 'uwulink_config'], [DynamicConfigs.Slippage, 'slippage_configs'], ]) @@ -29,9 +31,7 @@ export function getConfigName(config: DynamicConfigs): string { const names = isInterface ? WEB_CONFIG_NAMES : WALLET_CONFIG_NAMES const name = names.get(config) if (!name) { - const err = new Error( - `Dynamic config ${DynamicConfigs[config]} does not have a name mapped for this application` - ) + const err = new Error(`Dynamic config ${DynamicConfigs[config]} does not have a name mapped for this application`) logger.error(err, { tags: { file: 'configs.ts', diff --git a/packages/uniswap/src/features/gating/experiments.ts b/packages/uniswap/src/features/gating/experiments.ts index bfe158afbc2..de4dfd3ba7c 100644 --- a/packages/uniswap/src/features/gating/experiments.ts +++ b/packages/uniswap/src/features/gating/experiments.ts @@ -8,8 +8,5 @@ export enum Experiments { } export type ExperimentProperties = { - [Experiments.ArbitrumXV2OpenOrders]: - | 'priceImprovementBps' - | 'forceOpenOrders' - | 'deadlineBufferSecs' + [Experiments.ArbitrumXV2OpenOrders]: 'priceImprovementBps' | 'forceOpenOrders' | 'deadlineBufferSecs' } diff --git a/packages/uniswap/src/features/gating/flags.ts b/packages/uniswap/src/features/gating/flags.ts index 61c2e032bf9..2abd691044c 100644 --- a/packages/uniswap/src/features/gating/flags.ts +++ b/packages/uniswap/src/features/gating/flags.ts @@ -9,7 +9,8 @@ export enum FeatureFlags { CurrencyConversion, // Wallet - ExtensionOnboarding, + ExtensionOnboarding, // this is beta onboarding, cant change name for version compatibility + ExtensionPromotionGA, FeedTab, ForAggregator, CexTransfers, @@ -31,17 +32,19 @@ export enum FeatureFlags { // Extension ExtensionBuyButton, + ExtensionBetaFeedbackPrompt, + ExtensionAutoConnect, // Web NavRefresh, NavigationHotkeys, Eip6936Enabled, ExitAnimation, - ExtensionBetaLaunch, - ExtensionGeneralLaunch, + ExtensionLaunch, ForAggregatorWeb, GqlTokenLists, LimitsFees, + L2NFTs, MultichainUX, MultichainExplore, MultipleRoutingOptions, @@ -66,10 +69,10 @@ export const WEB_FEATURE_FLAG_NAMES = new Map([ [FeatureFlags.NavigationHotkeys, 'navigation_hotkeys'], [FeatureFlags.Eip6936Enabled, 'eip6963_enabled'], [FeatureFlags.ExitAnimation, 'exit_animation'], - [FeatureFlags.ExtensionBetaLaunch, 'extension_beta_launch'], - [FeatureFlags.ExtensionGeneralLaunch, 'extension_general_launch'], + [FeatureFlags.ExtensionLaunch, 'extension_launch'], [FeatureFlags.GqlTokenLists, 'gql_token_lists'], [FeatureFlags.LimitsFees, 'limits_fees'], + [FeatureFlags.L2NFTs, 'l2_nfts'], [FeatureFlags.MultichainUX, 'multichain_ux'], [FeatureFlags.MultichainExplore, 'multichain_explore'], [FeatureFlags.MultipleRoutingOptions, 'multiple_routing_options'], @@ -92,6 +95,7 @@ export const WALLET_FEATURE_FLAG_NAMES = new Map([ [FeatureFlags.CurrencyConversion, 'currency_conversion'], // Wallet Specific [FeatureFlags.ExtensionOnboarding, 'extension-onboarding'], + [FeatureFlags.ExtensionPromotionGA, 'extension-promotion-ga'], [FeatureFlags.FeedTab, 'feed-tab'], [FeatureFlags.ForAggregator, 'for-aggregator'], [FeatureFlags.CexTransfers, 'cex-transfers'], @@ -112,6 +116,8 @@ export const WALLET_FEATURE_FLAG_NAMES = new Map([ [FeatureFlags.UniswapX, 'uniswapx'], // Extension Specific [FeatureFlags.ExtensionBuyButton, 'extension-buy-button'], + [FeatureFlags.ExtensionBetaFeedbackPrompt, 'extension-beta-feedback-prompt'], + [FeatureFlags.ExtensionAutoConnect, 'extension-auto-connect'], ]) export enum FeatureFlagClient { @@ -126,16 +132,10 @@ const FEATURE_FLAG_NAMES = { export function getFeatureFlagName(flag: FeatureFlags, client?: FeatureFlagClient): string { const names = - client !== undefined - ? FEATURE_FLAG_NAMES[client] - : isInterface - ? WEB_FEATURE_FLAG_NAMES - : WALLET_FEATURE_FLAG_NAMES + client !== undefined ? FEATURE_FLAG_NAMES[client] : isInterface ? WEB_FEATURE_FLAG_NAMES : WALLET_FEATURE_FLAG_NAMES const name = names.get(flag) if (!name) { - const err = new Error( - `Feature ${FeatureFlags[flag]} does not have a name mapped for this application` - ) + const err = new Error(`Feature ${FeatureFlags[flag]} does not have a name mapped for this application`) logger.error(err, { tags: { diff --git a/packages/uniswap/src/features/gating/hooks.ts b/packages/uniswap/src/features/gating/hooks.ts index de2cae08ab0..3edd647c4ab 100644 --- a/packages/uniswap/src/features/gating/hooks.ts +++ b/packages/uniswap/src/features/gating/hooks.ts @@ -30,7 +30,7 @@ export function useExperimentGroupName(experiment: Experiments): string | null { export function useExperimentValue< Exp extends keyof ExperimentProperties, Param extends ExperimentProperties[Exp], - ValType + ValType, >(experiment: Exp, param: Param, defaultValue: ValType): ValType { const statsigExperiment = useExperiment(experiment).config return statsigExperiment.get(param, defaultValue, (value): value is ValType => { @@ -41,7 +41,7 @@ export function useExperimentValue< export function useExperimentValueWithExposureLoggingDisabled< Exp extends keyof ExperimentProperties, Param extends ExperimentProperties[Exp], - ValType + ValType, >(experiment: Exp, param: Param, defaultValue: ValType): ValType { const statsigExperiment = useExperimentWithExposureLoggingDisabled(experiment).config return statsigExperiment.get(param, defaultValue, (value): value is ValType => { diff --git a/packages/uniswap/src/features/gating/overrides/customPersistedOverrides.native.ts b/packages/uniswap/src/features/gating/overrides/customPersistedOverrides.native.ts index 3b648b15063..f15e88d04c4 100644 --- a/packages/uniswap/src/features/gating/overrides/customPersistedOverrides.native.ts +++ b/packages/uniswap/src/features/gating/overrides/customPersistedOverrides.native.ts @@ -1,7 +1,6 @@ -// eslint-disable-next-line no-restricted-imports import AsyncStorage from '@react-native-async-storage/async-storage' import { Statsig, StatsigOverrides } from 'uniswap/src/features/gating/sdk/statsig' -import { isDevEnv } from 'uniswap/src/utils/env' +import { isDevEnv } from 'utilities/src/environment' import { logger } from 'utilities/src/logger/logger' const STATSIG_PERSISTED_OVERRIDE_KEY = 'STATSIG_PERSISTED_OVERRIDE_KEY' @@ -38,11 +37,7 @@ export function loadStatsigOverrides(): void { } } } catch (error) { - logger.debug( - 'persistedOverrides', - 'useLoadStatsigOverrides', - `Failed to load persisted overrides: ${error}` - ) + logger.debug('persistedOverrides', 'useLoadStatsigOverrides', `Failed to load persisted overrides: ${error}`) } } @@ -54,11 +49,9 @@ export function loadStatsigOverrides(): void { if (isDevEnv()) { function syncWithStorage(): void { const overrides = Statsig.getAllOverrides() - AsyncStorage.setItem(STATSIG_PERSISTED_OVERRIDE_KEY, JSON.stringify(overrides)).catch( - (error) => { - logger.debug('persistedOverrides', 'persistOverrides', error) - } - ) + AsyncStorage.setItem(STATSIG_PERSISTED_OVERRIDE_KEY, JSON.stringify(overrides)).catch((error) => { + logger.debug('persistedOverrides', 'persistOverrides', error) + }) } const overrideConfig = Statsig.overrideConfig.bind(Statsig) diff --git a/packages/uniswap/src/features/telemetry/Trace.tsx b/packages/uniswap/src/features/telemetry/Trace.tsx index 3ddf52bb7a3..3c660b30405 100644 --- a/packages/uniswap/src/features/telemetry/Trace.tsx +++ b/packages/uniswap/src/features/telemetry/Trace.tsx @@ -51,7 +51,8 @@ function _Trace({ logKeyPress={logKeyPress} logPress={logPress} properties={typedProps} - {...rest}> + {...rest} + > {children} ) diff --git a/packages/uniswap/src/features/telemetry/constants/extension.ts b/packages/uniswap/src/features/telemetry/constants/extension.ts index 5eb95cc1bef..ffe33961479 100644 --- a/packages/uniswap/src/features/telemetry/constants/extension.ts +++ b/packages/uniswap/src/features/telemetry/constants/extension.ts @@ -9,5 +9,7 @@ export enum ExtensionEventName { ExtensionLoad = 'Extension Load', ExtensionEthMethodRequest = 'Extension Eth Method Request', ProviderDirectMethodRequest = 'Provider Direct Method Request', + SidebarDisconnect = 'Sidebar Disconnect', + SidebarSwitchChain = 'Sidebar Switch Chain', UnknownMethodRequest = 'Unknown Method Request', } diff --git a/packages/uniswap/src/features/telemetry/constants/mobile.ts b/packages/uniswap/src/features/telemetry/constants/mobile.ts index 54b233a5bf3..ca13e353b3f 100644 --- a/packages/uniswap/src/features/telemetry/constants/mobile.ts +++ b/packages/uniswap/src/features/telemetry/constants/mobile.ts @@ -3,6 +3,7 @@ */ export enum MobileEventName { AppRating = 'App Rating', + AutomatedOnDeviceRecoveryTriggered = 'Automated On Device Recovery Triggered', BalancesReport = 'Balances Report', DeepLinkOpened = 'Deep Link Opened', ExploreFilterSelected = 'Explore Filter Selected', diff --git a/packages/uniswap/src/features/telemetry/constants/trace.ts b/packages/uniswap/src/features/telemetry/constants/trace.ts index 0e6dcbbec5e..c897633a381 100644 --- a/packages/uniswap/src/features/telemetry/constants/trace.ts +++ b/packages/uniswap/src/features/telemetry/constants/trace.ts @@ -15,11 +15,11 @@ export const ModalName = { CloudBackupInfo: 'cloud-backup-info-modal', DappRequest: 'dapp-request', ENSClaimPeriod: 'ens-claim-period', + EnterPassword: 'enter-password-modal', ExchangeTransferModal: 'exchange-transfer-modal', Experiments: 'experiments', Explore: 'explore-modal', ExtensionPromoModal: 'extension-promo', - ExtensionWaitlistModal: 'extension-waitlist', ExtensionBetaFeedbackModal: 'extension-beta-feedback', FaceIDWarning: 'face-id-warning', FOTInfo: 'fee-on-transfer', @@ -139,6 +139,7 @@ export const ElementName = { MoonpayExplorerView: 'moonpay-explorer-view', NetworkButton: 'network-button', Next: 'next', + NftItem: 'nft-item', OK: 'ok', OnboardingImportBackup: 'onboarding-import-backup', OnboardingImportSeedPhrase: 'onboarding-import-seed-phrase', diff --git a/packages/uniswap/src/features/telemetry/send.native.ts b/packages/uniswap/src/features/telemetry/send.native.ts index 04d341ec344..4152bc31d4f 100644 --- a/packages/uniswap/src/features/telemetry/send.native.ts +++ b/packages/uniswap/src/features/telemetry/send.native.ts @@ -1,10 +1,6 @@ -// eslint-disable-next-line no-restricted-imports import appsFlyer from 'react-native-appsflyer' -import { - AppsFlyerEventProperties, - UniverseEventProperties, -} from 'uniswap/src/features/telemetry/types' -import { isBetaEnv, isDevEnv } from 'uniswap/src/utils/env' +import { AppsFlyerEventProperties, UniverseEventProperties } from 'uniswap/src/features/telemetry/types' +import { isBetaEnv, isDevEnv } from 'utilities/src/environment' import { logger } from 'utilities/src/logger/logger' // eslint-disable-next-line no-restricted-imports import { analytics } from 'utilities/src/telemetry/analytics/analytics' @@ -25,11 +21,7 @@ export async function sendAppsFlyerEvent { const [eventName, eventProperties] = args if (__DEV__ || isDevEnv() || isBetaEnv()) { - logger.debug( - 'telemetry/send.native.ts', - 'sendWalletAppsFlyerEvent', - JSON.stringify({ eventName, eventProperties }) - ) + logger.debug('telemetry/send.native.ts', 'sendWalletAppsFlyerEvent', JSON.stringify({ eventName, eventProperties })) } else { await appsFlyer.logEvent(eventName, eventProperties ?? {}) } diff --git a/packages/uniswap/src/features/telemetry/send.ts b/packages/uniswap/src/features/telemetry/send.ts index f797a410e56..d472dc9d368 100644 --- a/packages/uniswap/src/features/telemetry/send.ts +++ b/packages/uniswap/src/features/telemetry/send.ts @@ -1,7 +1,4 @@ -import { - AppsFlyerEventProperties, - UniverseEventProperties, -} from 'uniswap/src/features/telemetry/types' +import { AppsFlyerEventProperties, UniverseEventProperties } from 'uniswap/src/features/telemetry/types' import { NotImplementedError } from 'utilities/src/errors' export function sendAnalyticsEvent( diff --git a/packages/uniswap/src/features/telemetry/send.web.ts b/packages/uniswap/src/features/telemetry/send.web.ts index 961b0ec22e9..b1c9c38c4ee 100644 --- a/packages/uniswap/src/features/telemetry/send.web.ts +++ b/packages/uniswap/src/features/telemetry/send.web.ts @@ -1,7 +1,4 @@ -import { - AppsFlyerEventProperties, - UniverseEventProperties, -} from 'uniswap/src/features/telemetry/types' +import { AppsFlyerEventProperties, UniverseEventProperties } from 'uniswap/src/features/telemetry/types' import { logger } from 'utilities/src/logger/logger' // eslint-disable-next-line no-restricted-imports import { analytics } from 'utilities/src/telemetry/analytics/analytics' diff --git a/packages/uniswap/src/features/telemetry/types.ts b/packages/uniswap/src/features/telemetry/types.ts index 9629b0c62b0..2c931d48310 100644 --- a/packages/uniswap/src/features/telemetry/types.ts +++ b/packages/uniswap/src/features/telemetry/types.ts @@ -44,12 +44,7 @@ import { LimitsExpiry } from 'uniswap/src/types/limits' import { ImportType } from 'uniswap/src/types/onboarding' import { SwapTab } from 'uniswap/src/types/screens/interface' import { ShareableEntity } from 'uniswap/src/types/sharing' -import { - EthMethod, - UwULinkMethod, - WCEventType, - WCRequestOutcome, -} from 'uniswap/src/types/walletConnect' +import { EthMethod, UwULinkMethod, WCEventType, WCRequestOutcome } from 'uniswap/src/types/walletConnect' import { WidgetEvent, WidgetType } from 'uniswap/src/types/widgets' import { WrapType } from 'uniswap/src/types/wrap' import { ITraceContext } from 'utilities/src/telemetry/trace/TraceContext' @@ -190,6 +185,11 @@ export type UniverseEventProperties = { [ExtensionEventName.ProviderDirectMethodRequest]: WindowEthereumRequestProperties [ExtensionEventName.ExtensionEthMethodRequest]: WindowEthereumRequestProperties [ExtensionEventName.DeprecatedMethodRequest]: WindowEthereumRequestProperties + [ExtensionEventName.SidebarSwitchChain]: { + previousChainId?: number + newChainId: number + } + [ExtensionEventName.SidebarDisconnect]: undefined [ExtensionEventName.UnknownMethodRequest]: WindowEthereumRequestProperties [FiatOnRampEventName.FiatOnRampAmountEntered]: ITraceContext & { source: 'chip' | 'textInput' @@ -354,6 +354,15 @@ export type UniverseEventProperties = { appRatingPromptedMs?: number appRatingProvidedMs?: number } + [MobileEventName.AutomatedOnDeviceRecoveryTriggered]: { + showNotificationScreen: boolean + showBiometricsScreen: boolean + notificationOSPermission: string + hasAnyNotificationsEnabled: boolean + deviceSupportsBiometrics: boolean | undefined + isBiometricsEnrolled: boolean | undefined + isBiometricAuthEnabled: boolean + } [MobileEventName.BalancesReport]: { total_balances_usd: number wallets: string[] diff --git a/packages/uniswap/src/features/telemetry/user.ts b/packages/uniswap/src/features/telemetry/user.ts index 0689b08b959..0eba3dcdf55 100644 --- a/packages/uniswap/src/features/telemetry/user.ts +++ b/packages/uniswap/src/features/telemetry/user.ts @@ -48,7 +48,7 @@ export enum ExtensionUserPropertyName { export function setUserProperty( property: MobileUserPropertyName | ExtensionUserPropertyName | CustomUserProperties, value: UserPropertyValue, - insert?: boolean + insert?: boolean, ): void { analytics.setUserProperty(property, value, insert) } diff --git a/packages/uniswap/src/features/tokens/safetyHooks.ts b/packages/uniswap/src/features/tokens/safetyHooks.ts new file mode 100644 index 00000000000..03ab0c1bf9c --- /dev/null +++ b/packages/uniswap/src/features/tokens/safetyHooks.ts @@ -0,0 +1,14 @@ +import { ThemeKeys } from 'ui/src' +import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' + +export function useTokenSafetyLevelColors(safetyLevel: Maybe): ThemeKeys { + switch (safetyLevel) { + case SafetyLevel.MediumWarning: + return 'DEP_accentWarning' + case SafetyLevel.StrongWarning: + return 'statusCritical' + case SafetyLevel.Blocked: + default: + return 'neutral2' + } +} diff --git a/packages/uniswap/src/features/unitags/api.ts b/packages/uniswap/src/features/unitags/api.ts index 71d3e11388c..a32a0da7112 100644 --- a/packages/uniswap/src/features/unitags/api.ts +++ b/packages/uniswap/src/features/unitags/api.ts @@ -37,7 +37,7 @@ export const unitagsApolloClient = new ApolloClient({ export function addQueryParamsToEndpoint( endpoint: string, - params: Record + params: Record, ): string { const url = new URL(endpoint, uniswapUrls.apiOrigin) // dummy base URL, we only need the path with query params Object.entries(params).forEach(([key, value]) => { @@ -49,9 +49,7 @@ export function addQueryParamsToEndpoint( return url.pathname + url.search } -export function useUnitagQuery( - username?: string -): ReturnType> { +export function useUnitagQuery(username?: string): ReturnType> { return useRestQuery>( addQueryParamsToEndpoint('/username', { username }), { username }, // dummy body so that cache key is unique per query params @@ -61,13 +59,11 @@ export function useUnitagQuery( ttlMs: ONE_MINUTE_MS * 2, }, 'GET', - unitagsApolloClient + unitagsApolloClient, ) } -export function useUnitagByAddressQuery( - address?: Address -): ReturnType> { +export function useUnitagByAddressQuery(address?: Address): ReturnType> { return useRestQuery>( addQueryParamsToEndpoint('/address', { address }), { address }, // dummy body so that cache key is unique per query params @@ -77,13 +73,13 @@ export function useUnitagByAddressQuery( ttlMs: ONE_MINUTE_MS * 2, }, 'GET', - unitagsApolloClient + unitagsApolloClient, ) } export function useWaitlistPositionQuery( accounts: Address[], - skip: boolean + skip: boolean, ): ReturnType> { const addresses = accounts.join(',') return useRestQuery>( @@ -95,6 +91,6 @@ export function useWaitlistPositionQuery( ttlMs: ONE_MINUTE_MS * 2, }, 'GET', - unitagsApolloClient + unitagsApolloClient, ) } diff --git a/packages/uniswap/src/features/unitags/context.tsx b/packages/uniswap/src/features/unitags/context.tsx index 0845b1479be..89040e89bfa 100644 --- a/packages/uniswap/src/features/unitags/context.tsx +++ b/packages/uniswap/src/features/unitags/context.tsx @@ -7,9 +7,7 @@ type UnitagUpdaterContextType = { const UnitagUpdaterContext = createContext(null) -export function UnitagUpdaterContextProvider({ - children, -}: PropsWithChildren): JSX.Element { +export function UnitagUpdaterContextProvider({ children }: PropsWithChildren): JSX.Element { const [refetchUnitagsCounter, setRefetchUnitagsCounter] = useState(0) const triggerRefetchUnitags = (): void => { diff --git a/packages/uniswap/src/i18n/locales/source/en-US.json b/packages/uniswap/src/i18n/locales/source/en-US.json index 424ecf88588..efd1eabc6ae 100644 --- a/packages/uniswap/src/i18n/locales/source/en-US.json +++ b/packages/uniswap/src/i18n/locales/source/en-US.json @@ -147,9 +147,12 @@ "common.navigation.settings": "Settings", "common.navigation.systemSettings": "Settings", "common.text.connected": "Connected", + "common.text.contract": "Contract", "common.text.disconnected": "Disconnected", "common.text.error": "Error", "common.text.notAvailable": "N/A", + "common.text.recipient": "To", + "common.text.sender": "From", "common.text.unknown": "Unknown", "currency.aud": "Australian Dollar", "currency.brl": "Brazilian Real", @@ -171,17 +174,15 @@ "currency.usd": "United States Dollar", "currency.vnd": "Vietnamese Dong", "dapp.request.approve.action": "Approve", - "dapp.request.approve.fallbackTitle": "Approve spending tokens", - "dapp.request.approve.helptext": "Allow this site to access and spend this token from your wallet.", + "dapp.request.approve.fallbackTitle": "Approve this site to access tokens", + "dapp.request.approve.helptext": "Allow this site to access and spend this token for transactions. Make sure you trust this site.", "dapp.request.approve.label": "Wallet", - "dapp.request.approve.title": "Approve spending {{tokenSymbol}}", - "dapp.request.base.title": "Confirm transaction", + "dapp.request.approve.title": "Approve access to {{tokenSymbol}}", + "dapp.request.base.title": "Transaction request", "dapp.request.connect.helptext": "Allow this site to view your wallet address, balance, and request approvals for transactions.", "dapp.request.connect.title": "Connect to site", - "dapp.request.fallback.calldata.label": "data", - "dapp.request.fallback.function.label": "function", - "dapp.request.fallback.recipient.label": "to", - "dapp.request.fallback.sending.label": "sending", + "dapp.request.fallback.calldata.label": "Raw data", + "dapp.request.fallback.function.label": "Function", "dapp.request.permit2.description": "Permit2 manages token approvals across multiple dapps.", "dapp.request.permit2.header": "Sign Permit2", "dapp.request.reject.action": "Reject all", @@ -276,7 +277,6 @@ "fiatOnRamp.error.unsupported": "Not supported in region", "fiatOnRamp.error.usd": "Only available to purchase in USD", "fiatOnRamp.quote.advice": "You’ll continue to the provider’s portal to see the fees associated with your transaction.", - "fiatOnRamp.quote.others": "others", "fiatOnRamp.quote.type.other": "Other options", "fiatOnRamp.quote.type.recent": "Recently used", "fiatOnRamp.region.placeholder": "Search by country or region", @@ -293,8 +293,10 @@ "home.activity.empty.title": "No activity yet", "home.activity.error.load": "Couldn’t load activity", "home.activity.title": "Activity", - "home.banner.extension.confirm": "Join Beta", - "home.banner.extension.message": "Be the first to try out the Uniswap Extension on your web browser", + "home.banner.extension.confirm.beta": "Join Beta", + "home.banner.extension.confirm.default": "Download", + "home.banner.extension.message.beta": "Be the first to try out the Uniswap Extension on your web browser", + "home.banner.extension.message.default": "Download on Chrome to access this wallet from your desktop", "home.banner.extension.title": "Uniswap Extension is here", "home.banner.offline": "You are in offline mode", "home.extension.error": "Error loading accounts", @@ -307,10 +309,12 @@ "home.label.scan": "Scan", "home.label.send": "Send", "home.label.swap": "Swap", - "home.modal.getExtension.step1": "1. Visit uniswap.org/ext on your computer", - "home.modal.getExtension.step2": "2. Add the Uniswap Extension on your Chrome browser", - "home.modal.getExtension.step3": "3. Enter your username to get access", - "home.modal.getExtension.title": "Join the Uniswap Extension Beta", + "home.modal.getExtension.beta.step3": "3. Enter your username to get access", + "home.modal.getExtension.beta.title": "Join the Uniswap Extension Beta", + "home.modal.getExtension.ga.step1": "1. Visit uniswap.org/ext on Chrome desktop", + "home.modal.getExtension.ga.step2": "2. Add the Uniswap Extension to your browser", + "home.modal.getExtension.ga.step3": "3. Scan the QR code with your Uniswap mobile app to import your wallet", + "home.modal.getExtension.ga.title": "Download the Uniswap Extension", "home.nfts.title": "NFTs", "home.tokens.empty.action.buy.description": "Purchase crypto with a debit card or a bank account.", "home.tokens.empty.action.buy.title": "Buy crypto with card", @@ -396,13 +400,14 @@ "notifications.scantastic.subtitle": "Continue on Uniswap Extension", "notifications.scantastic.title": "Success!", "onboarding.backup.manual.banner": "It’s best to write this on a piece of paper and store it in a safe place or in a secure password manager.", + "onboarding.backup.manual.error": "Invalid or misspelled word", "onboarding.backup.manual.placeholder": "Secret word", "onboarding.backup.manual.progress": "{{completedStepsCount}}/{{totalStepsCount}} completed", "onboarding.backup.manual.selectedWordPlaceholder": "Select word", - "onboarding.backup.manual.subtitle_one": "What’s the {{count}}st word in your recovery phrase?", - "onboarding.backup.manual.subtitle_two": "What’s the {{count}}nd word in your recovery phrase?", - "onboarding.backup.manual.subtitle_few": "What’s the {{count}}rd word in your recovery phrase?", - "onboarding.backup.manual.subtitle_other": "What’s the {{count}}th word in your recovery phrase?", + "onboarding.backup.manual.subtitle_one": "What’s the {{count}}st word in your recovery phrase?", + "onboarding.backup.manual.subtitle_two": "What’s the {{count}}nd word in your recovery phrase?", + "onboarding.backup.manual.subtitle_few": "What’s the {{count}}rd word in your recovery phrase?", + "onboarding.backup.manual.subtitle_other": "What’s the {{count}}th word in your recovery phrase?", "onboarding.backup.manual.title": "Let’s make sure you’ve recorded it correctly", "onboarding.backup.option.cloud.description": "Encrypt your recovery phrase with a secure password", "onboarding.backup.option.cloud.title": "{{cloudProviderName}} backup", @@ -411,7 +416,7 @@ "onboarding.backup.subtitle": "Backups let you restore your wallet if you delete the app or lose your device", "onboarding.backup.title.existing": "Back up your wallet", "onboarding.backup.title.new": "Choose a backup method", - "onboarding.backup.view.disclaimer": "I understand that if I lose my recovery phrase, Uniswap Labs cannot help me restore it", + "onboarding.backup.view.disclaimer": "I understand that if I lose my recovery phrase, Uniswap Labs cannot help me restore it.", "onboarding.backup.view.subtitle.message1": "Read the following carefully before continuing", "onboarding.backup.view.subtitle.message2": "You’ll need to enter all 12 of these secret words to recover your wallet.", "onboarding.backup.view.title": "Write down your recovery phrase", @@ -428,13 +433,13 @@ "onboarding.complete.pin.description": "Click the pin icon to add Uniswap Extension to your toolbar.", "onboarding.complete.pin.title": "Pin Uniswap Extension", "onboarding.complete.title": "You’re all set", - "onboarding.extension.connectMobile.button": "Import from your phone", - "onboarding.extension.connectMobile.title": "Have the Uniswap mobile app?", "onboarding.extension.getOnTheBetaWaitlist.subtitle": "Download the mobile app to claim a username", "onboarding.extension.getOnTheBetaWaitlist.title": "Get on the Beta waitlist", "onboarding.extension.password.subtitle": "You’ll need this to unlock your wallet and access your recovery phrase", "onboarding.extension.password.title.default": "Create password", "onboarding.extension.password.title.reset": "Reset your password", + "onboarding.extension.unsupported.description": "Uniswap Extension is only compatible with Chrome right now.", + "onboarding.extension.unsupported.title": "This browser is not supported (yet)", "onboarding.import.error.invalidWords_one": "1 word is invalid or misspelled", "onboarding.import.error.invalidWords_other": "{{count}} words are invalid or misspelled", "onboarding.import.method.import.message": "Enter your recovery phrase from another crypto wallet", @@ -458,7 +463,8 @@ "onboarding.importMnemonic.subtitle": "Type or paste your 12-word recovery phrase", "onboarding.importMnemonic.title": "Enter your recovery phrase", "onboarding.intro.button.alreadyHave": "I already have a wallet", - "onboarding.intro.title": "Welcome to \nUniswap Wallet", + "onboarding.intro.mobileScan.button": "Scan QR code to import", + "onboarding.intro.mobileScan.title": "Have the Uniswap app?", "onboarding.introBetaWaitlist.button.checkEligibility": "Check eligibility", "onboarding.introBetaWaitlist.button.letsGo": "Let’s go", "onboarding.introBetaWaitlist.checkEligibilityInstructions": "Enter your uni.eth username below to check if you’re eligible for the Beta.", @@ -467,7 +473,7 @@ "onboarding.introBetaWaitlist.ineligibleExplanation": "You’re still on the waitlist. We’ll notify you in the Uniswap mobile app when you become eligible!", "onboarding.introBetaWaitlist.unitagPlaceholder": "username", "onboarding.landing.button.add": "Add an existing wallet", - "onboarding.landing.button.create": "Create a new wallet", + "onboarding.landing.button.create": "Create a wallet", "onboarding.notification.permission.message": "To receive notifications, turn on notifications for Uniswap Wallet in your device’s settings.", "onboarding.notification.permission.title": "Notifications permission", "onboarding.notification.subtitle": "Get notified when your transfers, swaps, and approvals complete.", @@ -554,10 +560,6 @@ "scantastic.error.timeout.title": "Your connection timed out", "scantastic.modal.ipMismatch.description": "To scan this QR code, your phone must be connected to the same WiFi network as your computer.", "scantastic.modal.ipMismatch.title": "Switch your WiFi network", - "scantastic.modal.notOnWaitlist.message": "In order to become eligible for the Uniswap Extension Beta, join the waitlist by claiming a uni.eth username", - "scantastic.modal.notOnWaitlist.title": "You’re not on the waitlist", - "scantastic.modal.onWaitlist.message": "We’ll notify you in the app when you become eligible to join the Uniswap Extension Beta.", - "scantastic.modal.onWaitlist.title": "You’re still on the waitlist", "send.button.review": "Review transfer", "send.button.send": "Send", "send.gas.error.title": "N/A", @@ -574,12 +576,9 @@ "send.recipient.warning.viewOnly.title": "You have this as a view-only wallet", "send.recipientSelect.search.empty.message": "When you send tokens to a wallet address, they’ll show up here", "send.recipientSelect.search.empty.title": "No wallets saved", - "send.recipientSelect.title": "To", - "send.review.input.tokenAmount.title": "{{currencyAmount}} of {{currencySymbol}}", "send.review.modal.title": "You’re sending", "send.review.summary.button.title": "Confirm send", "send.review.summary.sending": "Sending", - "send.review.summary.to": "To", "send.search.empty.subtitle": "The address you typed either does not exist or is spelled incorrectly.", "send.search.empty.title": "No results found", "send.search.placeholder": "Search ENS or address", @@ -614,20 +613,18 @@ "send.warning.viewOnly.title": "This wallet is view-only", "setting.recoveryPhrase.account.show": "Show recovery phrase", "setting.recoveryPhrase.action.hide": "Hide recovery phrase", - "setting.recoveryPhrase.remove.button": "Remove recovery phrase", - "setting.recoveryPhrase.remove.confirm.subtitle": "I understand that Uniswap Labs can’t help me recover my wallet if I failed to do so", + "setting.recoveryPhrase.remove": "Remove recovery phrase", + "setting.recoveryPhrase.remove.confirm.subtitle": "I understand that Uniswap Labs can’t help me recover my wallet if I failed to do so.", "setting.recoveryPhrase.remove.confirm.title": "I saved my recovery phrase", - "setting.recoveryPhrase.remove.initial.subtitle": "Make sure you’ve saved your recovery phrase. You will lose access to your funds otherwise", + "setting.recoveryPhrase.remove.initial.subtitle": "Make sure you’ve saved your recovery phrase. You will lose access to your wallets otherwise", "setting.recoveryPhrase.remove.initial.title": "Before you continue", "setting.recoveryPhrase.remove.password.error": "Wrong password. Try again", - "setting.recoveryPhrase.remove.password.input": "Enter password", - "setting.recoveryPhrase.remove.subtitle": "Enter your password to continue", + "setting.recoveryPhrase.remove.subtitle": "Enter your password to confirm", "setting.recoveryPhrase.remove.title": "You’re removing your recovery phrase", - "setting.recoveryPhrase.view.error": "Wrong password, try again", "setting.recoveryPhrase.view.warning.message1": "Anyone who knows your recovery phrase can access your wallet and funds", "setting.recoveryPhrase.view.warning.message2": "View this in private", - "setting.recoveryPhrase.view.warning.message3": "Do not share this with anyone", - "setting.recoveryPhrase.view.warning.message4": "Never enter it to any websites or apps", + "setting.recoveryPhrase.view.warning.message3": "Do not share with anyone", + "setting.recoveryPhrase.view.warning.message4": "Never enter it to any websites or applications", "setting.recoveryPhrase.view.warning.title": "Before you continue", "setting.recoveryPhrase.warning.screenshot.message": "Anyone who gains access to your photos can access your wallet. We recommend that you write down your words instead.", "setting.recoveryPhrase.warning.screenshot.title": "Screenshots aren’t secure", @@ -648,7 +645,6 @@ "settings.section.wallet.button.viewLess": "View less", "settings.section.wallet.label.viewOnly": "View-only", "settings.section.wallet.title": "Wallet settings", - "settings.setting.appearance.option.auto": "Auto", "settings.setting.appearance.option.dark.subtitle": "Always use dark mode", "settings.setting.appearance.option.dark.title": "Dark mode", "settings.setting.appearance.option.device.subtitle": "Default to your device’s appearance", @@ -712,11 +708,9 @@ "settings.setting.privacy.analytics.description": "We use anonymous usage data to enhance your experience across Uniswap Labs products. When disabled, we only track errors and essential usage.", "settings.setting.privacy.analytics.title": "Allow analytics", "settings.setting.privacy.title": "Privacy", - "settings.setting.recoveryPhrase.remove": "Remove recovery phrase", + "settings.setting.recoveryPhrase.password.title": "Enter your password", "settings.setting.recoveryPhrase.title": "Recovery phrase", - "settings.setting.recoveryPhrase.view": "View recovery phrase", "settings.setting.smallBalances.title": "Hide small balances", - "settings.setting.theme.title": "Theme", "settings.setting.unknownTokens.title": "Hide unknown tokens", "settings.setting.wallet.action.editLabel": "Edit label", "settings.setting.wallet.action.editProfile": "Edit profile", @@ -807,7 +801,6 @@ "token.balances.other": "Balances on other networks", "token.balances.viewOnly": "{{ownerAddress}}’s balance", "token.error.unknown": "Unknown token", - "token.links.contract": "Contract", "token.links.title": "Links", "token.links.twitter": "Twitter", "token.links.website": "Website", @@ -886,6 +879,8 @@ "transaction.amount.unlimited": "Unlimited", "transaction.currency.unknown": "unknown token", "transaction.date": "Submitted on {{date}}", + "transaction.details.dappName": "App", + "transaction.details.from": "From", "transaction.details.networkFee": "Network cost", "transaction.details.transactionId": "Transaction ID", "transaction.network.all": "All networks", @@ -1064,22 +1059,19 @@ "walletConnect.pending.button.scrollDown": "Scroll down to connect", "walletConnect.pending.switchAccount": "Switch Account", "walletConnect.pending.switchNetwork": "Switch Network", - "walletConnect.pending.title": "{{dappName}} wants to connect to your wallet", + "walletConnect.pending.title": "Connect to {{dappName}}", "walletConnect.permissions.networks": "Networks", "walletConnect.permissions.option.transferAssets": "Transfer your assets without consent", "walletConnect.permissions.option.viewTokenBalances": "View your token balances", "walletConnect.permissions.option.viewWalletAddress": "View your wallet address", - "walletConnect.permissions.title": "App permissions", + "walletConnect.permissions.title": "Site permissions", "walletConnect.request.button.scrollDown": "Scroll down to sign", "walletConnect.request.button.sign": "Sign", - "walletConnect.request.details.label.function": "Function: ", - "walletConnect.request.details.label.recipient": "To: ", - "walletConnect.request.details.label.sending": "Sending: ", + "walletConnect.request.details.label.function": "Function", + "walletConnect.request.details.label.sending": "Sending", "walletConnect.request.error.insufficientFunds": "You don’t have enough {{currencySymbol}} to complete this transaction.", "walletConnect.request.error.network": "Internet or network connection error", - "walletConnect.request.label.network": "Network", "walletConnect.request.warning.general.message": "Be careful: this message may transfer assets", - "walletConnect.request.warning.general.transaction": "Be careful: this transaction may transfer assets", "walletConnect.request.warning.message": "In order to sign messages or transactions, you’ll need to import the wallet’s recovery phrase.", "walletConnect.request.warning.title": "This wallet is in view-only mode" } diff --git a/packages/uniswap/src/test/render.tsx b/packages/uniswap/src/test/render.tsx index 08df6441989..38ee4111586 100644 --- a/packages/uniswap/src/test/render.tsx +++ b/packages/uniswap/src/test/render.tsx @@ -13,10 +13,7 @@ import 'uniswap/src/i18n/i18n' * @param preloadedState and store * @returns `ui` wrapped with providers */ -export function renderWithProviders( - ui: React.ReactElement, - renderOptions: RenderOptions = {} -): RenderResult { +export function renderWithProviders(ui: React.ReactElement, renderOptions: RenderOptions = {}): RenderResult { function Wrapper({ children }: PropsWithChildren): JSX.Element { return {children} } diff --git a/packages/uniswap/src/types/screens/extension.ts b/packages/uniswap/src/types/screens/extension.ts index 7d87ed52f70..fa53314b820 100644 --- a/packages/uniswap/src/types/screens/extension.ts +++ b/packages/uniswap/src/types/screens/extension.ts @@ -1 +1,7 @@ -export type ExtensionScreen = 'None Yet' +export enum HomeTabs { + Tokens = 'Tokens', + NFTs = 'NFTs', + Activity = 'Activity', +} + +export type ExtensionScreen = HomeTabs diff --git a/packages/wallet/src/utils/addresses.test.ts b/packages/uniswap/src/utils/addresses.test.ts similarity index 95% rename from packages/wallet/src/utils/addresses.test.ts rename to packages/uniswap/src/utils/addresses.test.ts index 893921b5819..c8789ff272a 100644 --- a/packages/wallet/src/utils/addresses.test.ts +++ b/packages/uniswap/src/utils/addresses.test.ts @@ -1,4 +1,4 @@ -import { getValidAddress } from 'wallet/src/utils/addresses' +import { getValidAddress } from 'uniswap/src/utils/addresses' it('returns lower case address for valid address', () => { const validAddress = '0x71C7656EC7ab88b098defB751B7401B5f6d8976F' diff --git a/packages/wallet/src/utils/addresses.ts b/packages/uniswap/src/utils/addresses.ts similarity index 93% rename from packages/wallet/src/utils/addresses.ts rename to packages/uniswap/src/utils/addresses.ts index bbc0a4efa9b..30001ca304a 100644 --- a/packages/wallet/src/utils/addresses.ts +++ b/packages/uniswap/src/utils/addresses.ts @@ -1,4 +1,5 @@ -import { logger, utils } from 'ethers' +import { getAddress } from '@ethersproject/address' +import { logger } from 'utilities/src/logger/logger' export enum AddressStringFormat { Lowercase, @@ -21,11 +22,7 @@ export enum AddressStringFormat { * @param log If logging is enabled in case of errors * @returns The normalized address or false if the address is invalid */ -export function getValidAddress( - address: Maybe, - withChecksum = false, - log = true -): Nullable { +export function getValidAddress(address: Maybe, withChecksum = false, log = true): Nullable { if (!address) { return null } @@ -34,7 +31,7 @@ export function getValidAddress( if (withChecksum) { try { - return utils.getAddress(addressWith0x) + return getAddress(addressWith0x) } catch (error) { if (log) { logger.warn('utils/addresses', 'getValidAddress', `Invalid address at checksum: ${address}`) diff --git a/packages/uniswap/src/utils/cloud-backup/getCloudProviderName.native.ts b/packages/uniswap/src/utils/cloud-backup/getCloudProviderName.native.ts index 7a19eef443a..37c0199b89b 100644 --- a/packages/uniswap/src/utils/cloud-backup/getCloudProviderName.native.ts +++ b/packages/uniswap/src/utils/cloud-backup/getCloudProviderName.native.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line no-restricted-imports import { Platform } from 'react-native' export function getCloudProviderName(): string { diff --git a/packages/uniswap/src/utils/currencyId.ts b/packages/uniswap/src/utils/currencyId.ts new file mode 100644 index 00000000000..ac060f784e7 --- /dev/null +++ b/packages/uniswap/src/utils/currencyId.ts @@ -0,0 +1,99 @@ +import { Currency } from '@uniswap/sdk-core' +import { getNativeAddress, getWrappedNativeAddress } from 'uniswap/src/constants/addresses' +import { DEFAULT_NATIVE_ADDRESS } from 'uniswap/src/constants/chains' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' +import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' +import { CurrencyId } from 'uniswap/src/types/currency' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' + +export function currencyId(currency: Currency): CurrencyId { + return buildCurrencyId(currency.chainId, currencyAddress(currency)) +} + +export function buildCurrencyId(chainId: WalletChainId, address: string): string { + return `${chainId}-${address}` +} + +export function buildNativeCurrencyId(chainId: WalletChainId): string { + return buildCurrencyId(chainId, getNativeAddress(chainId)) +} + +export function buildWrappedNativeCurrencyId(chainId: WalletChainId): string { + return buildCurrencyId(chainId, getWrappedNativeAddress(chainId)) +} + +export function areCurrencyIdsEqual(id1: CurrencyId, id2: CurrencyId): boolean { + return id1.toLowerCase() === id2.toLowerCase() +} + +export function currencyAddress(currency: Currency): string { + if (currency.isNative) { + return getNativeAddress(currency.chainId) + } + + return currency.address +} + +export const NATIVE_ANALYTICS_ADDRESS_VALUE = 'NATIVE' + +export function getCurrencyAddressForAnalytics(currency: Currency): string { + if (currency.isNative) { + return NATIVE_ANALYTICS_ADDRESS_VALUE + } + + return currency.address +} + +export const isNativeCurrencyAddress = (chainId: WalletChainId, address: Maybe
): boolean => { + if (!address) { + return true + } + + return areAddressesEqual(address, getNativeAddress(chainId)) +} + +// Currency ids are formatted as `chainId-tokenaddress` +export function currencyIdToAddress(_currencyId: string): Address { + const currencyIdParts = _currencyId.split('-') + if (!currencyIdParts[1]) { + throw new Error(`Invalid currencyId format: ${_currencyId}`) + } + return currencyIdParts[1] +} + +function isPolygonChain(chainId: number): chainId is UniverseChainId.Polygon | UniverseChainId.PolygonMumbai { + return chainId === UniverseChainId.PolygonMumbai || chainId === UniverseChainId.Polygon +} + +function isCeloChain(chainId: number): chainId is UniverseChainId.Celo { + return chainId === UniverseChainId.Celo +} + +// Similar to `currencyIdToAddress`, except native addresses are `null`. +export function currencyIdToGraphQLAddress(_currencyId?: string): Address | null { + if (!_currencyId) { + return null + } + + const address = currencyIdToAddress(_currencyId) + const chainId = currencyIdToChain(_currencyId) + + if (!chainId) { + return null + } + + // backend only expects `null` for the native asset, except Polygon & Celo + if (isNativeCurrencyAddress(chainId, address) && !isPolygonChain(chainId) && !isCeloChain(chainId)) { + return null + } + + return address.toLowerCase() +} + +export function currencyIdToChain(_currencyId: string): WalletChainId | null { + return toSupportedChainId(_currencyId.split('-')[0]) +} + +export function isDefaultNativeAddress(address: string): boolean { + return areAddressesEqual(address, DEFAULT_NATIVE_ADDRESS) +} diff --git a/packages/uniswap/src/utils/env/index.ts b/packages/uniswap/src/utils/env/index.ts index 30b6f15a2d9..fc0b2e6c907 100644 --- a/packages/uniswap/src/utils/env/index.ts +++ b/packages/uniswap/src/utils/env/index.ts @@ -10,9 +10,7 @@ export function isDevEnv(): boolean { if (isInterface) { return process.env.NODE_ENV === 'development' } else if (isExtension) { - return ( - __DEV__ || chrome.runtime.id === EXTENSION_ID_DEV || chrome.runtime.id === EXTENSION_ID_LOCAL - ) + return __DEV__ || chrome.runtime.id === EXTENSION_ID_DEV || chrome.runtime.id === EXTENSION_ID_LOCAL } else { throw createAndLogError('isProdEnv') } diff --git a/packages/uniswap/src/utils/useKeyboardLayout.android.test.ts b/packages/uniswap/src/utils/useKeyboardLayout.android.test.ts index 3f0b1315700..45e644a33c6 100644 --- a/packages/uniswap/src/utils/useKeyboardLayout.android.test.ts +++ b/packages/uniswap/src/utils/useKeyboardLayout.android.test.ts @@ -1,5 +1,4 @@ import { renderHook } from '@testing-library/react-hooks' -/* eslint-disable-next-line no-restricted-imports */ import { DeviceEventEmitter, Dimensions } from 'react-native' import { act } from 'react-test-renderer' import { useKeyboardLayout } from 'uniswap/src/utils/useKeyboardLayout' diff --git a/packages/uniswap/src/utils/useKeyboardLayout.ios.test.ts b/packages/uniswap/src/utils/useKeyboardLayout.ios.test.ts index 8488bcdd1b5..ce93f7f17d2 100644 --- a/packages/uniswap/src/utils/useKeyboardLayout.ios.test.ts +++ b/packages/uniswap/src/utils/useKeyboardLayout.ios.test.ts @@ -1,5 +1,4 @@ import { renderHook } from '@testing-library/react-hooks' -/* eslint-disable-next-line no-restricted-imports */ import { DeviceEventEmitter, Dimensions } from 'react-native' import { act } from 'react-test-renderer' import { useKeyboardLayout } from 'uniswap/src/utils/useKeyboardLayout' diff --git a/packages/uniswap/src/utils/useKeyboardLayout.native.ts b/packages/uniswap/src/utils/useKeyboardLayout.native.ts index a74e51b0d89..99d55240770 100644 --- a/packages/uniswap/src/utils/useKeyboardLayout.native.ts +++ b/packages/uniswap/src/utils/useKeyboardLayout.native.ts @@ -1,5 +1,4 @@ import { useEffect, useState } from 'react' -/* eslint-disable-next-line no-restricted-imports */ import { EmitterSubscription, Keyboard, KeyboardEvent, useWindowDimensions } from 'react-native' import { KeyboardLayout } from 'uniswap/src/utils/useKeyboardLayout' import { isAndroid } from 'utilities/src/platform' @@ -20,13 +19,13 @@ export function useKeyboardLayout(): KeyboardLayout { }), Keyboard.addListener('keyboardDidHide', (e: KeyboardEvent) => { setKeyboardPosition(e.endCoordinates.screenY) - }) + }), ) } else { keyboardListeners.push( Keyboard.addListener('keyboardWillChangeFrame', (e: KeyboardEvent) => { setKeyboardPosition(e.endCoordinates.screenY) - }) + }), ) } diff --git a/packages/utilities/package.json b/packages/utilities/package.json index d4714d0f7ec..405ef0646f7 100644 --- a/packages/utilities/package.json +++ b/packages/utilities/package.json @@ -7,6 +7,7 @@ "@amplitude/analytics-react-native": "1.4.0", "@amplitude/analytics-types": "0.13.0", "@apollo/client": "3.10.4", + "@datadog/browser-logs": "^5.20.0", "@ethersproject/abstract-signer": "5.7.0", "@ethersproject/address": "5.7.0", "@ethersproject/constants": "5.7.0", @@ -25,8 +26,10 @@ "jsbi": "3.2.5", "react": "18.2.0", "react-native": "0.73.6", + "react-native-device-info": "10.0.2", "react-test-renderer": "18.2.0", "subscriptions-transport-ws": "0.11.0", + "uuid": "9.0.0", "zen-observable-ts": "1.2.5" }, "devDependencies": { @@ -34,6 +37,7 @@ "@testing-library/react-hooks": "7.0.2", "@types/chrome": "0.0.254", "@types/react": "^18.0.15", + "@types/uuid": "9.0.1", "@uniswap/eslint-config": "workspace:^", "eslint": "8.44.0", "jest": "29.7.0", @@ -45,6 +49,7 @@ "scripts": { "build": "tsc -b", "check:deps:usage": "depcheck", + "format": "../../scripts/prettier.sh", "lint": "eslint . --ext ts,tsx --max-warnings=0", "lint:fix": "eslint . --ext ts,tsx --fix", "test": "jest", diff --git a/packages/utilities/src/apollo/SubscriptionLink.ts b/packages/utilities/src/apollo/SubscriptionLink.ts index d692dfde0d9..0f2e35c8471 100644 --- a/packages/utilities/src/apollo/SubscriptionLink.ts +++ b/packages/utilities/src/apollo/SubscriptionLink.ts @@ -79,7 +79,7 @@ export function createSubscriptionLink( region = '', // left blank for a custom domain name (eg realtime.gateway.uniswap.org) token, }: SubscriptionLinkConfig, - client: ApolloClient + client: ApolloClient, ): SubscriptionLink { const auth: AuthOptions = { type: AUTH_TYPE.AWS_LAMBDA, token } // Order is intentional here - the header must be set before sending the subscription. diff --git a/packages/utilities/src/apollo/splitSubscription.ts b/packages/utilities/src/apollo/splitSubscription.ts index 367f8e15744..5553fd33b32 100644 --- a/packages/utilities/src/apollo/splitSubscription.ts +++ b/packages/utilities/src/apollo/splitSubscription.ts @@ -2,10 +2,7 @@ import { ApolloLink, HttpLink, split } from '@apollo/client' import { getMainDefinition } from '@apollo/client/utilities' import { SubscriptionLink } from 'utilities/src/apollo/SubscriptionLink' -export function splitSubscription( - subscriptionLink: SubscriptionLink, - httpLink: HttpLink -): ApolloLink { +export function splitSubscription(subscriptionLink: SubscriptionLink, httpLink: HttpLink): ApolloLink { // Use the subscriptionLink for subscriptions, and the httpLink for everything else; // see https://www.apollographql.com/docs/react/api/link/introduction/#directional-composition. return split( @@ -14,6 +11,6 @@ export function splitSubscription( return definition.kind === 'OperationDefinition' && definition.operation === 'subscription' }, subscriptionLink, - httpLink + httpLink, ) } diff --git a/packages/utilities/src/contracts/getContract.ts b/packages/utilities/src/contracts/getContract.ts index e532450262e..ac02a2ebf22 100644 --- a/packages/utilities/src/contracts/getContract.ts +++ b/packages/utilities/src/contracts/getContract.ts @@ -8,7 +8,7 @@ export function getContract( address: string, ABI: ContractInterface, provider: JsonRpcProvider, - account?: string + account?: string, ): Contract { if (!isAddress(address) || address === AddressZero) { throw Error(`Invalid 'address' parameter '${address}'.`) diff --git a/packages/utilities/src/device/locales.native.ts b/packages/utilities/src/device/locales.native.ts index b2a064218ce..894c2320906 100644 --- a/packages/utilities/src/device/locales.native.ts +++ b/packages/utilities/src/device/locales.native.ts @@ -1,10 +1,6 @@ // eslint-disable-next-line no-restricted-imports import { getLocales } from 'expo-localization' -import { - DEFAULT_LANGUAGE_CODE, - DEFAULT_LANGUAGE_TAG, - DeviceLocale, -} from 'utilities/src/device/constants' +import { DEFAULT_LANGUAGE_CODE, DEFAULT_LANGUAGE_TAG, DeviceLocale } from 'utilities/src/device/constants' import { logger } from 'utilities/src/logger/logger' export function getDeviceLocales(): DeviceLocale[] { diff --git a/packages/utilities/src/device/locales.web.ts b/packages/utilities/src/device/locales.web.ts index 50c7b8e486b..76c10e82eb6 100644 --- a/packages/utilities/src/device/locales.web.ts +++ b/packages/utilities/src/device/locales.web.ts @@ -1,8 +1,4 @@ -import { - DEFAULT_LANGUAGE_CODE, - DEFAULT_LANGUAGE_TAG, - DeviceLocale, -} from 'utilities/src/device/constants' +import { DEFAULT_LANGUAGE_CODE, DEFAULT_LANGUAGE_TAG, DeviceLocale } from 'utilities/src/device/constants' import { logger } from 'utilities/src/logger/logger' export function getDeviceLocales(): DeviceLocale[] { diff --git a/packages/utilities/src/environment/constants.ts b/packages/utilities/src/environment/constants.ts new file mode 100644 index 00000000000..bf5ea7d2404 --- /dev/null +++ b/packages/utilities/src/environment/constants.ts @@ -0,0 +1,3 @@ +export const isDetoxBuild = Boolean(process.env.DETOX_MODE) +export const isJestRun = !!process.env.JEST_WORKER_ID +export const isNonJestDev = __DEV__ && !isJestRun diff --git a/packages/uniswap/src/utils/env/index.native.ts b/packages/utilities/src/environment/index.native.ts similarity index 52% rename from packages/uniswap/src/utils/env/index.native.ts rename to packages/utilities/src/environment/index.native.ts index 9072a6aed16..ac4a649cf5e 100644 --- a/packages/uniswap/src/utils/env/index.native.ts +++ b/packages/utilities/src/environment/index.native.ts @@ -1,8 +1,11 @@ -// eslint-disable-next-line no-restricted-imports import DeviceInfo from 'react-native-device-info' const BUNDLE_ID = DeviceInfo.getBundleId() +export function isTestEnv(): boolean { + return !!process.env.JEST_WORKER_ID || process.env.NODE_ENV === 'test' +} + export function isDevEnv(): boolean { return BUNDLE_ID.endsWith('.dev') } @@ -14,3 +17,13 @@ export function isBetaEnv(): boolean { export function isProdEnv(): boolean { return BUNDLE_ID === 'com.uniswap.mobile' } + +export function getEnvName(): 'production' | 'staging' | 'development' { + if (isBetaEnv()) { + return 'staging' + } + if (isProdEnv()) { + return 'production' + } + return 'development' +} diff --git a/packages/utilities/src/environment/index.ts b/packages/utilities/src/environment/index.ts index 0c6aa8d503e..48ceef16a20 100644 --- a/packages/utilities/src/environment/index.ts +++ b/packages/utilities/src/environment/index.ts @@ -1,3 +1,52 @@ -export const isJestRun = !!process.env.JEST_WORKER_ID -export const isNonJestDev = __DEV__ && !isJestRun -export const isDetoxBuild = process.env.DETOX_MODE +import { createAndLogError } from 'utilities/src/logger/logger' +import { isExtension, isInterface } from 'utilities/src/platform' + +const EXTENSION_ID_LOCAL = 'ceofpnbcmdjbibjjdniemjemmgaibeih' +const EXTENSION_ID_DEV = 'afhngfaoadjjlhbgopehflaabbgfbcmn' +const EXTENSION_ID_BETA = 'foilfbjokdonehdajefeadkclfpmhdga' +const EXTENSION_ID_PROD = 'nnpmfplkfogfpmcngplhnbdnnilmcdcg' + +export function isTestEnv(): boolean { + return !!process.env.JEST_WORKER_ID || process.env.NODE_ENV === 'test' +} + +export function isDevEnv(): boolean { + if (isInterface) { + return process.env.NODE_ENV === 'development' + } else if (isExtension) { + return __DEV__ || chrome.runtime.id === EXTENSION_ID_DEV || chrome.runtime.id === EXTENSION_ID_LOCAL + } else { + throw createAndLogError('isProdEnv') + } +} + +export function isBetaEnv(): boolean { + if (isInterface) { + // This is set in vercel builds and deploys from web/staging. + return Boolean(process.env.REACT_APP_STAGING) + } else if (isExtension) { + return chrome.runtime.id === EXTENSION_ID_BETA + } else { + throw createAndLogError('isBetaEnv') + } +} + +export function isProdEnv(): boolean { + if (isInterface) { + return process.env.NODE_ENV === 'production' && !isBetaEnv() + } else if (isExtension) { + return chrome.runtime.id === EXTENSION_ID_PROD + } else { + throw createAndLogError('isProdEnv') + } +} + +export function getEnvName(): 'production' | 'staging' | 'development' { + if (isBetaEnv()) { + return 'staging' + } + if (isProdEnv()) { + return 'production' + } + return 'development' +} diff --git a/packages/utilities/src/format/convertScientificNotation.ts b/packages/utilities/src/format/convertScientificNotation.ts index a433e325d0d..4c40c7e2ecb 100644 --- a/packages/utilities/src/format/convertScientificNotation.ts +++ b/packages/utilities/src/format/convertScientificNotation.ts @@ -17,17 +17,14 @@ export function convertScientificNotationToNumber(value: string): string { x *= Math.pow(10, decimalPlaces) } try { - convertedValue = JSBI.multiply( - JSBI.BigInt(x), - JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(e)) - ).toString() + convertedValue = JSBI.multiply(JSBI.BigInt(x), JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(e))).toString() } catch (error) { // If the numbers can't be converted to BigInts then just do regular arithmetic (i.e. when the exponent is negative) logger.debug( 'convertScientificNotation', 'convertScientificNotationToNumber', 'BigInt arithmetic unsuccessful', - e + e, ) convertedValue = (x * Math.pow(10, e)).toString() } diff --git a/packages/utilities/src/format/localeBased.ts b/packages/utilities/src/format/localeBased.ts index 9c4ca82d4c4..9189c562b79 100644 --- a/packages/utilities/src/format/localeBased.ts +++ b/packages/utilities/src/format/localeBased.ts @@ -104,9 +104,7 @@ export function formatPercent(rawPercentage: Maybe, locale: str return '-' } const percentage = - typeof rawPercentage === 'string' - ? parseFloat(rawPercentage) - : parseFloat(rawPercentage.toString()) + typeof rawPercentage === 'string' ? parseFloat(rawPercentage) : parseFloat(rawPercentage.toString()) return formatNumber({ input: percentage / 100, type: NumberType.Percentage, locale }) } @@ -129,8 +127,8 @@ export function addFiatSymbolToNumber({ ? parts[1]?.value : '' : parts[parts.length - 2]?.type === 'literal' - ? parts[parts.length - 2]?.value - : '' + ? parts[parts.length - 2]?.value + : '' return isSymbolAtFront ? `${currencySymbol}${extra}${value}` : `${value}${extra}${currencySymbol}` } @@ -146,10 +144,7 @@ export type FiatCurrencyComponents = { * Helper function to return components of a currency value for a specific locale * E.g. comma, period, or space for separating thousands */ -export function getFiatCurrencyComponents( - locale: string, - currencyCode: string -): FiatCurrencyComponents { +export function getFiatCurrencyComponents(locale: string, currencyCode: string): FiatCurrencyComponents { const format = TwoDecimalsCurrency.createFormat(locale, currencyCode) // See MDN for official docs https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/formatToParts diff --git a/packages/utilities/src/format/urls.test.ts b/packages/utilities/src/format/urls.test.ts index b4218da4e30..bb2d8b9f4d9 100644 --- a/packages/utilities/src/format/urls.test.ts +++ b/packages/utilities/src/format/urls.test.ts @@ -1,6 +1,6 @@ // Copied from https://github.com/Uniswap/interface/blob/main/src/utils/uriToHttp.test.ts -import { isGifUri, isSVGUri, uriToHttpUrls } from 'utilities/src/format/urls' +import { formatDappURL, isGifUri, isSVGUri, uriToHttpUrls } from 'utilities/src/format/urls' describe(uriToHttpUrls, () => { it('returns .eth.link for ens names', () => { @@ -148,3 +148,19 @@ describe(isGifUri, () => { expect(isGifUri('This is not a URI.gif')).toEqual(false) }) }) + +describe(formatDappURL, () => { + it('removes prefix from url', () => { + expect(formatDappURL('https://example.com')).toEqual('example.com') + expect(formatDappURL('https://www.example.com')).toEqual('example.com') + expect(formatDappURL('www.example.com')).toEqual('example.com') + }) + + it('removes trailing slash from url', () => { + expect(formatDappURL('example.com/')).toEqual('example.com') + }) + + it('does not remove http from url', () => { + expect(formatDappURL('http://example.com')).toEqual('http://example.com') + }) +}) diff --git a/packages/utilities/src/format/urls.ts b/packages/utilities/src/format/urls.ts index 3ddb6f43fac..b7c5aab4136 100644 --- a/packages/utilities/src/format/urls.ts +++ b/packages/utilities/src/format/urls.ts @@ -75,3 +75,12 @@ export function isSVGUri(uri: Maybe): boolean { export function isGifUri(uri: Maybe): boolean { return isSegmentUri(uri, '.gif') } + +/** + * Removes safe prefixes and trailing slashes from URL to improve human readability. + * + * @param {string} url The URL to check. + */ +export function formatDappURL(url: string): string { + return url?.replace('https://', '').replace('www.', '').replace(/\/$/, '') +} diff --git a/packages/utilities/src/logger/Datadog.ts b/packages/utilities/src/logger/Datadog.ts new file mode 100644 index 00000000000..82ae814b006 --- /dev/null +++ b/packages/utilities/src/logger/Datadog.ts @@ -0,0 +1,75 @@ +import { datadogLogs } from '@datadog/browser-logs' +import { getEnvName, isTestEnv } from 'utilities/src/environment' +import { LogLevel, LoggerErrorContext } from 'utilities/src/logger/types' +import { v4 as uuidv4 } from 'uuid' + +// setup user information +const USER_ID_KEY = 'datadog-user-id' + +export function setupDatadog(): void { + if (isTestEnv()) { + return + } + if (!process.env.REACT_APP_DATADOG_CLIENT_TOKEN) { + // eslint-disable-next-line no-console + console.error(`No datadog client token, disabling`) + return + } + + datadogLogs.init({ + clientToken: process.env.REACT_APP_DATADOG_CLIENT_TOKEN, + site: 'datadoghq.com', + forwardErrorsToLogs: true, + }) + + let userId = localStorage.getItem(USER_ID_KEY) + if (!userId) { + localStorage.setItem(USER_ID_KEY, (userId = uuidv4())) + } + datadogLogs.setUser({ + id: userId, + }) + + datadogLogs.setUserProperty('env', getEnvName()) + datadogLogs.setUserProperty('version', process.env.REACT_APP_GIT_COMMIT_HASH) +} + +export function logToDatadog( + message: string, + { + level, + ...options + }: { + level: LogLevel + args: unknown[] + fileName: string + functionName: string + }, +): void { + if (isTestEnv()) { + return + } + datadogLogs.logger[level](message, options) +} + +export function logErrorToDatadog(error: Error, context?: LoggerErrorContext): void { + if (isTestEnv()) { + return + } + if (error instanceof Error) { + datadogLogs.logger.error(error.message, { + error: { + kind: error.name, + stack: error.stack, + }, + ...context, + }) + } else { + datadogLogs.logger.error(error, { + error: { + stack: new Error().stack, + }, + ...context, + }) + } +} diff --git a/packages/utilities/src/logger/Sentry.native.ts b/packages/utilities/src/logger/Sentry.native.ts index 7f48cca8375..f6a7f32dfd3 100644 --- a/packages/utilities/src/logger/Sentry.native.ts +++ b/packages/utilities/src/logger/Sentry.native.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line no-restricted-imports import * as SentryRN from '@sentry/react-native' import { CaptureContext, SeverityLevel } from '@sentry/types' import { BreadCrumb, ISentry } from 'utilities/src/logger/Sentry' @@ -22,12 +21,7 @@ export function captureException(error: unknown, captureContext?: CaptureContext * @param extraArgs Key/value pairs to enrich logging and allow filtering. * More info here: https://docs.sentry.io/platforms/react-native/enriching-events/context/ */ -export function captureMessage( - level: SeverityLevel, - context: string, - message: string, - ...extraArgs: unknown[] -): void { +export function captureMessage(level: SeverityLevel, context: string, message: string, ...extraArgs: unknown[]): void { SentryRN.captureMessage(message, { level, tags: { mobileContext: context }, diff --git a/packages/utilities/src/logger/Sentry.ts b/packages/utilities/src/logger/Sentry.ts index 8e0ba6d4096..ba87f0444d8 100644 --- a/packages/utilities/src/logger/Sentry.ts +++ b/packages/utilities/src/logger/Sentry.ts @@ -1,6 +1,6 @@ import { SeverityLevel } from '@sentry/types' import { NotImplementedError } from 'utilities/src/errors' -import { LoggerErrorContext } from 'utilities/src/logger/logger' +import { LoggerErrorContext } from 'utilities/src/logger/types' export type BreadCrumb = { message: string @@ -14,12 +14,7 @@ export type BreadCrumb = { /** Dummy Sentry logging class. Overridden by mobile or extension related code. */ export interface ISentry { captureException(error: unknown, captureContext: LoggerErrorContext): void - captureMessage( - level: SeverityLevel, - context: string, - message: string, - ...extraArgs: unknown[] - ): void + captureMessage(level: SeverityLevel, context: string, message: string, ...extraArgs: unknown[]): void addBreadCrumb(breadCrumb: BreadCrumb): void } diff --git a/packages/utilities/src/logger/Sentry.web.ts b/packages/utilities/src/logger/Sentry.web.ts index 8ed7cc38664..f7087c079f0 100644 --- a/packages/utilities/src/logger/Sentry.web.ts +++ b/packages/utilities/src/logger/Sentry.web.ts @@ -21,12 +21,7 @@ export function captureException(error: unknown, captureContext?: CaptureContext * @param extraArgs Key/value pairs to enrich logging and allow filtering. * More info here: https://docs.sentry.io/platforms/react-native/enriching-events/context/ */ -export function captureMessage( - level: SeverityLevel, - context: string, - message: string, - ...extraArgs: unknown[] -): void { +export function captureMessage(level: SeverityLevel, context: string, message: string, ...extraArgs: unknown[]): void { SentryReact.captureMessage(message, { level, tags: { webContext: context }, diff --git a/packages/utilities/src/logger/console.ts b/packages/utilities/src/logger/console.ts index 89e17208316..3d642959bf3 100644 --- a/packages/utilities/src/logger/console.ts +++ b/packages/utilities/src/logger/console.ts @@ -55,8 +55,7 @@ function isIgnoredMessage(msg: unknown, arg: unknown): boolean { return IGNORED_MESSAGES.some((ignoredMessage) => { const result = msg.includes(ignoredMessage.message) && - (!ignoredMessage.firstArgValues || - ignoredMessage.firstArgValues.some((argVal) => argVal === arg)) + (!ignoredMessage.firstArgValues || ignoredMessage.firstArgValues.some((argVal) => argVal === arg)) return result }) } diff --git a/packages/utilities/src/logger/logger.ts b/packages/utilities/src/logger/logger.ts index 3221f6b54a5..7acb1b21757 100644 --- a/packages/utilities/src/logger/logger.ts +++ b/packages/utilities/src/logger/logger.ts @@ -1,6 +1,7 @@ -/* eslint-disable no-console */ -import { Extras, ScopeContext } from '@sentry/types' +import { Extras } from '@sentry/types' +import { logErrorToDatadog, logToDatadog } from 'utilities/src/logger/Datadog' import { Sentry } from 'utilities/src/logger/Sentry' +import { LogLevel, LoggerErrorContext } from 'utilities/src/logger/types' import { isInterface, isWeb } from 'utilities/src/platform' // weird temp fix: the web app is complaining about __DEV__ being global @@ -11,7 +12,6 @@ import { isInterface, isWeb } from 'utilities/src/platform' // perhaps because the declarations are not applying to external packages // but somehow its also not picking up the declarations here declare global { - // @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore its ok const __DEV__: boolean @@ -19,14 +19,8 @@ declare global { const SENTRY_CHAR_LIMIT = 8192 -type LogLevel = 'debug' | 'info' | 'warn' | 'error' - -export type LoggerErrorContext = Omit, 'tags'> & { - tags: { file: string; function: string } -} - /** - * Logs a message to console. Additionally sends log to Sentry if using 'error', 'warn', or 'info'. + * Logs a message to console. Additionally sends log to Sentry and Datadog if using 'error', 'warn', or 'info'. * Use `logger.debug` for development only logs. * * ex. `logger.warn('myFile', 'myFunc', 'Some warning', myArray)` @@ -43,8 +37,7 @@ export const logger = { logMessage('info', fileName, functionName, message, ...args), warn: (fileName: string, functionName: string, message: string, ...args: unknown[]): void => logMessage('warn', fileName, functionName, message, ...args), - error: (error: unknown, captureContext: LoggerErrorContext): void => - logException(error, captureContext), + error: (error: unknown, captureContext: LoggerErrorContext): void => logException(error, captureContext), } function logMessage( @@ -56,6 +49,7 @@ function logMessage( ): void { // Log to console directly for dev builds or interface for debugging if (__DEV__ || isInterface) { + // eslint-disable-next-line no-console console[level](...formatMessage(level, fileName, functionName, message), ...args) } @@ -69,6 +63,15 @@ function logMessage( } else if (level === 'info') { Sentry.captureMessage('info', `${fileName}#${functionName}`, message, ...args) } + + if (isInterface) { + logToDatadog(message, { + level, + args, + functionName, + fileName, + }) + } } function logException(error: unknown, captureContext: LoggerErrorContext): void { @@ -76,6 +79,7 @@ function logException(error: unknown, captureContext: LoggerErrorContext): void // Log to console directly for dev builds or interface for debugging if (__DEV__ || isInterface) { + // eslint-disable-next-line no-console console.error(error) } @@ -94,12 +98,16 @@ function logException(error: unknown, captureContext: LoggerErrorContext): void } Sentry.captureException(error, updatedContext) + if (isInterface) { + logErrorToDatadog(error instanceof Error ? error : new Error(`${error}`), updatedContext) + } } interface RNError { nativeStackAndroid?: unknown userInfo?: unknown } + // Adds extra fields from errors provided by React Native function addErrorExtras(error: unknown, captureContext: LoggerErrorContext): LoggerErrorContext { if (error instanceof Error) { @@ -127,13 +135,10 @@ function formatMessage( level: LogLevel, fileName: string, functionName: string, - message: string + message: string, ): (string | Record)[] { const t = new Date() - const timeString = `${pad(t.getHours())}:${pad(t.getMinutes())}:${pad(t.getSeconds())}.${pad( - t.getMilliseconds(), - 3 - )}` + const timeString = `${pad(t.getHours())}:${pad(t.getMinutes())}:${pad(t.getSeconds())}.${pad(t.getMilliseconds(), 3)}` if (isWeb) { // Simpler printing for web logging return [ @@ -152,3 +157,14 @@ function formatMessage( return [`${timeString}::${fileName}#${functionName}`, message] } } + +export function createAndLogError(funcName: string): Error { + const e = new Error('Unsupported app environment that failed all checks') + logger.error(e, { + tags: { + file: 'utilities/src/environment/index.ts', + function: funcName, + }, + }) + return e +} diff --git a/packages/utilities/src/logger/mocks.ts b/packages/utilities/src/logger/mocks.ts new file mode 100644 index 00000000000..3260a1dfea1 --- /dev/null +++ b/packages/utilities/src/logger/mocks.ts @@ -0,0 +1,7 @@ +// mock this since it errors about multiple SDK instances in test mode +jest.mock('@datadog/browser-logs', () => ({ + datadogLogs: { + // leave it empty as we should avoid it in test mode + logger: {}, + }, +})) diff --git a/packages/utilities/src/logger/types.ts b/packages/utilities/src/logger/types.ts new file mode 100644 index 00000000000..fa9a08c709d --- /dev/null +++ b/packages/utilities/src/logger/types.ts @@ -0,0 +1,7 @@ +import { type ScopeContext } from '@sentry/types' + +export type LogLevel = 'debug' | 'info' | 'warn' | 'error' + +export type LoggerErrorContext = Omit, 'tags'> & { + tags: { file: string; function: string } +} diff --git a/packages/utilities/src/platform/index.native.ts b/packages/utilities/src/platform/index.native.ts index b17aee6bddb..0b820449b5e 100644 --- a/packages/utilities/src/platform/index.native.ts +++ b/packages/utilities/src/platform/index.native.ts @@ -1,4 +1,3 @@ -// eslint-disable-next-line no-restricted-imports import { Platform } from 'react-native' // Platform diff --git a/packages/utilities/src/platform/index.ts b/packages/utilities/src/platform/index.ts index 7209025e438..3857be9527f 100644 --- a/packages/utilities/src/platform/index.ts +++ b/packages/utilities/src/platform/index.ts @@ -26,9 +26,7 @@ export const isMobile: boolean = export const isWebIOS: boolean = typeof document !== 'undefined' && typeof navigator !== 'undefined' && - (['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone'].includes( - navigator.platform - ) || + (['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone'].includes(navigator.platform) || // iPad on iOS 13 detection (navigator.userAgent.includes('Mac') && 'ontouchend' in document)) @@ -43,10 +41,8 @@ export const isTouchable = ('ontouchstart' in window || navigator.maxTouchPoints > 0) // Browser -export const isChrome: boolean = - typeof navigator !== 'undefined' && /Chrome/.test(navigator.userAgent || '') -export const isSafari: boolean = - typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent || '') +export const isChrome: boolean = typeof navigator !== 'undefined' && /Chrome/.test(navigator.userAgent || '') +export const isSafari: boolean = typeof navigator !== 'undefined' && /Safari/.test(navigator.userAgent || '') export const isMobileWebSafari: boolean = isTouchable && isSafari export const isMobileWebAndroid: boolean = isTouchable && isWebAndroid diff --git a/packages/utilities/src/primitives/array.ts b/packages/utilities/src/primitives/array.ts index 7a1b8f98756..63cbeff0cfa 100644 --- a/packages/utilities/src/primitives/array.ts +++ b/packages/utilities/src/primitives/array.ts @@ -2,10 +2,7 @@ function onlyUnique(value: T, index: number, self: T[]): boolean { return self.indexOf(value) === index } -export function unique( - array: T[], - isUnique: (value: T, index: number, self: T[]) => boolean = onlyUnique -): T[] { +export function unique(array: T[], isUnique: (value: T, index: number, self: T[]) => boolean = onlyUnique): T[] { return array.filter(isUnique) } @@ -19,11 +16,7 @@ export function next(array: T[], current: T): T | undefined { // get items in `array` that are not in `without` // e.g. difference([B, C, D], [A, B, C]) would return ([D]) -export function differenceWith( - array: T[], - without: T[], - comparator: (item1: T, item2: T) => boolean -): T[] { +export function differenceWith(array: T[], without: T[], comparator: (item1: T, item2: T) => boolean): T[] { return array.filter((item: T) => { const inWithout = Boolean(without.find((otherItem: T) => comparator(item, otherItem))) return !inWithout diff --git a/packages/utilities/src/primitives/string.ts b/packages/utilities/src/primitives/string.ts index 2d3d696951d..a3e994a395b 100644 --- a/packages/utilities/src/primitives/string.ts +++ b/packages/utilities/src/primitives/string.ts @@ -37,9 +37,7 @@ export function containsNonPrintableChars(msg: string): boolean { const regex = /[\p{C}\p{Z}]/gu if (regex.test(msg)) { - return ![...msg].every( - (char) => char === '\n' || char === '\r' || char === '\t' || !/\p{C}/u.test(char) - ) + return ![...msg].every((char) => char === '\n' || char === '\r' || char === '\t' || !/\p{C}/u.test(char)) } return false diff --git a/packages/utilities/src/react/hook.test.tsx b/packages/utilities/src/react/hook.test.tsx index 6e99cf88289..c10bdfade0b 100644 --- a/packages/utilities/src/react/hook.test.tsx +++ b/packages/utilities/src/react/hook.test.tsx @@ -71,9 +71,7 @@ describe('useAsyncData', () => { const asyncCallback = jest.fn().mockResolvedValue('data') const onCancel = jest.fn() - const { unmount, rerender, waitForNextUpdate } = renderHook(() => - useAsyncData(asyncCallback, onCancel) - ) + const { unmount, rerender, waitForNextUpdate } = renderHook(() => useAsyncData(asyncCallback, onCancel)) await act(async () => { rerender() @@ -93,7 +91,7 @@ describe('useAsyncData', () => { ({ asyncCallback, onCancel }) => useAsyncData(asyncCallback, onCancel), { initialProps: { asyncCallback: initialCallback, onCancel: cancel }, - } + }, ) expect(initialCallback).toHaveBeenCalledTimes(1) @@ -117,7 +115,7 @@ describe('useAsyncData', () => { ({ asyncCallback, onCancel }) => useAsyncData(asyncCallback, onCancel), { initialProps: { asyncCallback: initialCallback, onCancel: cancel }, - } + }, ) const newCallback = jest.fn().mockResolvedValue('data') @@ -136,12 +134,9 @@ describe('useAsyncData', () => { it('enters loading state and returns new callback result when the callback changes', async () => { const initialCallback = jest.fn().mockImplementation(() => createPromise('data1')) - const { rerender, result, waitForNextUpdate } = renderHook( - ({ asyncCallback }) => useAsyncData(asyncCallback), - { - initialProps: { asyncCallback: initialCallback }, - } - ) + const { rerender, result, waitForNextUpdate } = renderHook(({ asyncCallback }) => useAsyncData(asyncCallback), { + initialProps: { asyncCallback: initialCallback }, + }) expect(result.current).toEqual({ data: undefined, isLoading: true }) @@ -201,11 +196,11 @@ describe('useMemoCompare', () => { (props) => useMemoCompare( () => props, - () => true + () => true, ), { initialProps: initialValue, - } + }, ) rerender({ a: 1 }) @@ -217,11 +212,11 @@ describe('useMemoCompare', () => { (props) => useMemoCompare( () => props, - () => false + () => false, ), { initialProps: { a: 1 }, - } + }, ) const newValue = { a: 2 } diff --git a/packages/utilities/src/react/hooks.ts b/packages/utilities/src/react/hooks.ts index b31ce71f874..dc86fa90b92 100644 --- a/packages/utilities/src/react/hooks.ts +++ b/packages/utilities/src/react/hooks.ts @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useRef, useState } from 'react' +import { RefObject, useEffect, useMemo, useRef, useState } from 'react' // modified from https://usehooks.com/usePrevious/ export function usePrevious(value: T): T | undefined { @@ -19,7 +19,7 @@ export function usePrevious(value: T): T | undefined { // above link contains example on how to add delayed execution if ever needed export function useAsyncData( asyncCallback: () => Promise | undefined, - onCancel?: () => void + onCancel?: () => void, ): { isLoading: boolean data: T | undefined @@ -110,3 +110,39 @@ export function useMemoCompare(next: () => T, compare: (a: T | undefined, b: // Finally, if equal then return the previous value if it's set return isEqual && previous ? previous : nextValue } + +export function useOnClickOutside( + node: RefObject, + handler: undefined | (() => void), + ignoredNodes: Array> = [], +): void { + const handlerRef = useRef void)>(handler) + + useEffect(() => { + handlerRef.current = handler + }, [handler]) + + useEffect(() => { + const handleClickOutside = (e: MouseEvent): void => { + const nodeClicked = node.current?.contains(e.target as Node) + const ignoredNodeClicked = ignoredNodes.reduce( + (reducer, val) => reducer || !!val.current?.contains(e.target as Node), + false, + ) + + if ((nodeClicked || ignoredNodeClicked) ?? false) { + return + } + + if (handlerRef.current) { + handlerRef.current() + } + } + + document.addEventListener('mousedown', handleClickOutside) + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [node, ignoredNodes]) +} diff --git a/packages/utilities/src/telemetry/analytics/analytics.native.ts b/packages/utilities/src/telemetry/analytics/analytics.native.ts index bfe358b324d..c2a7a910f9c 100644 --- a/packages/utilities/src/telemetry/analytics/analytics.native.ts +++ b/packages/utilities/src/telemetry/analytics/analytics.native.ts @@ -1,13 +1,5 @@ /* eslint-disable no-restricted-imports */ -import { - Identify, - flush, - getUserId, - identify, - init, - setDeviceId, - track, -} from '@amplitude/analytics-react-native' +import { Identify, flush, getUserId, identify, init, setDeviceId, track } from '@amplitude/analytics-react-native' import { ANONYMOUS_DEVICE_ID } from '@uniswap/analytics' import { ApplicationTransport } from 'utilities/src/telemetry/analytics/ApplicationTransport' import { Analytics, UserPropertyValue } from 'utilities/src/telemetry/analytics/analytics' @@ -33,7 +25,7 @@ export const analytics: Analytics = { transportProvider: ApplicationTransport, allowed: boolean, _initHash?: string, - userIdGetter?: () => Promise + userIdGetter?: () => Promise, ): Promise { try { allowAnalytics = allowed @@ -47,7 +39,7 @@ export const analytics: Analytics = { ...AMPLITUDE_SHARED_TRACKING_OPTIONS, ...AMPLITUDE_NATIVE_TRACKING_OPTIONS, }, - } + }, ) userId = userIdGetter ? await userIdGetter() : getUserId() diff --git a/packages/utilities/src/telemetry/analytics/analytics.ts b/packages/utilities/src/telemetry/analytics/analytics.ts index 95eb4a8c442..482dedc41a4 100644 --- a/packages/utilities/src/telemetry/analytics/analytics.ts +++ b/packages/utilities/src/telemetry/analytics/analytics.ts @@ -13,7 +13,7 @@ export interface Analytics { transportProvider: ApplicationTransport, allowed: boolean, initHash?: string, - userIdGetter?: () => Promise + userIdGetter?: () => Promise, ): Promise setAllowAnalytics(allowed: boolean): Promise sendEvent(eventName: string, eventProperties: Record): void @@ -26,7 +26,7 @@ export const analytics: Analytics = { _transportProvider: ApplicationTransport, _allowed: boolean, _initHash?: string, - _userIdGetter?: () => Promise + _userIdGetter?: () => Promise, ): Promise { throw new NotImplementedError('initAnalytics') }, diff --git a/packages/utilities/src/telemetry/analytics/analytics.web.ts b/packages/utilities/src/telemetry/analytics/analytics.web.ts index f8c41e0b321..82a1b283e26 100644 --- a/packages/utilities/src/telemetry/analytics/analytics.web.ts +++ b/packages/utilities/src/telemetry/analytics/analytics.web.ts @@ -1,12 +1,4 @@ -import { - flush, - getUserId, - Identify, - identify, - init, - setDeviceId, - track, -} from '@amplitude/analytics-browser' +import { flush, getUserId, Identify, identify, init, setDeviceId, track } from '@amplitude/analytics-browser' // eslint-disable-next-line no-restricted-imports import { ANONYMOUS_DEVICE_ID } from '@uniswap/analytics' // eslint-disable-next-line no-restricted-imports @@ -66,7 +58,7 @@ export const analytics: Analytics = { transportProvider: ApplicationTransport, allowed: boolean, initHash?: string, - userIdGetter?: () => Promise + userIdGetter?: () => Promise, ): Promise { // Set properties commitHash = initHash @@ -80,7 +72,7 @@ export const analytics: Analytics = { transportProvider, // Used to support custom reverse proxy header // Disable tracking of private user information by Amplitude trackingOptions: AMPLITUDE_SHARED_TRACKING_OPTIONS, - } + }, ) userId = userIdGetter ? await userIdGetter() : getUserId() @@ -123,11 +115,7 @@ export const analytics: Analytics = { loggers.flushEvents() flush() }, - async setUserProperty( - property: string, - value: UserPropertyValue, - insert?: boolean - ): Promise { + async setUserProperty(property: string, value: UserPropertyValue, insert?: boolean): Promise { if (!(await getAnalyticsAtomDirect())) { return } diff --git a/packages/utilities/src/telemetry/analytics/logging.ts b/packages/utilities/src/telemetry/analytics/logging.ts index f3e11a85acb..3be0ceb34e3 100644 --- a/packages/utilities/src/telemetry/analytics/logging.ts +++ b/packages/utilities/src/telemetry/analytics/logging.ts @@ -1,4 +1,4 @@ -import { isNonJestDev } from 'utilities/src/environment' +import { isNonJestDev } from 'utilities/src/environment/constants' import { logger } from 'utilities/src/logger/logger' // eslint-disable-next-line no-restricted-imports import { UserPropertyValue } from 'utilities/src/telemetry/analytics/analytics' diff --git a/packages/utilities/src/telemetry/trace/AnalyticsNavigationContext.tsx b/packages/utilities/src/telemetry/trace/AnalyticsNavigationContext.tsx index 4a265a34212..477a50fc62a 100644 --- a/packages/utilities/src/telemetry/trace/AnalyticsNavigationContext.tsx +++ b/packages/utilities/src/telemetry/trace/AnalyticsNavigationContext.tsx @@ -19,9 +19,5 @@ export function AnalyticsNavigationContextProvider({ shouldLogScreen, children, }: PropsWithChildren): JSX.Element { - return ( - - {children} - - ) + return {children} } diff --git a/packages/utilities/src/telemetry/trace/Trace.tsx b/packages/utilities/src/telemetry/trace/Trace.tsx index 4ba506ae9f3..5b116c8df20 100644 --- a/packages/utilities/src/telemetry/trace/Trace.tsx +++ b/packages/utilities/src/telemetry/trace/Trace.tsx @@ -8,11 +8,7 @@ import { useAnalyticsNavigationContext } from 'utilities/src/telemetry/trace/Ana import { ITraceContext, TraceContext, useTrace } from 'utilities/src/telemetry/trace/TraceContext' import { getEventHandlers } from 'utilities/src/telemetry/trace/utils' -export function getEventsFromProps( - logPress?: boolean, - logFocus?: boolean, - logKeyPress?: boolean -): string[] { +export function getEventsFromProps(logPress?: boolean, logFocus?: boolean, logKeyPress?: boolean): string[] { const events = [] if (logPress) { events.push(isWeb ? 'onClick' : 'onPress') @@ -70,8 +66,7 @@ function _Trace({ }: PropsWithChildren): JSX.Element { const id = useId() - const { useIsPartOfNavigationTree, shouldLogScreen: shouldLogScreen } = - useAnalyticsNavigationContext() + const { useIsPartOfNavigationTree, shouldLogScreen: shouldLogScreen } = useAnalyticsNavigationContext() const isPartOfNavigationTree = useIsPartOfNavigationTree() const parentTrace = useTrace() @@ -135,8 +130,8 @@ function _Trace({ events, eventOnTrigger ?? SharedEventName.ELEMENT_CLICKED, element, - properties - ) + properties, + ), ) }) } @@ -154,16 +149,14 @@ function _Trace({ combinedProps={combinedProps} directFromPage={directFromPage} logImpression={logImpression} - properties={properties}> + properties={properties} + > {modifiedChildren} ) } -type NavAwareTraceProps = Pick< - TraceProps, - 'logImpression' | 'properties' | 'directFromPage' | 'eventOnTrigger' -> +type NavAwareTraceProps = Pick // Internal component to keep track of navigation events // Needed since we need to rely on `navigation.useFocusEffect` to track @@ -186,7 +179,7 @@ function NavAwareTrace({ analytics.sendEvent(eventOnTrigger ?? SharedEventName.PAGE_VIEWED, eventProps) } } - }, [combinedProps, directFromPage, eventOnTrigger, logImpression, properties, shouldLogScreen]) + }, [combinedProps, directFromPage, eventOnTrigger, logImpression, properties, shouldLogScreen]), ) return <>{children} diff --git a/packages/utilities/src/telemetry/trace/utils.ts b/packages/utilities/src/telemetry/trace/utils.ts index 1e837d3f419..3af4d5c9244 100644 --- a/packages/utilities/src/telemetry/trace/utils.ts +++ b/packages/utilities/src/telemetry/trace/utils.ts @@ -15,7 +15,7 @@ export function getEventHandlers( triggers: string[], eventName: string, element?: string, - properties?: Record + properties?: Record, ): Partial void>> { const eventHandlers: Partial void>> = {} for (const event of triggers) { @@ -23,16 +23,11 @@ export function getEventHandlers( // Some interface elements don't have handlers defined. // TODO(WEB-4252): Potentially can remove isInterface check once web is fully converted to tamagui if (!child.props[event] && !isInterface) { - logger.info( - 'trace/utils.ts', - 'getEventHandlers', - 'Found a null handler while logging an event', - { - eventName, - ...consumedProps, - ...properties, - } - ) + logger.info('trace/utils.ts', 'getEventHandlers', 'Found a null handler while logging an event', { + eventName, + ...consumedProps, + ...properties, + }) } // call child event handler with original arguments diff --git a/packages/utilities/src/time/date.ts b/packages/utilities/src/time/date.ts index 52c86c12dcd..304212ffcbd 100644 --- a/packages/utilities/src/time/date.ts +++ b/packages/utilities/src/time/date.ts @@ -1,7 +1,3 @@ export function areSameDays(a: Date, b: Date): boolean { - return ( - a.getDate() === b.getDate() && - a.getMonth() === b.getMonth() && - a.getFullYear() === b.getFullYear() - ) + return a.getDate() === b.getDate() && a.getMonth() === b.getMonth() && a.getFullYear() === b.getFullYear() } diff --git a/packages/utilities/src/time/timing.ts b/packages/utilities/src/time/timing.ts index e518cf1adbf..3f0da1008b0 100644 --- a/packages/utilities/src/time/timing.ts +++ b/packages/utilities/src/time/timing.ts @@ -6,10 +6,7 @@ export function sleep(milliseconds: number): Promise { return new Promise((resolve) => setTimeout(() => resolve(true), milliseconds)) } -export async function promiseTimeout( - promise: Promise, - milliseconds: number -): Promise { +export async function promiseTimeout(promise: Promise, milliseconds: number): Promise { // Create a promise that rejects in milliseconds const timeout = new Promise((resolve) => { const id = setTimeout(() => { @@ -27,10 +24,7 @@ export async function promiseTimeout( * @param promise to execute * @param milliseconds length of minimum delay time in ms */ -export async function promiseMinDelay( - promise: Promise, - milliseconds: number -): Promise { +export async function promiseMinDelay(promise: Promise, milliseconds: number): Promise { const minDelay = new Promise((resolve) => { const id = setTimeout(() => { clearTimeout(id) @@ -43,11 +37,7 @@ export async function promiseMinDelay( } // https://usehooks-typescript.com/react-hook/use-interval -export function useInterval( - callback: () => void, - delay: number | null, - immediateStart?: boolean -): void { +export function useInterval(callback: () => void, delay: number | null, immediateStart?: boolean): void { const savedCallback = useRef<() => void | null>() // Remember the latest callback. @@ -81,7 +71,7 @@ type Timeout = ReturnType // https://medium.com/javascript-in-plain-english/usetimeout-react-hook-3cc58b94af1f export const useTimeout = ( callback: () => void, - delay = 0 // in ms (default: immediately put into JS Event Queue) + delay = 0, // in ms (default: immediately put into JS Event Queue) ): (() => void) => { const timeoutIdRef = useRef() @@ -136,7 +126,7 @@ export function useDebounceWithStatus(value: T, delay: number = DEFAULT_DELAY export function debounceCallback void>( func: T, - wait: number + wait: number, ): { triggerDebounce: () => void; cancelDebounce: () => void } { let timeout: NodeJS.Timeout diff --git a/packages/wallet/jest-setup.js b/packages/wallet/jest-setup.js index 87339720ed9..b5070eb35a0 100644 --- a/packages/wallet/jest-setup.js +++ b/packages/wallet/jest-setup.js @@ -1,4 +1,5 @@ import 'uniswap/src/i18n/i18n' // Uses real translations for tests +import 'utilities/src/logger/mocks' import { localizeMock as mockRNLocalize } from 'react-native-localize/mock' import { AppearanceSettingType } from 'wallet/src/features/appearance/slice' @@ -20,7 +21,8 @@ jest.mock('wallet/src/features/appearance/hooks', () => { } }) -jest.mock('uniswap/src/utils/env', () => ({ +jest.mock('utilities/src/environment', () => ({ + isTestEnv: jest.fn(() => true), isDevEnv: jest.fn(() => false), isBetaEnv: jest.fn(() => false), isProdEnv: jest.fn(() => false), diff --git a/packages/wallet/package.json b/packages/wallet/package.json index b4259338e97..ddf5c9d484d 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "scripts": { "check:deps:usage": "depcheck", + "format": "../../scripts/prettier.sh", "lint": "eslint . --ext ts,tsx --max-warnings=0", "lint:fix": "eslint . --ext ts,tsx --fix", "test": "jest", @@ -61,8 +62,6 @@ "react-native-svg": "15.1.0", "react-native-webview": "11.23.1", "react-redux": "8.0.5", - "react-virtualized-auto-sizer": "1.0.20", - "react-window": "1.8.9", "redux": "4.2.1", "redux-persist": "6.0.0", "redux-saga": "1.2.2", @@ -85,7 +84,6 @@ "@testing-library/react-hooks": "7.0.2", "@testing-library/react-native": "11.5.0", "@types/react": "^18.0.15", - "@types/react-window": "1.8.2", "@types/zxcvbn": "4.4.2", "@uniswap/eslint-config": "workspace:^", "depcheck": "1.4.7", diff --git a/packages/wallet/src/components/CurrencyLogo/CurrencyLogo.test.tsx b/packages/wallet/src/components/CurrencyLogo/CurrencyLogo.test.tsx index f8c70b69205..c4f55a3e714 100644 --- a/packages/wallet/src/components/CurrencyLogo/CurrencyLogo.test.tsx +++ b/packages/wallet/src/components/CurrencyLogo/CurrencyLogo.test.tsx @@ -1,9 +1,5 @@ import { CurrencyLogo } from 'uniswap/src/components/CurrencyLogo/CurrencyLogo' -import { - ARBITRUM_DAI_CURRENCY_INFO, - UNI_CURRENCY_INFO, - arbitrumDaiCurrencyInfo, -} from 'wallet/src/test/fixtures' +import { ARBITRUM_DAI_CURRENCY_INFO, UNI_CURRENCY_INFO, arbitrumDaiCurrencyInfo } from 'wallet/src/test/fixtures' import { render } from 'wallet/src/test/test-utils' describe(CurrencyLogo, () => { @@ -30,7 +26,7 @@ describe(CurrencyLogo, () => { it('is rendered if hideNetworkLogo is false', () => { const { queryByTestId } = render( - + , ) const networkLogo = queryByTestId('network-logo') @@ -39,9 +35,7 @@ describe(CurrencyLogo, () => { }) it('is not rendered if hideNetworkLogo is true', () => { - const { queryByTestId } = render( - - ) + const { queryByTestId } = render() const networkLogo = queryByTestId('network-logo') diff --git a/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.test.tsx b/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.test.tsx index c016ed118f7..6a7f0b1ad03 100644 --- a/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.test.tsx +++ b/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.test.tsx @@ -1,8 +1,4 @@ -import { - UniverseChainId, - WALLET_SUPPORTED_CHAIN_IDS, - WalletChainId, -} from 'uniswap/src/types/chains' +import { UniverseChainId, WALLET_SUPPORTED_CHAIN_IDS, WalletChainId } from 'uniswap/src/types/chains' import { WalletConnectEvent } from 'uniswap/src/types/walletConnect' import { DappLogoWithTxStatus, @@ -42,7 +38,7 @@ describe(LogoWithTxStatus, () => { txStatus: TransactionStatus.Pending, chainId: UniverseChainId.Mainnet, })} - /> + />, ) expect(tree).toMatchSnapshot() @@ -51,7 +47,7 @@ describe(LogoWithTxStatus, () => { describe('logo', () => { it('shows Moonpay logo for fiat purchase', () => { const { queryByTestId } = render( - + , ) expect(queryByTestId('moonpay-logo')).toBeTruthy() @@ -80,23 +76,21 @@ describe(LogoWithTxStatus, () => { describe('transaction summary network logo', () => { it('shows network logo if chainId is specified and is not Mainnet', () => { const { queryByTestId } = render( - + , ) expect(queryByTestId('network-logo')).toBeTruthy() }) it('does not show network logo if chainId is not specified', () => { - const { queryByTestId } = render( - - ) + const { queryByTestId } = render() expect(queryByTestId('network-logo')).toBeFalsy() }) it('does not show network logo if chainId is Mainnet', () => { const { queryByTestId } = render( - + , ) expect(queryByTestId('network-logo')).toBeFalsy() @@ -116,21 +110,18 @@ describe(LogoWithTxStatus, () => { TransactionType.Unknown, ] const transactionWithoutIcons = Object.values(TransactionType).filter( - (txType) => !transactionWithIcons.includes(txType) + (txType) => !transactionWithIcons.includes(txType), ) const nftAssetTypesWithIcons = [AssetType.ERC721, AssetType.ERC1155] const nftAssetTypesWithoutIcons = Object.values(AssetType).filter( - (assetType) => !nftAssetTypesWithIcons.includes(assetType) + (assetType) => !nftAssetTypesWithIcons.includes(assetType), ) for (const txType of transactionWithIcons) { it(`shows icon for ${txType}`, () => { const { queryByTestId } = render( - + , ) expect(queryByTestId('status-icon')).toBeTruthy() @@ -144,7 +135,7 @@ describe(LogoWithTxStatus, () => { {...currencyLogoProps({ chainId: UniverseChainId.Mainnet })} assetType={assetType} txType={TransactionType.NFTTrade} - /> + />, ) expect(queryByTestId('status-icon')).toBeTruthy() @@ -157,10 +148,7 @@ describe(LogoWithTxStatus, () => { jest.spyOn(console, 'warn').mockImplementation(consoleWarnMock) const { queryByTestId } = render( - + , ) expect(queryByTestId('status-icon')).toBeFalsy() @@ -168,7 +156,7 @@ describe(LogoWithTxStatus, () => { expect.anything(), expect.anything(), expect.stringContaining('Could not find icon for transaction type:'), - txType + txType, ) }) } @@ -183,7 +171,7 @@ describe(LogoWithTxStatus, () => { {...currencyLogoProps({ chainId: UniverseChainId.Mainnet })} assetType={assetType} txType={TransactionType.NFTTrade} - /> + />, ) expect(queryByTestId('status-icon')).toBeFalsy() @@ -191,7 +179,7 @@ describe(LogoWithTxStatus, () => { expect.anything(), expect.anything(), expect.stringContaining('Could not find icon for transaction type:'), - TransactionType.NFTTrade + TransactionType.NFTTrade, ) }) } @@ -203,7 +191,7 @@ describe(LogoWithTxStatus, () => { // (this is needed because native implementation is not used by default // with our test setup where we exclude files with native extensions) jest.mock('wallet/src/features/images/ImageUri', () => - jest.requireActual('wallet/src/features/images/ImageUri.native.tsx') + jest.requireActual('wallet/src/features/images/ImageUri.native.tsx'), ) describe(DappLogoWithTxStatus, () => { @@ -314,9 +302,7 @@ describe(DappLogoWithWCBadge, () => { }) it('renders wallet connect logo if chain is Mainnet', () => { - const { queryByTestId } = render( - - ) + const { queryByTestId } = render() expect(queryByTestId('network-logo')).toBeFalsy() expect(queryByTestId('wallet-connect-logo')).toBeTruthy() diff --git a/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.tsx b/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.tsx index 4729ae1ec54..c91e8a90faa 100644 --- a/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.tsx +++ b/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.tsx @@ -5,13 +5,7 @@ import type { IconProps } from 'ui/src' import { Flex, useSporeColors } from 'ui/src' import WalletConnectLogo from 'ui/src/assets/icons/walletconnect.svg' import MoonpayLogo from 'ui/src/assets/logos/svg/moonpay.svg' -import { - AlertTriangle, - Approve, - ArrowDownInCircle, - ArrowUpInCircle, - QuestionInCircle, -} from 'ui/src/components/icons' +import { AlertTriangle, Approve, ArrowDownInCircle, ArrowUpInCircle, QuestionInCircle } from 'ui/src/components/icons' import { borderRadii } from 'ui/src/theme' import { CurrencyLogo, STATUS_RATIO } from 'uniswap/src/components/CurrencyLogo/CurrencyLogo' import { TransactionSummaryNetworkLogo } from 'uniswap/src/components/CurrencyLogo/NetworkLogo' @@ -24,11 +18,7 @@ import { AssetType } from 'wallet/src/entities/assets' import { ImageUri } from 'wallet/src/features/images/ImageUri' import { NFTViewer } from 'wallet/src/features/images/NFTViewer' import { RemoteImage } from 'wallet/src/features/images/RemoteImage' -import { - NFTTradeType, - TransactionStatus, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { NFTTradeType, TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' interface LogoWithTxStatusBaseProps { assetType: AssetType @@ -93,7 +83,8 @@ function getLogo(props: LogoWithTxStatusProps): JSX.Element { height={size} overflow="hidden" testID="nft-viewer" - width={size}> + width={size} + > {props.nftImageUrl && } ) @@ -155,12 +146,7 @@ export function LogoWithTxStatus(props: LogoWithTxStatusProps): JSX.Element { useEffect(() => { if (!icon) { - logger.warn( - 'statusIcon', - 'GenerateStatusIcon', - 'Could not find icon for transaction type:', - txType - ) + logger.warn('statusIcon', 'GenerateStatusIcon', 'Could not find icon for transaction type:', txType) } }, [icon, txType]) @@ -195,9 +181,7 @@ export function DappLogoWithTxStatus({ const getStatusIcon = (): JSX.Element | undefined => { switch (event) { case WalletConnectEvent.NetworkChanged: - return chainId ? ( - - ) : undefined + return chainId ? : undefined case WalletConnectEvent.TransactionConfirmed: return case WalletConnectEvent.TransactionFailed: @@ -257,18 +241,22 @@ export function DappLogoWithWCBadge({ dappName, size, chainId, + hideWCBadge = false, + circular = false, }: { dappImageUrl: Maybe dappName: string size: number chainId: WalletChainId | null + hideWCBadge?: boolean + circular?: boolean }): JSX.Element { const dappImageSize = size const statusSize = dappImageSize * STATUS_RATIO const totalSize = dappImageSize + statusSize * (1 / 4) const dappImage = dappImageUrl ? ( + {dappImage} @@ -287,7 +275,7 @@ export function DappLogoWithWCBadge({ - ) : ( + ) : !hideWCBadge ? ( + right={-2} + > - )} + ) : null} ) } diff --git a/packages/wallet/src/components/CurrencyLogo/SplitLogo.test.tsx b/packages/wallet/src/components/CurrencyLogo/SplitLogo.test.tsx index eca8d3febec..483204f7f6a 100644 --- a/packages/wallet/src/components/CurrencyLogo/SplitLogo.test.tsx +++ b/packages/wallet/src/components/CurrencyLogo/SplitLogo.test.tsx @@ -1,11 +1,6 @@ import { UniverseChainId } from 'uniswap/src/types/chains' import { SplitLogo } from 'wallet/src/components/CurrencyLogo/SplitLogo' -import { - DAI_CURRENCY_INFO, - ETH_CURRENCY_INFO, - daiCurrencyInfo, - ethCurrencyInfo, -} from 'wallet/src/test/fixtures' +import { DAI_CURRENCY_INFO, ETH_CURRENCY_INFO, daiCurrencyInfo, ethCurrencyInfo } from 'wallet/src/test/fixtures' import { render, within } from 'wallet/src/test/test-utils' describe(SplitLogo, () => { @@ -16,7 +11,7 @@ describe(SplitLogo, () => { inputCurrencyInfo={DAI_CURRENCY_INFO} outputCurrencyInfo={ETH_CURRENCY_INFO} size={10} - /> + />, ) expect(tree).toMatchSnapshot() @@ -30,7 +25,7 @@ describe(SplitLogo, () => { inputCurrencyInfo={daiCurrencyInfo()} outputCurrencyInfo={ethCurrencyInfo()} size={10} - /> + />, ) const inputCurrencyLogo = getByTestId('input-currency-logo-container') @@ -45,7 +40,7 @@ describe(SplitLogo, () => { inputCurrencyInfo={null} outputCurrencyInfo={ethCurrencyInfo()} size={10} - /> + />, ) const inputCurrencyLogo = getByTestId('input-currency-logo-container') @@ -62,7 +57,7 @@ describe(SplitLogo, () => { inputCurrencyInfo={daiCurrencyInfo()} outputCurrencyInfo={ethCurrencyInfo()} size={10} - /> + />, ) const outputCurrencyLogo = getByTestId('output-currency-logo-container') @@ -77,7 +72,7 @@ describe(SplitLogo, () => { inputCurrencyInfo={daiCurrencyInfo()} outputCurrencyInfo={null} size={10} - /> + />, ) const outputCurrencyLogo = getByTestId('output-currency-logo-container') @@ -94,7 +89,7 @@ describe(SplitLogo, () => { inputCurrencyInfo={daiCurrencyInfo()} outputCurrencyInfo={ethCurrencyInfo()} size={10} - /> + />, ) const icon = getByTestId('network-logo') @@ -109,7 +104,7 @@ describe(SplitLogo, () => { inputCurrencyInfo={daiCurrencyInfo()} outputCurrencyInfo={ethCurrencyInfo()} size={10} - /> + />, ) const icon = queryByTestId('network-logo') diff --git a/packages/wallet/src/components/CurrencyLogo/SplitLogo.tsx b/packages/wallet/src/components/CurrencyLogo/SplitLogo.tsx index 6dfe03ac463..351d769a247 100644 --- a/packages/wallet/src/components/CurrencyLogo/SplitLogo.tsx +++ b/packages/wallet/src/components/CurrencyLogo/SplitLogo.tsx @@ -15,12 +15,7 @@ interface Props { * Logo, where left 50% of width is taken from one icon (its left 50%) * and right side is taken from another icon (its right 50%) */ -export function SplitLogo({ - size, - inputCurrencyInfo, - outputCurrencyInfo, - chainId, -}: Props): JSX.Element { +export function SplitLogo({ size, inputCurrencyInfo, outputCurrencyInfo, chainId }: Props): JSX.Element { const iconSize = size / 2 const icon = @@ -36,7 +31,8 @@ export function SplitLogo({ position="absolute" testID="input-currency-logo-container" top={0} - width={iconSize - 1 /* -1 to allow for space between the icons */}> + width={iconSize - 1 /* -1 to allow for space between the icons */} + > + width={iconSize - 1 /* -1 to allow for space between the icons */} + > {icon && ( diff --git a/packages/wallet/src/components/CurrencyLogo/__snapshots__/LogoWithTxStatus.test.tsx.snap b/packages/wallet/src/components/CurrencyLogo/__snapshots__/LogoWithTxStatus.test.tsx.snap index d0853b45b13..6171553c043 100644 --- a/packages/wallet/src/components/CurrencyLogo/__snapshots__/LogoWithTxStatus.test.tsx.snap +++ b/packages/wallet/src/components/CurrencyLogo/__snapshots__/LogoWithTxStatus.test.tsx.snap @@ -69,8 +69,10 @@ exports[`DappLogoWithWCBadge renders without error 1`] = ` { }) it('renders a currency logo with network logo', () => { - const tree = renderWithProviders( - - ) + const tree = renderWithProviders() expect(tree).toMatchSnapshot() }) diff --git a/packages/wallet/src/components/DevelopmentOnly/UniconSampleSheet.tsx b/packages/wallet/src/components/DevelopmentOnly/UniconSampleSheet.tsx index 11842b89945..b2821a5afbe 100644 --- a/packages/wallet/src/components/DevelopmentOnly/UniconSampleSheet.tsx +++ b/packages/wallet/src/components/DevelopmentOnly/UniconSampleSheet.tsx @@ -13,11 +13,7 @@ const generateRandomEthereumAddresses = (numberOfAddresses: number): string[] => export const UniconSampleSheet = ({ onClose }: { onClose: () => void }): JSX.Element => { return ( - + {generateRandomEthereumAddresses(80).map((address) => { diff --git a/packages/wallet/src/components/ErrorBoundary/ErrorBoundary.tsx b/packages/wallet/src/components/ErrorBoundary/ErrorBoundary.tsx index bebf404109e..67a3b48dcac 100644 --- a/packages/wallet/src/components/ErrorBoundary/ErrorBoundary.tsx +++ b/packages/wallet/src/components/ErrorBoundary/ErrorBoundary.tsx @@ -64,13 +64,7 @@ function ErrorScreen({ error }: { error: Error }): JSX.Element { } return ( - + diff --git a/packages/wallet/src/components/QRCodeScanner/QRCode.tsx b/packages/wallet/src/components/QRCodeScanner/QRCode.tsx index 5c93bb368aa..800e996fbc4 100644 --- a/packages/wallet/src/components/QRCodeScanner/QRCode.tsx +++ b/packages/wallet/src/components/QRCodeScanner/QRCode.tsx @@ -159,7 +159,8 @@ const _QRCodeDisplay = ({ shadowColor={displayShadow ? '$sporeBlack' : 'transparent'} shadowOffset={{ width: 0, height: 16 }} shadowOpacity={displayShadow ? 0.1 : 0} - shadowRadius={16}> + shadowRadius={16} + > + pt="$spacing2" + > + py={isWeb ? '$spacing60' : '$spacing24'} + > { - const arr = Array.prototype.slice.call( - QRCode.create(value, { errorCorrectionLevel }).modules.data, - 0 - ) + const arr = Array.prototype.slice.call(QRCode.create(value, { errorCorrectionLevel }).modules.data, 0) const sqrt = Math.sqrt(arr.length) return arr.reduce( - (rows, key, index) => - (index % sqrt === 0 ? rows.push([key]) : rows[rows.length - 1].push(key)) && rows, - [] + (rows, key, index) => (index % sqrt === 0 ? rows.push([key]) : rows[rows.length - 1].push(key)) && rows, + [], ) } diff --git a/packages/wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/index.jsx b/packages/wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/index.jsx index 194408b991a..a25b80d73a5 100644 --- a/packages/wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/index.jsx +++ b/packages/wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/index.jsx @@ -1,11 +1,11 @@ // Component logic from: https://github.com/awesomejerry/react-native-qrcode-svg // Custom matric renderer from: https://github.com/awesomejerry/react-native-qrcode-svg/pull/139/files -import React, { useMemo } from 'react' +import { useMemo } from 'react' import Svg, { Defs, G, LinearGradient, Path, Rect, Stop } from 'react-native-svg' +import { useMedia } from 'ui/src' import genMatrix from 'wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/genMatrix.js' import transformMatrixIntoPath from 'wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/transformMatrixIntoCirclePath.js' -import { useMedia } from 'ui/src' const QREyes = ({ x = -1, y = -1, fillColor, size }) => ( @@ -78,7 +78,8 @@ const QRCode = ({ ref={getRef} height={size} viewBox={[-quietZone, -quietZone, size + quietZone * 2, size + quietZone * 2].join(' ')} - width={size}> + width={size} + > + y2={gradientDirection[2]} + > diff --git a/packages/wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/transformMatrixIntoCirclePath.js b/packages/wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/transformMatrixIntoCirclePath.js index e2935a8b5f3..4c76a258b6f 100644 --- a/packages/wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/transformMatrixIntoCirclePath.js +++ b/packages/wallet/src/components/QRCodeScanner/custom-qr-code-generator/src/transformMatrixIntoCirclePath.js @@ -1,25 +1,25 @@ export default (matrix, size) => { - const cellSize = size / matrix.length; - const radius = cellSize / 2; - let path = ''; + const cellSize = size / matrix.length + const radius = cellSize / 2 + let path = '' matrix.forEach((row, i) => { row.forEach((column, j) => { if (column) { - const cx = j * cellSize + radius; - const cy = i * cellSize + radius; + const cx = j * cellSize + radius + const cy = i * cellSize + radius path += ` M ${cx - radius},${cy} A ${radius},${radius} 0 1,0 ${cx + radius},${cy} A ${radius},${radius} 0 1,0 ${cx - radius},${cy} - `; + ` } - }); - }); + }) + }) return { cellSize, path, - }; -}; + } +} diff --git a/packages/wallet/src/components/RecipientSearch/RecipientList.tsx b/packages/wallet/src/components/RecipientSearch/RecipientList.tsx index a7d914a50a2..111482d599a 100644 --- a/packages/wallet/src/components/RecipientSearch/RecipientList.tsx +++ b/packages/wallet/src/components/RecipientSearch/RecipientList.tsx @@ -19,14 +19,9 @@ interface RecipientListProps { onPress: (recipient: string) => void } -export function RecipientList({ - onPress, - sections, - renderedInModal = false, -}: RecipientListProps): JSX.Element { +export function RecipientList({ onPress, sections, renderedInModal = false }: RecipientListProps): JSX.Element { const insets = useDeviceInsets() - const [selectedViewOnlyRecipient, setSelectedViewOnlyRecipient] = - useState(null) + const [selectedViewOnlyRecipient, setSelectedViewOnlyRecipient] = useState(null) const onRecipientPress = useCallback( (recipient: SearchableRecipient) => { @@ -37,7 +32,7 @@ export function RecipientList({ onPress(recipient.address) } }, - [onPress] + [onPress], ) const onConfirmViewOnlyRecipient = useCallback(() => { @@ -91,7 +86,8 @@ function SectionHeader(info: { section: SectionListData }): entering={FadeIn} // TODO(EXT-526): re-enable `exiting` animation when it's fixed. exiting={isWeb ? undefined : FadeOut} - py="$spacing8"> + py="$spacing8" + > {info.section.title} @@ -108,15 +104,9 @@ interface RecipientProps { onPress: (recipient: SearchableRecipient) => void } -export const RecipientRow = memo(function RecipientRow({ - recipient, - onPress, -}: RecipientProps): JSX.Element { +export const RecipientRow = memo(function RecipientRow({ recipient, onPress }: RecipientProps): JSX.Element { const domain = recipient.name - ? extractDomain( - recipient.name, - recipient.isUnitag ? SearchResultType.Unitag : SearchResultType.ENSAddress - ) + ? extractDomain(recipient.name, recipient.isUnitag ? SearchResultType.Unitag : SearchResultType.ENSAddress) : undefined const onPressWithAnalytics = (): void => { diff --git a/packages/wallet/src/components/RecipientSearch/ViewOnlyRecipientModal.tsx b/packages/wallet/src/components/RecipientSearch/ViewOnlyRecipientModal.tsx index 24734de1ac1..be9009c75c7 100644 --- a/packages/wallet/src/components/RecipientSearch/ViewOnlyRecipientModal.tsx +++ b/packages/wallet/src/components/RecipientSearch/ViewOnlyRecipientModal.tsx @@ -10,10 +10,7 @@ type ViewOnlyRecipientModalProps = { onCancel: () => void } -export function ViewOnlyRecipientModal({ - onConfirm, - onCancel, -}: ViewOnlyRecipientModalProps): JSX.Element { +export function ViewOnlyRecipientModal({ onConfirm, onCancel }: ViewOnlyRecipientModalProps): JSX.Element { const { t } = useTranslation() return ( @@ -25,7 +22,8 @@ export function ViewOnlyRecipientModal({ borderRadius="$rounded12" height={iconSizes.icon48} mb="$spacing8" - width={iconSizes.icon48}> + width={iconSizes.icon48} + > diff --git a/packages/wallet/src/components/RecipientSearch/filter.ts b/packages/wallet/src/components/RecipientSearch/filter.ts index 86926b5264d..2be1e3ceb7f 100644 --- a/packages/wallet/src/components/RecipientSearch/filter.ts +++ b/packages/wallet/src/components/RecipientSearch/filter.ts @@ -25,7 +25,7 @@ const searchNameOptions: Fuse.IFuseOptions[] + list: AutocompleteOption[], ): AutocompleteOption[] { if (!searchPattern) { return [] @@ -40,7 +40,7 @@ export function filterRecipientsByName( export function filterRecipientsByAddress( searchPattern: string | null, - list: AutocompleteOption[] + list: AutocompleteOption[], ): AutocompleteOption[] { if (!searchPattern) { return [] @@ -55,7 +55,7 @@ export function filterRecipientsByAddress( export function filterRecipientByNameAndAddress( searchPattern: string | null, - list: AutocompleteOption[] + list: AutocompleteOption[], ): AutocompleteOption[] { if (!searchPattern) { return [] @@ -63,10 +63,7 @@ export function filterRecipientByNameAndAddress( // run both fuses and remove dupes return unique( - [ - ...filterRecipientsByAddress(searchPattern, list), - ...filterRecipientsByName(searchPattern, list), - ], - (v, i, a) => a.findIndex((v2) => v2.data.address === v.data.address) === i + [...filterRecipientsByAddress(searchPattern, list), ...filterRecipientsByName(searchPattern, list)], + (v, i, a) => a.findIndex((v2) => v2.data.address === v.data.address) === i, ) } diff --git a/packages/wallet/src/components/RecipientSearch/hooks.ts b/packages/wallet/src/components/RecipientSearch/hooks.ts index 042a1118a6b..d7246af11b3 100644 --- a/packages/wallet/src/components/RecipientSearch/hooks.ts +++ b/packages/wallet/src/components/RecipientSearch/hooks.ts @@ -3,6 +3,7 @@ import { useCallback, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' import { useUnitagByName } from 'uniswap/src/features/unitags/hooks' import { UniverseChainId } from 'uniswap/src/types/chains' +import { getValidAddress } from 'uniswap/src/utils/addresses' import { useMemoCompare } from 'utilities/src/react/hooks' import { useDebounce } from 'utilities/src/time/timing' import { filterRecipientByNameAndAddress } from 'wallet/src/components/RecipientSearch/filter' @@ -16,7 +17,6 @@ import { selectRecipientsByRecency } from 'wallet/src/features/transactions/sele import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { selectInactiveAccounts } from 'wallet/src/features/wallet/selectors' import { useAppSelector } from 'wallet/src/state' -import { getValidAddress } from 'wallet/src/utils/addresses' const MAX_RECENT_RECIPIENTS = 15 @@ -27,7 +27,7 @@ type RecipientSection = { function useValidatedSearchedAddress( searchTerm: string, - debounceDelayMs?: number + debounceDelayMs?: number, ): { recipients: SearchableRecipient[] searchTerm: string @@ -40,11 +40,7 @@ function useValidatedSearchedAddress( name: dotEthName, } = useENS(UniverseChainId.Mainnet, searchTerm, true) - const { - loading: ensLoading, - address: ensAddress, - name: ensName, - } = useENS(UniverseChainId.Mainnet, searchTerm, false) + const { loading: ensLoading, address: ensAddress, name: ensName } = useENS(UniverseChainId.Mainnet, searchTerm, false) const { loading: unitagLoading, unitag } = useUnitagByName(searchTerm ?? undefined) @@ -108,7 +104,7 @@ function useValidatedSearchedAddress( searchTerm, loading: dotEthLoading || ensLoading || unitagLoading, }), - [memoRecipients, searchTerm, dotEthLoading, ensLoading, unitagLoading] + [memoRecipients, searchTerm, dotEthLoading, ensLoading, unitagLoading], ) // Debounce search results to prevent flickering const debouncedResult = useDebounce(memoResult, debounceDelayMs) @@ -120,7 +116,7 @@ function useValidatedSearchedAddress( export function useRecipients( pattern: string, - debounceDelayMs?: number + debounceDelayMs?: number, ): { sections: RecipientSection[] searchableRecipientOptions: { @@ -144,9 +140,9 @@ export function useRecipients( } return acc }, - { importedWallets: [], viewOnlyWallets: [] } + { importedWallets: [], viewOnlyWallets: [] }, ), - [inactiveLocalAccounts] + [inactiveLocalAccounts], ) const recentRecipients = useAppSelector(selectRecipientsByRecency).slice(0, MAX_RECENT_RECIPIENTS) @@ -202,7 +198,7 @@ export function useRecipients( (address) => { address, - } + }, ), }) } @@ -220,12 +216,10 @@ export function useRecipients( const searchableRecipientOptions = useMemo( () => - uniqueAddressesOnly([ - ...validatedAddressRecipients, - ...inactiveLocalAccounts, - ...recentRecipients, - ]).map((item) => ({ data: item, key: item.address })), - [recentRecipients, validatedAddressRecipients, inactiveLocalAccounts] + uniqueAddressesOnly([...validatedAddressRecipients, ...inactiveLocalAccounts, ...recentRecipients]).map( + (item) => ({ data: item, key: item.address }), + ), + [recentRecipients, validatedAddressRecipients, inactiveLocalAccounts], ) return useMemo( @@ -235,25 +229,21 @@ export function useRecipients( loading, debouncedPattern: searchTerm, }), - [loading, searchableRecipientOptions, sections, searchTerm] + [loading, searchableRecipientOptions, sections, searchTerm], ) } -export function useFilteredRecipientSections( - searchPattern: string, - debounceDelayMs?: number -): RecipientSection[] { +export function useFilteredRecipientSections(searchPattern: string, debounceDelayMs?: number): RecipientSection[] { const sectionsRef = useRef([]) const { sections, searchableRecipientOptions, loading, debouncedPattern } = useRecipients( searchPattern, - debounceDelayMs + debounceDelayMs, ) const getFilteredSections = useCallback(() => { - const filteredAddresses = filterRecipientByNameAndAddress( - debouncedPattern, - searchableRecipientOptions - ).map((item) => item.data.address) + const filteredAddresses = filterRecipientByNameAndAddress(debouncedPattern, searchableRecipientOptions).map( + (item) => item.data.address, + ) return filterSections(sections, filteredAddresses) }, [debouncedPattern, searchableRecipientOptions, sections]) diff --git a/packages/wallet/src/components/RecipientSearch/utils.ts b/packages/wallet/src/components/RecipientSearch/utils.ts index ae3ac923af0..07f00a083e9 100644 --- a/packages/wallet/src/components/RecipientSearch/utils.ts +++ b/packages/wallet/src/components/RecipientSearch/utils.ts @@ -3,7 +3,7 @@ import { SearchableRecipient } from 'wallet/src/features/address/types' export function filterSections( sections: SectionListData[], - filteredAddresses: string[] + filteredAddresses: string[], ): { title: string; data: SearchableRecipient[] }[] { return sections .map((section) => { diff --git a/packages/wallet/src/components/TokenSelector/SelectTokenButton.tsx b/packages/wallet/src/components/TokenSelector/SelectTokenButton.tsx index c313e2c120f..b8cb82057fa 100644 --- a/packages/wallet/src/components/TokenSelector/SelectTokenButton.tsx +++ b/packages/wallet/src/components/TokenSelector/SelectTokenButton.tsx @@ -12,11 +12,7 @@ interface SelectTokenButtonProps { testID?: string } -export function SelectTokenButton({ - selectedCurrencyInfo, - onPress, - testID, -}: SelectTokenButtonProps): JSX.Element { +export function SelectTokenButton({ selectedCurrencyInfo, onPress, testID }: SelectTokenButtonProps): JSX.Element { const { t } = useTranslation() return ( @@ -25,7 +21,8 @@ export function SelectTokenButton({ backgroundColor={selectedCurrencyInfo ? '$surface3' : '$accent1'} borderRadius="$roundedFull" testID={testID} - onPress={onPress}> + onPress={onPress} + > {selectedCurrencyInfo ? ( @@ -33,32 +30,16 @@ export function SelectTokenButton({ {getSymbolDisplayText(selectedCurrencyInfo.currency.symbol)} {isWeb && ( - + )} ) : ( - + {t('tokens.selector.button.choose')} {isWeb && ( - + )} )} diff --git a/packages/wallet/src/components/TokenSelector/TokenOptionItem.tsx b/packages/wallet/src/components/TokenSelector/TokenOptionItem.tsx index 87496f5048b..fd954fe4f63 100644 --- a/packages/wallet/src/components/TokenSelector/TokenOptionItem.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenOptionItem.tsx @@ -3,16 +3,16 @@ import { Keyboard } from 'react-native' import { Flex, ImpactFeedbackStyle, Text, TouchableArea, isWeb } from 'ui/src' import { iconSizes } from 'ui/src/theme' import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' +import { TokenOption } from 'uniswap/src/components/TokenSelector/types' +import WarningIcon from 'uniswap/src/components/icons/WarningIcon' import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { shortenAddress } from 'uniswap/src/utils/addresses' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' import { NumberType } from 'utilities/src/format/types' -import { TokenOption } from 'wallet/src/components/TokenSelector/types' -import WarningIcon from 'wallet/src/components/icons/WarningIcon' import { InlineNetworkPill } from 'wallet/src/components/network/NetworkPill' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import TokenWarningModal from 'wallet/src/features/tokens/TokenWarningModal' import { useTokenWarningDismissed } from 'wallet/src/features/tokens/safetyHooks' -import { shortenAddress } from 'wallet/src/utils/addresses' interface OptionProps { option: TokenOption @@ -67,13 +67,9 @@ function _TokenOptionItem({ opacity={showWarnings && safetyLevel === SafetyLevel.Blocked ? 0.5 : 1} testID={`token-option-${currency.chainId}-${currency.symbol}`} width="100%" - onPress={onPressTokenOption}> - + onPress={onPressTokenOption} + > + - {(safetyLevel === SafetyLevel.Blocked || - safetyLevel === SafetyLevel.StrongWarning) && ( - + {(safetyLevel === SafetyLevel.Blocked || safetyLevel === SafetyLevel.StrongWarning) && ( + )} diff --git a/packages/wallet/src/components/TokenSelector/TokenSelector.tsx b/packages/wallet/src/components/TokenSelector/TokenSelector.tsx index 5d2efd8b5d4..9eeeebd8fe0 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSelector.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenSelector.tsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next' import { Keyboard, LayoutAnimation } from 'react-native' import { Flex, isWeb, useSporeColors } from 'ui/src' import { zIndices } from 'ui/src/theme' +import { SuggestedTokenSection, TokenSection } from 'uniswap/src/components/TokenSelector/types' import { useBottomSheetContext } from 'uniswap/src/components/modals/BottomSheetContext' import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal' import { NetworkFilter } from 'uniswap/src/components/network/NetworkFilter' @@ -19,7 +20,6 @@ import { TokenSelectorSendList } from 'wallet/src/components/TokenSelector/Token import { TokenSelectorSwapInputList } from 'wallet/src/components/TokenSelector/TokenSelectorSwapInputList' import { TokenSelectorSwapOutputList } from 'wallet/src/components/TokenSelector/TokenSelectorSwapOutputList' import { useFilterCallbacks } from 'wallet/src/components/TokenSelector/hooks' -import { SuggestedTokenSection, TokenSection } from 'wallet/src/components/TokenSelector/types' import PasteButton from 'wallet/src/components/buttons/PasteButton' import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { SearchContext } from 'wallet/src/features/search/SearchContext' @@ -45,11 +45,7 @@ export interface TokenSelectorProps { chainId?: WalletChainId isSurfaceReady?: boolean onClose: () => void - onSelectCurrency: ( - currency: Currency, - currencyField: CurrencyField, - context: SearchContext - ) => void + onSelectCurrency: (currency: Currency, currencyField: CurrencyField, context: SearchContext) => void variation: TokenSelectorVariation } @@ -64,10 +60,7 @@ function TokenSelectorContent({ }: TokenSelectorProps): JSX.Element { const { navigateToBuyOrReceiveWithEmptyWallet } = useWalletNavigation() - const { onChangeChainFilter, onChangeText, searchFilter, chainFilter } = useFilterCallbacks( - chainId ?? null, - flow - ) + const { onChangeChainFilter, onChangeText, searchFilter, chainFilter } = useFilterCallbacks(chainId ?? null, flow) const debouncedSearchFilter = useDebounce(searchFilter) const [hasClipboardString, setHasClipboardString] = useState(false) @@ -93,11 +86,7 @@ function TokenSelectorContent({ : undefined const onSelectCurrencyCallback = useCallback( - ( - currencyInfo: CurrencyInfo, - section: SuggestedTokenSection | TokenSection, - index: number - ): void => { + (currencyInfo: CurrencyInfo, section: SuggestedTokenSection | TokenSection, index: number): void => { const searchContext: SearchContext = { category: section.title, query: debouncedSearchFilter ?? undefined, @@ -107,7 +96,7 @@ function TokenSelectorContent({ onSelectCurrency(currencyInfo.currency, currencyField, searchContext) }, - [currencyField, onSelectCurrency, debouncedSearchFilter] + [currencyField, onSelectCurrency, debouncedSearchFilter], ) const handlePaste = async (): Promise => { @@ -160,19 +149,9 @@ function TokenSelectorContent({ /> ) case TokenSelectorVariation.BalancesAndPopular: - return ( - - ) + return case TokenSelectorVariation.SuggestedAndFavoritesAndPopular: - return ( - - ) + return } }, [ searchInFocus, @@ -190,7 +169,8 @@ function TokenSelectorContent({ + py="$spacing8" + > Keyboard.dismiss()} - onPressAnimation={() => - LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) - } + onPressAnimation={() => LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)} onPressChain={onChangeChainFilter} /> @@ -253,7 +231,8 @@ function _TokenSelectorModal(props: TokenSelectorProps): JSX.Element { backgroundColor={colors.surface1.get()} name={ModalName.TokenSelector} snapPoints={['65%', '100%']} - onClose={props.onClose}> + onClose={props.onClose} + > ) diff --git a/packages/wallet/src/components/TokenSelector/TokenSelectorEmptySearchList.tsx b/packages/wallet/src/components/TokenSelector/TokenSelectorEmptySearchList.tsx index 8e7b4c174e3..85dbc1306e4 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSelectorEmptySearchList.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenSelectorEmptySearchList.tsx @@ -1,23 +1,19 @@ import { memo, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { Text, TouchableArea } from 'ui/src' +import { OnSelectCurrency, TokenOption, TokenSection } from 'uniswap/src/components/TokenSelector/types' +import { getTokenOptionsSection } from 'uniswap/src/components/TokenSelector/utils' import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GqlResult } from 'uniswap/src/data/types' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' +import { currencyId } from 'uniswap/src/utils/currencyId' import { TokenSelectorList } from 'wallet/src/components/TokenSelector/TokenSelectorList' -import { - OnSelectCurrency, - TokenOption, - TokenSection, -} from 'wallet/src/components/TokenSelector/types' -import { getTokenOptionsSection } from 'wallet/src/components/TokenSelector/utils' import { buildCurrency, gqlTokenToCurrencyInfo } from 'wallet/src/features/dataApi/utils' import { SearchResultType, TokenSearchResult } from 'wallet/src/features/search/SearchResult' import { clearSearchHistory } from 'wallet/src/features/search/searchHistorySlice' import { selectSearchHistory } from 'wallet/src/features/search/selectSearchHistory' import { usePopularTokens } from 'wallet/src/features/tokens/hooks' import { useAppDispatch, useAppSelector } from 'wallet/src/state' -import { currencyId } from 'wallet/src/utils/currencyId' function searchResultToCurrencyInfo({ chainId, @@ -50,9 +46,7 @@ function searchResultToCurrencyInfo({ return currencyInfo } -function currencyInfosToTokenOptions( - currencyInfos: Array | undefined -): TokenOption[] | undefined { +function currencyInfosToTokenOptions(currencyInfos: Array | undefined): TokenOption[] | undefined { return currencyInfos ?.filter((cI): cI is CurrencyInfo => Boolean(cI)) .map((currencyInfo) => ({ @@ -92,20 +86,17 @@ function useTokenSectionsForEmptySearch(): GqlResult { t('tokens.selector.section.recent'), currencyInfosToTokenOptions( searchHistory - .filter( - (searchResult): searchResult is TokenSearchResult => - searchResult.type === SearchResultType.Token - ) - .map(searchResultToCurrencyInfo) + .filter((searchResult): searchResult is TokenSearchResult => searchResult.type === SearchResultType.Token) + .map(searchResultToCurrencyInfo), ), - + , ) ?? []), ...(getTokenOptionsSection( t('tokens.selector.section.popular'), - currencyInfosToTokenOptions(popularTokens?.map(gqlTokenToCurrencyInfo)) + currencyInfosToTokenOptions(popularTokens?.map(gqlTokenToCurrencyInfo)), ) ?? []), ], - [onPressClearSearchHistory, popularTokens, searchHistory, t] + [onPressClearSearchHistory, popularTokens, searchHistory, t], ) return useMemo( @@ -113,15 +104,11 @@ function useTokenSectionsForEmptySearch(): GqlResult { data: sections, loading, }), - [loading, sections] + [loading, sections], ) } -function _TokenSelectorEmptySearchList({ - onSelectCurrency, -}: { - onSelectCurrency: OnSelectCurrency -}): JSX.Element { +function _TokenSelectorEmptySearchList({ onSelectCurrency }: { onSelectCurrency: OnSelectCurrency }): JSX.Element { const { t } = useTranslation() const { data: sections, loading, error, refetch } = useTokenSectionsForEmptySearch() diff --git a/packages/wallet/src/components/TokenSelector/TokenSelectorList.tsx b/packages/wallet/src/components/TokenSelector/TokenSelectorList.tsx index c2a43c3c77d..bef9a0bd517 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSelectorList.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenSelectorList.tsx @@ -5,32 +5,30 @@ import { Flex, Loader, Skeleton, Text, isWeb } from 'ui/src' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { fonts } from 'ui/src/theme' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' -import { UniverseChainId } from 'uniswap/src/types/chains' -import { CurrencyId } from 'uniswap/src/types/currency' -import { TokenOptionItem } from 'wallet/src/components/TokenSelector/TokenOptionItem' import { SectionHeaderProps, TokenSectionBaseList, TokenSectionBaseListRef, -} from 'wallet/src/components/TokenSelector/TokenSectionBaseList' -import { renderSuggestedTokenItem } from 'wallet/src/components/TokenSelector/renderSuggestedTokenItem' -import { suggestedTokensKeyExtractor } from 'wallet/src/components/TokenSelector/suggestedTokensKeyExtractor' +} from 'uniswap/src/components/TokenSelector/TokenSectionBaseList' +import { renderSuggestedTokenItem } from 'uniswap/src/components/TokenSelector/renderSuggestedTokenItem' +import { suggestedTokensKeyExtractor } from 'uniswap/src/components/TokenSelector/suggestedTokensKeyExtractor' import { OnSelectCurrency, SuggestedTokenSection, TokenOption, TokenSection, TokenSelectorListSections, -} from 'wallet/src/components/TokenSelector/types' +} from 'uniswap/src/components/TokenSelector/types' +import { UniverseChainId } from 'uniswap/src/types/chains' +import { CurrencyId } from 'uniswap/src/types/currency' +import { TokenOptionItem } from 'wallet/src/components/TokenSelector/TokenOptionItem' import { useBottomSheetFocusHook } from 'wallet/src/components/modals/hooks' function isSuggestedTokenItem(data: TokenOption | TokenOption[]): data is TokenOption[] { return Array.isArray(data) } -function isSuggestedTokenSection( - section: SuggestedTokenSection | TokenSection -): section is SuggestedTokenSection { +function isSuggestedTokenSection(section: SuggestedTokenSection | TokenSection): section is SuggestedTokenSection { return Array.isArray((section as SuggestedTokenSection).data[0]) } @@ -53,15 +51,13 @@ function TokenOptionItemWrapper({ }): JSX.Element { const onPress = useCallback( () => onSelectCurrency(tokenOption.currencyInfo, section, index), - [index, onSelectCurrency, section, tokenOption.currencyInfo] + [index, onSelectCurrency, section, tokenOption.currencyInfo], ) return ( ( ), - [] + [], ) if (hasError) { @@ -197,12 +193,7 @@ function _TokenSelectorList({ export function SectionHeader({ title, rightElement }: SectionHeaderProps): JSX.Element { return ( - + {title} diff --git a/packages/wallet/src/components/TokenSelector/TokenSelectorSearchResultsList.tsx b/packages/wallet/src/components/TokenSelector/TokenSelectorSearchResultsList.tsx index c9bebf29384..d75f4ffd941 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSelectorSearchResultsList.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenSelectorSearchResultsList.tsx @@ -1,21 +1,12 @@ import { memo, useCallback, useMemo } from 'react' import { Trans, useTranslation } from 'react-i18next' import { Flex, Text } from 'ui/src' +import { OnSelectCurrency, TokenSection } from 'uniswap/src/components/TokenSelector/types' +import { formatSearchResults, getTokenOptionsSection } from 'uniswap/src/components/TokenSelector/utils' import { GqlResult } from 'uniswap/src/data/types' import { UniverseChainId } from 'uniswap/src/types/chains' -import { - SectionHeader, - TokenSelectorList, -} from 'wallet/src/components/TokenSelector/TokenSelectorList' -import { - usePortfolioBalancesForAddressById, - usePortfolioTokenOptions, -} from 'wallet/src/components/TokenSelector/hooks' -import { OnSelectCurrency, TokenSection } from 'wallet/src/components/TokenSelector/types' -import { - formatSearchResults, - getTokenOptionsSection, -} from 'wallet/src/components/TokenSelector/utils' +import { SectionHeader, TokenSelectorList } from 'wallet/src/components/TokenSelector/TokenSelectorList' +import { usePortfolioBalancesForAddressById, usePortfolioTokenOptions } from 'wallet/src/components/TokenSelector/hooks' import { useSearchTokens } from 'wallet/src/features/dataApi/searchTokens' import { SearchResultType } from 'wallet/src/features/search/SearchResult' import { addToSearchHistory } from 'wallet/src/features/search/searchHistorySlice' @@ -41,7 +32,7 @@ function EmptyResults({ searchFilter }: { searchFilter: string }): JSX.Element { function useTokenSectionsForSearchResults( chainFilter: UniverseChainId | null, searchFilter: string | null, - isBalancesOnlySearch: boolean + isBalancesOnlySearch: boolean, ): GqlResult { const { t } = useTranslation() const activeAccountAddress = useActiveAccountAddressWithThrow() @@ -73,18 +64,16 @@ function useTokenSectionsForSearchResults( }, [searchResultCurrencies, portfolioBalancesById, searchFilter]) const loading = - portfolioTokenOptionsLoading || - portfolioBalancesByIdLoading || - (!isBalancesOnlySearch && searchTokensLoading) + portfolioTokenOptionsLoading || portfolioBalancesByIdLoading || (!isBalancesOnlySearch && searchTokensLoading) const sections = useMemo( () => getTokenOptionsSection( t('tokens.selector.section.search'), // Use local search when only searching balances - isBalancesOnlySearch ? portfolioTokenOptions : searchResults + isBalancesOnlySearch ? portfolioTokenOptions : searchResults, ), - [isBalancesOnlySearch, portfolioTokenOptions, searchResults, t] + [isBalancesOnlySearch, portfolioTokenOptions, searchResults, t], ) const error = @@ -105,7 +94,7 @@ function useTokenSectionsForSearchResults( error: error || undefined, refetch: refetchAll, }), - [error, loading, refetchAll, sections] + [error, loading, refetchAll, sections], ) } @@ -145,7 +134,7 @@ function _TokenSelectorSearchResultsList({ logoUrl: currencyInfo.logoUrl ?? null, safetyLevel: currencyInfo.safetyLevel ?? null, }, - }) + }), ) } } @@ -153,9 +142,8 @@ function _TokenSelectorSearchResultsList({ const userIsTyping = Boolean(searchFilter && debouncedSearchFilter !== searchFilter) const emptyElement = useMemo( - () => - debouncedSearchFilter ? : undefined, - [debouncedSearchFilter] + () => (debouncedSearchFilter ? : undefined), + [debouncedSearchFilter], ) return ( getTokenOptionsSection(t('tokens.selector.section.yours'), portfolioTokenOptions), - [portfolioTokenOptions, t] + [portfolioTokenOptions, t], ) return useMemo( @@ -42,7 +39,7 @@ function useTokenSectionsForSend(chainFilter: UniverseChainId | null): GqlResult error: error || undefined, refetch: refetchPortfolioTokenOptions, }), - [error, loading, refetchPortfolioTokenOptions, sections] + [error, loading, refetchPortfolioTokenOptions, sections], ) } @@ -51,7 +48,7 @@ function EmptyList({ onEmptyActionPress }: { onEmptyActionPress?: () => void }): const { data: ipAddressData, isLoading } = useFiatOnRampIpAddressQuery( // TODO(EXT-669): re-enable this once we have an onramp for the Extension. - isWeb ? skipToken : undefined + isWeb ? skipToken : undefined, ) const fiatOnRampEligible = Boolean(ipAddressData?.isBuyAllowed) @@ -72,9 +69,7 @@ function EmptyList({ onEmptyActionPress }: { onEmptyActionPress?: () => void }): : t('tokens.selector.empty.receive.title') } description={ - fiatOnRampEligible - ? t('tokens.selector.empty.buy.message') - : t('tokens.selector.empty.receive.message') + fiatOnRampEligible ? t('tokens.selector.empty.buy.message') : t('tokens.selector.empty.receive.message') } title={t('tokens.selector.empty.title')} onPress={onEmptyActionPress} @@ -95,10 +90,7 @@ function _TokenSelectorSendList({ onEmptyActionPress: () => void }): JSX.Element { const { data: sections, loading, error, refetch } = useTokenSectionsForSend(chainFilter) - const emptyElement = useMemo( - () => , - [onEmptyActionPress] - ) + const emptyElement = useMemo(() => , [onEmptyActionPress]) return ( { +function useTokenSectionsForSwapInput(chainFilter: UniverseChainId | null): GqlResult { const { t } = useTranslation() const activeAccountAddress = useActiveAccountAddressWithThrow() @@ -40,8 +28,7 @@ function useTokenSectionsForSwapInput( } = usePopularTokensOptions(activeAccountAddress, chainFilter ?? UniverseChainId.Mainnet) const error = - (!portfolioTokenOptions && portfolioTokenOptionsError) || - (!popularTokenOptions && popularTokenOptionsError) + (!portfolioTokenOptions && portfolioTokenOptionsError) || (!popularTokenOptions && popularTokenOptionsError) const loading = portfolioTokenOptionsLoading || popularTokenOptionsLoading @@ -55,17 +42,11 @@ function useTokenSectionsForSwapInput( return } - const popularMinusPortfolioTokens = tokenOptionDifference( - popularTokenOptions, - portfolioTokenOptions - ) + const popularMinusPortfolioTokens = tokenOptionDifference(popularTokenOptions, portfolioTokenOptions) return [ ...(getTokenOptionsSection(t('tokens.selector.section.yours'), portfolioTokenOptions) ?? []), - ...(getTokenOptionsSection( - t('tokens.selector.section.popular'), - popularMinusPortfolioTokens - ) ?? []), + ...(getTokenOptionsSection(t('tokens.selector.section.popular'), popularMinusPortfolioTokens) ?? []), ] satisfies TokenSection[] }, [loading, popularTokenOptions, portfolioTokenOptions, t]) @@ -76,7 +57,7 @@ function useTokenSectionsForSwapInput( error: error || undefined, refetch: refetchAll, }), - [error, loading, refetchAll, sections] + [error, loading, refetchAll, sections], ) } diff --git a/packages/wallet/src/components/TokenSelector/TokenSelectorSwapOutputList.tsx b/packages/wallet/src/components/TokenSelector/TokenSelectorSwapOutputList.tsx index b6fe138a7df..864df0b649e 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSelectorSwapOutputList.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenSelectorSwapOutputList.tsx @@ -1,6 +1,8 @@ import { memo, useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { isWeb } from 'ui/src' +import { OnSelectCurrency, TokenSelectorListSections } from 'uniswap/src/components/TokenSelector/types' +import { getTokenOptionsSection } from 'uniswap/src/components/TokenSelector/utils' import { GqlResult } from 'uniswap/src/data/types' import { UniverseChainId } from 'uniswap/src/types/chains' import { TokenSelectorList } from 'wallet/src/components/TokenSelector/TokenSelectorList' @@ -9,16 +11,9 @@ import { useFavoriteTokensOptions, usePopularTokensOptions, } from 'wallet/src/components/TokenSelector/hooks' -import { - OnSelectCurrency, - TokenSelectorListSections, -} from 'wallet/src/components/TokenSelector/types' -import { getTokenOptionsSection } from 'wallet/src/components/TokenSelector/utils' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' -function useTokenSectionsForSwapOutput( - chainFilter: UniverseChainId | null -): GqlResult { +function useTokenSectionsForSwapOutput(chainFilter: UniverseChainId | null): GqlResult { const { t } = useTranslation() const activeAccountAddress = useActiveAccountAddressWithThrow() @@ -50,8 +45,7 @@ function useTokenSectionsForSwapOutput( (!favoriteTokenOptions && favoriteTokenOptionsError) || (!commonTokenOptions && commonTokenOptionsError) - const loading = - popularTokenOptionsLoading || favoriteTokenOptionsLoading || commonTokenOptionsLoading + const loading = popularTokenOptionsLoading || favoriteTokenOptionsLoading || commonTokenOptionsLoading const refetchAll = useCallback(() => { refetchPopularTokenOptions?.() @@ -68,10 +62,7 @@ function useTokenSectionsForSwapOutput( // we draw the pills as a single item of a section list, so `data` is an array of Token[] { title: t('tokens.selector.section.suggested'), data: [commonTokenOptions ?? []] }, // TODO temporarily hiding favorites from extension until we add favorites functionality - ...(isWeb - ? [] - : getTokenOptionsSection(t('tokens.selector.section.favorite'), favoriteTokenOptions) ?? - []), + ...(isWeb ? [] : getTokenOptionsSection(t('tokens.selector.section.favorite'), favoriteTokenOptions) ?? []), ...(getTokenOptionsSection(t('tokens.selector.section.popular'), popularTokenOptions) ?? []), ] }, [commonTokenOptions, favoriteTokenOptions, loading, popularTokenOptions, t]) @@ -83,7 +74,7 @@ function useTokenSectionsForSwapOutput( error: error || undefined, refetch: refetchAll, }), - [error, loading, refetchAll, sections] + [error, loading, refetchAll, sections], ) } diff --git a/packages/wallet/src/components/TokenSelector/filter.test.ts b/packages/wallet/src/components/TokenSelector/filter.test.ts index 86cb707e873..1add6851217 100644 --- a/packages/wallet/src/components/TokenSelector/filter.test.ts +++ b/packages/wallet/src/components/TokenSelector/filter.test.ts @@ -1,10 +1,10 @@ import { Currency } from '@uniswap/sdk-core' +import { filter } from 'uniswap/src/components/TokenSelector/filter' +import { TokenOption } from 'uniswap/src/components/TokenSelector/types' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' -import { filter } from 'wallet/src/components/TokenSelector/filter' -import { TokenOption } from 'wallet/src/components/TokenSelector/types' +import { currencyId } from 'uniswap/src/utils/currencyId' import { DAI, DAI_ARBITRUM_ONE } from 'wallet/src/constants/tokens' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' -import { currencyId } from 'wallet/src/utils/currencyId' const ETH = NativeCurrency.onChain(UniverseChainId.Mainnet) diff --git a/packages/wallet/src/components/TokenSelector/hooks.test.ts b/packages/wallet/src/components/TokenSelector/hooks.test.ts index 93404c35ab7..5ad8f6ecec2 100644 --- a/packages/wallet/src/components/TokenSelector/hooks.test.ts +++ b/packages/wallet/src/components/TokenSelector/hooks.test.ts @@ -2,8 +2,12 @@ import { ApolloError } from '@apollo/client' import { toIncludeSameMembers } from 'jest-extended' import { PreloadedState } from 'redux' +import { createEmptyBalanceOption } from 'uniswap/src/components/TokenSelector/utils' +import { BRIDGED_BASE_ADDRESSES } from 'uniswap/src/constants/addresses' import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { useAllCommonBaseCurrencies, useCommonTokensOptions, @@ -15,9 +19,6 @@ import { usePortfolioBalancesForAddressById, usePortfolioTokenOptions, } from 'wallet/src/components/TokenSelector/hooks' -import { createEmptyBalanceOption } from 'wallet/src/components/TokenSelector/utils' -import { BRIDGED_BASE_ADDRESSES } from 'wallet/src/constants/addresses' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { tokenProjectToCurrencyInfos } from 'wallet/src/features/dataApi/utils' import { TokenSelectorFlow } from 'wallet/src/features/transactions/transfer/types' import { SharedState } from 'wallet/src/state/reducer' @@ -40,7 +41,6 @@ import { } from 'wallet/src/test/fixtures' import { act, createArray, renderHook, waitFor } from 'wallet/src/test/test-utils' import { portfolioBalancesById, queryResolvers } from 'wallet/src/test/utils' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' expect.extend({ toIncludeSameMembers }) diff --git a/packages/wallet/src/components/TokenSelector/hooks.ts b/packages/wallet/src/components/TokenSelector/hooks.ts index b9b60ef3d21..acbf946c496 100644 --- a/packages/wallet/src/components/TokenSelector/hooks.ts +++ b/packages/wallet/src/components/TokenSelector/hooks.ts @@ -1,14 +1,16 @@ import { useCallback, useEffect, useMemo, useState } from 'react' +import { filter } from 'uniswap/src/components/TokenSelector/filter' +import { TokenOption } from 'uniswap/src/components/TokenSelector/types' +import { createEmptyBalanceOption } from 'uniswap/src/components/TokenSelector/utils' +import { BRIDGED_BASE_ADDRESSES } from 'uniswap/src/constants/addresses' import { GqlResult } from 'uniswap/src/data/types' import { CurrencyInfo, PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { UniverseChainId } from 'uniswap/src/types/chains' -import { filter } from 'wallet/src/components/TokenSelector/filter' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' +import { buildNativeCurrencyId, buildWrappedNativeCurrencyId, currencyId } from 'uniswap/src/utils/currencyId' import { flowToModalName } from 'wallet/src/components/TokenSelector/flowToModalName' -import { TokenOption } from 'wallet/src/components/TokenSelector/types' -import { createEmptyBalanceOption } from 'wallet/src/components/TokenSelector/utils' -import { BRIDGED_BASE_ADDRESSES } from 'wallet/src/constants/addresses' import { DAI, USDC, USDT, WBTC } from 'wallet/src/constants/tokens' import { sortPortfolioBalances, @@ -21,12 +23,6 @@ import { usePersistedError } from 'wallet/src/features/dataApi/utils' import { selectFavoriteTokens } from 'wallet/src/features/favorites/selectors' import { TokenSelectorFlow } from 'wallet/src/features/transactions/transfer/types' import { useAppSelector } from 'wallet/src/state' -import { areAddressesEqual } from 'wallet/src/utils/addresses' -import { - buildNativeCurrencyId, - buildWrappedNativeCurrencyId, - currencyId, -} from 'wallet/src/utils/currencyId' // Use Mainnet base token addresses since TokenProjects query returns each token // on each network @@ -60,9 +56,7 @@ export function useCurrencies(currencyIds: string[]): GqlResult } const { address } = currencyInfo.currency - const bridgedAsset = BRIDGED_BASE_ADDRESSES.find((bridgedAddress) => - areAddressesEqual(bridgedAddress, address) - ) + const bridgedAsset = BRIDGED_BASE_ADDRESSES.find((bridgedAddress) => areAddressesEqual(bridgedAddress, address)) if (!bridgedAsset) { return true @@ -77,12 +71,7 @@ export function useCurrencies(currencyIds: string[]): GqlResult export function useFavoriteCurrencies(): GqlResult { const favoriteCurrencyIds = useAppSelector(selectFavoriteTokens) - const { - data: favoriteTokensOnAllChains, - loading, - error, - refetch, - } = useTokenProjects(favoriteCurrencyIds) + const { data: favoriteTokensOnAllChains, loading, error, refetch } = useTokenProjects(favoriteCurrencyIds) const persistedError = usePersistedError(loading, error) @@ -98,7 +87,7 @@ export function useFavoriteCurrencies(): GqlResult { .filter((token: CurrencyInfo | undefined): token is CurrencyInfo => { return !!token }), - [favoriteCurrencyIds, favoriteTokensOnAllChains] + [favoriteCurrencyIds, favoriteTokensOnAllChains], ) return { data: favoriteTokens, loading, error: persistedError, refetch } @@ -106,7 +95,7 @@ export function useFavoriteCurrencies(): GqlResult { export function useFilterCallbacks( chainId: UniverseChainId | null, - flow: TokenSelectorFlow + flow: TokenSelectorFlow, ): { chainFilter: UniverseChainId | null searchFilter: string | null @@ -129,17 +118,14 @@ export function useFilterCallbacks( modal: flowToModalName(flow), }) }, - [flow] + [flow], ) const onClearSearchFilter = useCallback(() => { setSearchFilter(null) }, []) - const onChangeText = useCallback( - (newSearchFilter: string) => setSearchFilter(newSearchFilter), - [setSearchFilter] - ) + const onChangeText = useCallback((newSearchFilter: string) => setSearchFilter(newSearchFilter), [setSearchFilter]) return { chainFilter, @@ -175,14 +161,13 @@ export function useCurrencyInfosToTokenOptions({ : currencyInfos return sortedCurrencyInfos.map( - (currencyInfo) => - portfolioBalancesById?.[currencyInfo.currencyId] ?? createEmptyBalanceOption(currencyInfo) + (currencyInfo) => portfolioBalancesById?.[currencyInfo.currencyId] ?? createEmptyBalanceOption(currencyInfo), ) }, [currencyInfos, portfolioBalancesById, sortAlphabetically]) } export function usePortfolioBalancesForAddressById( - address: Address + address: Address, ): GqlResult | undefined> { const { data: portfolioBalancesById, @@ -205,27 +190,19 @@ export function usePortfolioBalancesForAddressById( export function usePortfolioTokenOptions( address: Address, chainFilter: UniverseChainId | null, - searchFilter?: string + searchFilter?: string, ): GqlResult { - const { - data: portfolioBalancesById, - error, - refetch, - loading, - } = usePortfolioBalancesForAddressById(address) + const { data: portfolioBalancesById, error, refetch, loading } = usePortfolioBalancesForAddressById(address) const { shownTokens } = useTokenBalancesGroupedByVisibility({ balancesById: portfolioBalancesById, }) - const portfolioBalances = useMemo( - () => (shownTokens ? sortPortfolioBalances(shownTokens) : undefined), - [shownTokens] - ) + const portfolioBalances = useMemo(() => (shownTokens ? sortPortfolioBalances(shownTokens) : undefined), [shownTokens]) const filteredPortfolioBalances = useMemo( () => portfolioBalances && filter(portfolioBalances, chainFilter, searchFilter), - [chainFilter, portfolioBalances, searchFilter] + [chainFilter, portfolioBalances, searchFilter], ) return { @@ -238,7 +215,7 @@ export function usePortfolioTokenOptions( export function usePopularTokensOptions( address: Address, - chainFilter: UniverseChainId + chainFilter: UniverseChainId, ): GqlResult { const { data: portfolioBalancesById, @@ -265,9 +242,7 @@ export function usePopularTokensOptions( refetchPopularTokens?.() }, [portfolioBalancesByIdRefetch, refetchPopularTokens]) - const error = - (!portfolioBalancesById && portfolioBalancesByIdError) || - (!popularTokenOptions && popularTokensError) + const error = (!portfolioBalancesById && portfolioBalancesByIdError) || (!popularTokenOptions && popularTokensError) return { data: popularTokenOptions, @@ -279,7 +254,7 @@ export function usePopularTokensOptions( export function useCommonTokensOptions( address: Address, - chainFilter: UniverseChainId | null + chainFilter: UniverseChainId | null, ): GqlResult { const { data: portfolioBalancesById, @@ -306,12 +281,11 @@ export function useCommonTokensOptions( }, [portfolioBalancesByIdRefetch, refetchCommonBaseCurrencies]) const error = - (!portfolioBalancesById && portfolioBalancesByIdError) || - (!commonBaseCurrencies && commonBaseCurrenciesError) + (!portfolioBalancesById && portfolioBalancesByIdError) || (!commonBaseCurrencies && commonBaseCurrenciesError) const filteredCommonBaseTokenOptions = useMemo( () => commonBaseTokenOptions && filter(commonBaseTokenOptions, chainFilter), - [chainFilter, commonBaseTokenOptions] + [chainFilter, commonBaseTokenOptions], ) return { @@ -324,7 +298,7 @@ export function useCommonTokensOptions( export function useFavoriteTokensOptions( address: Address, - chainFilter: UniverseChainId | null + chainFilter: UniverseChainId | null, ): GqlResult { const { data: portfolioBalancesById, @@ -352,12 +326,11 @@ export function useFavoriteTokensOptions( }, [portfolioBalancesByIdRefetch, refetchFavoriteCurrencies]) const error = - (!portfolioBalancesById && portfolioBalancesByIdError) || - (!favoriteCurrencies && favoriteCurrenciesError) + (!portfolioBalancesById && portfolioBalancesByIdError) || (!favoriteCurrencies && favoriteCurrenciesError) const filteredFavoriteTokenOptions = useMemo( () => favoriteTokenOptions && filter(favoriteTokenOptions, chainFilter), - [chainFilter, favoriteTokenOptions] + [chainFilter, favoriteTokenOptions], ) return { diff --git a/packages/wallet/src/components/WalletConnect/DappIconPlaceholder.tsx b/packages/wallet/src/components/WalletConnect/DappIconPlaceholder.tsx index b2fee91ed53..ac094d0f9fe 100644 --- a/packages/wallet/src/components/WalletConnect/DappIconPlaceholder.tsx +++ b/packages/wallet/src/components/WalletConnect/DappIconPlaceholder.tsx @@ -1,13 +1,7 @@ import { Flex, Text } from 'ui/src' import { iconSizes } from 'ui/src/theme' -export function DappIconPlaceholder({ - name, - iconSize, -}: { - name?: string - iconSize: number -}): JSX.Element { +export function DappIconPlaceholder({ name, iconSize }: { name?: string; iconSize: number }): JSX.Element { return ( - = iconSizes.icon40 ? 'subheading1' : 'body2'}> + width={iconSize} + > + = iconSizes.icon40 ? 'subheading1' : 'body2'}> {name && name.length > 0 ? name.charAt(0) : ' '} diff --git a/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.test.tsx b/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.test.tsx index 9b4a78992ff..ef5f02ecc45 100644 --- a/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.test.tsx +++ b/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.test.tsx @@ -3,8 +3,6 @@ import { SAMPLE_SEED_ADDRESS_1 } from 'wallet/src/test/fixtures' import { render } from 'wallet/src/test/test-utils' it('renders wallet preview card', () => { - const tree = render( - null} /> - ) + const tree = render( null} />) expect(tree).toMatchSnapshot() }) diff --git a/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.tsx b/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.tsx index 66f9d551629..6713ff6ec2f 100644 --- a/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.tsx +++ b/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.tsx @@ -16,7 +16,8 @@ interface Props { hideSelectionCircle?: boolean } -export const ADDRESS_WRAPPER_HEIGHT = 36 +// Some preview cards do not have a name (no unitag), so we need to set a default height to keep their height consistent. +export const WALLET_PREVIEW_CARD_HEIGHT = 72 export default function WalletPreviewCard({ address, @@ -36,16 +37,17 @@ export default function WalletPreviewCard({ borderColor={selected ? '$surface3' : '$surface2'} borderRadius="$rounded20" borderWidth={1} - px="$spacing16" - py="$spacing16" + height={WALLET_PREVIEW_CARD_HEIGHT} + p="$spacing12" shadowColor={selected ? '$shadowColor' : '$transparent'} shadowOpacity={0.05} shadowRadius={selected ? '$spacing8' : '$none'} onPress={(): void => onSelect(address)} - {...rest}> - + {...rest} + > + - + {Boolean(balance) && ( {balanceFormatted} diff --git a/packages/wallet/src/components/WalletPreviewCard/__snapshots__/WalletPreviewCard.test.tsx.snap b/packages/wallet/src/components/WalletPreviewCard/__snapshots__/WalletPreviewCard.test.tsx.snap index cf4449be389..5405122d489 100644 --- a/packages/wallet/src/components/WalletPreviewCard/__snapshots__/WalletPreviewCard.test.tsx.snap +++ b/packages/wallet/src/components/WalletPreviewCard/__snapshots__/WalletPreviewCard.test.tsx.snap @@ -31,11 +31,12 @@ exports[`renders wallet preview card 1`] = ` "borderTopRightRadius": 20, "borderTopWidth": 1, "flexDirection": "column", + "height": 72, "opacity": 1, - "paddingBottom": 16, - "paddingLeft": 16, - "paddingRight": 16, - "paddingTop": 16, + "paddingBottom": 12, + "paddingLeft": 12, + "paddingRight": 12, + "paddingTop": 12, "shadowColor": "rgb(0,0,0)", "shadowOffset": { "height": 0, @@ -54,7 +55,9 @@ exports[`renders wallet preview card 1`] = ` diff --git a/packages/wallet/src/components/accounts/AccountDetails.test.tsx b/packages/wallet/src/components/accounts/AccountDetails.test.tsx index 5147574946c..e17e2bde1be 100644 --- a/packages/wallet/src/components/accounts/AccountDetails.test.tsx +++ b/packages/wallet/src/components/accounts/AccountDetails.test.tsx @@ -10,9 +10,7 @@ describe(AccountDetails, () => { }) it('renders without error with chevron', () => { - const tree = renderWithProviders( - - ) + const tree = renderWithProviders() expect(tree.toJSON()).toMatchSnapshot() }) diff --git a/packages/wallet/src/components/accounts/AccountDetails.tsx b/packages/wallet/src/components/accounts/AccountDetails.tsx index df40affbdba..398ec625fd9 100644 --- a/packages/wallet/src/components/accounts/AccountDetails.tsx +++ b/packages/wallet/src/components/accounts/AccountDetails.tsx @@ -1,7 +1,7 @@ import { ColorTokens, Flex, Text } from 'ui/src' import { RotatableChevron } from 'ui/src/components/icons' +import { shortenAddress } from 'uniswap/src/utils/addresses' import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' -import { shortenAddress } from 'wallet/src/utils/addresses' export function AccountDetails({ address, @@ -32,14 +32,7 @@ export function AccountDetails({ {shortenAddress(address)} - {chevron && ( - - )} + {chevron && } ) diff --git a/packages/wallet/src/components/accounts/AccountIcon.tsx b/packages/wallet/src/components/accounts/AccountIcon.tsx index 6fe10b1484f..7ddc7a3677d 100644 --- a/packages/wallet/src/components/accounts/AccountIcon.tsx +++ b/packages/wallet/src/components/accounts/AccountIcon.tsx @@ -44,7 +44,8 @@ export function AccountIcon({ borderRadius="$roundedFull" borderWidth={showBorder ? borderWidth : 0} position="relative" - testID="account-icon"> + testID="account-icon" + > {avatarUri ? ( + testID="account-icon/view-only-badge" + > )} @@ -86,15 +88,7 @@ export const UniconGradient = ({ color, size }: { color: string; size: number }) - + ) } diff --git a/packages/wallet/src/components/accounts/AddressDisplay.tsx b/packages/wallet/src/components/accounts/AddressDisplay.tsx index 040f5112301..5e8bff6b654 100644 --- a/packages/wallet/src/components/accounts/AddressDisplay.tsx +++ b/packages/wallet/src/components/accounts/AddressDisplay.tsx @@ -1,18 +1,11 @@ import { PropsWithChildren, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { FlexAlignType } from 'react-native' -import { - ColorTokens, - Flex, - HapticFeedback, - SpaceTokens, - Text, - TextProps, - TouchableArea, -} from 'ui/src' +import { ColorTokens, Flex, HapticFeedback, SpaceTokens, Text, TextProps, TouchableArea } from 'ui/src' import { CopySheets } from 'ui/src/components/icons' import { fonts } from 'ui/src/theme' import { ElementName } from 'uniswap/src/features/telemetry/constants' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText' import { pushNotification } from 'wallet/src/features/notifications/slice' @@ -20,7 +13,6 @@ import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/n import { useAvatar, useDisplayName } from 'wallet/src/features/wallet/hooks' import { DisplayNameType } from 'wallet/src/features/wallet/types' import { useAppDispatch } from 'wallet/src/state' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' import { setClipboard } from 'wallet/src/utils/clipboard' type AddressDisplayProps = { @@ -43,13 +35,7 @@ type AddressDisplayProps = { includeUnitagSuffix?: boolean textAlign?: FlexAlignType horizontalGap?: SpaceTokens - notificationsBadgeContainer?: ({ - children, - address, - }: { - children: JSX.Element - address: string - }) => JSX.Element + notificationsBadgeContainer?: ({ children, address }: { children: JSX.Element; address: string }) => JSX.Element gapBetweenLines?: SpaceTokens showViewOnlyLabel?: boolean showViewOnlyBadge?: boolean @@ -60,10 +46,7 @@ type CopyButtonWrapperProps = { backgroundColor?: string } -function CopyButtonWrapper({ - children, - onPress, -}: PropsWithChildren): JSX.Element { +function CopyButtonWrapper({ children, onPress }: PropsWithChildren): JSX.Element { if (onPress) { return ( @@ -116,8 +99,7 @@ export function AddressDisplay({ const displayName = useDisplayName(address, { includeUnitagSuffix, overrideDisplayName }) const { avatar } = useAvatar(address) - const showAddressAsSubtitle = - !hideAddressInSubtitle && displayName?.type !== DisplayNameType.Address + const showAddressAsSubtitle = !hideAddressInSubtitle && displayName?.type !== DisplayNameType.Address const onPressCopyAddress = async (): Promise => { if (!address) { @@ -129,15 +111,14 @@ export function AddressDisplay({ pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.Address, - }) + }), ) } // Extract sizes so copy icon can match font variants const mainSize = fonts[variant].fontSize const captionSize = fonts[captionVariant].fontSize - const itemAlignment = - textAlign || (!showAccountIcon || direction === 'column' ? 'center' : 'flex-start') + const itemAlignment = textAlign || (!showAccountIcon || direction === 'column' ? 'center' : 'flex-start') const icon = useMemo(() => { return ( @@ -169,12 +150,9 @@ export function AddressDisplay({ return ( {showAccountIcon && - (notificationsBadgeContainer - ? notificationsBadgeContainer({ children: icon, address }) - : icon)} + (notificationsBadgeContainer ? notificationsBadgeContainer({ children: icon, address }) : icon)} - + + py={showCopyWrapperButton ? '$spacing4' : '$none'} + > {sanitizeAddressText(shortenAddress(address))} diff --git a/packages/wallet/src/components/accounts/AnimatedUnitagDisplayName.tsx b/packages/wallet/src/components/accounts/AnimatedUnitagDisplayName.tsx index e406b3cc275..462181aec5d 100644 --- a/packages/wallet/src/components/accounts/AnimatedUnitagDisplayName.tsx +++ b/packages/wallet/src/components/accounts/AnimatedUnitagDisplayName.tsx @@ -3,12 +3,12 @@ import { LayoutChangeEvent } from 'react-native' import { AnimatePresence, Flex, HapticFeedback, Text, TouchableArea } from 'ui/src' import { CopyAlt, Unitag } from 'ui/src/components/icons' import { IconSizeTokens } from 'ui/src/theme' +import { sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types' import { UNITAG_SUFFIX } from 'wallet/src/features/unitags/constants' import { DisplayName, DisplayNameType } from 'wallet/src/features/wallet/types' import { useAppDispatch } from 'wallet/src/state' -import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' import { setClipboard } from 'wallet/src/utils/clipboard' type AnimatedUnitagDisplayNameProps = { @@ -46,31 +46,37 @@ export function AnimatedUnitagDisplayName({ pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.Address, - }) + }), ) } + const isLayoutReady = textWidth > 0 + return ( - + {displayName.name} + - - + {/* + We need to calculate this width in order to animate the suffix in and out, + but we don't want the initial render to show the suffix nor use the space and push other elements to the right. + So we set it to `position: absolute` on first render and then switch it to `relative` once we have the width. + */} + + {UNITAG_SUFFIX} + {isUnitag ? ( ) : null} + {address && ( diff --git a/packages/wallet/src/components/buttons/PasteButton.tsx b/packages/wallet/src/components/buttons/PasteButton.tsx index 5d518c711f9..9e8f503e7d9 100644 --- a/packages/wallet/src/components/buttons/PasteButton.tsx +++ b/packages/wallet/src/components/buttons/PasteButton.tsx @@ -46,7 +46,8 @@ export default function PasteButton({ borderRadius="$rounded12" p="$spacing8" onPress={onPressButton} - onPressIn={beforePress}> + onPressIn={beforePress} + > {label} diff --git a/packages/wallet/src/components/buttons/PlusMinusButton.tsx b/packages/wallet/src/components/buttons/PlusMinusButton.tsx index 4fe9e7f3787..98b61544623 100644 --- a/packages/wallet/src/components/buttons/PlusMinusButton.tsx +++ b/packages/wallet/src/components/buttons/PlusMinusButton.tsx @@ -26,7 +26,8 @@ export default function PlusMinusButton({ height={iconSizes.icon28} justifyContent="center" width={iconSizes.icon28} - onPress={(): void => onPress(type)}> + onPress={(): void => onPress(type)} + > {type === PlusMinusButtonType.Plus ? ( ) : ( diff --git a/packages/wallet/src/components/buttons/Switch.tsx b/packages/wallet/src/components/buttons/Switch.tsx index ef934e8e345..88ad5548ccf 100644 --- a/packages/wallet/src/components/buttons/Switch.tsx +++ b/packages/wallet/src/components/buttons/Switch.tsx @@ -30,7 +30,8 @@ export function Switch({ value, onValueChange, disabled }: SwitchProps): JSX.Ele false: disabledTrackColor, }, }} - onCheckedChange={disabled ? undefined : onValueChange}> + onCheckedChange={disabled ? undefined : onValueChange} + > ) @@ -56,7 +57,8 @@ export function WebSwitch({ value, onValueChange }: SwitchProps): JSX.Element { minHeight={TRACK_HEIGHT} minWidth={spacing.spacing60} p="$spacing4" - onCheckedChange={onValueChange}> + onCheckedChange={onValueChange} + > + {...rest} + > diff --git a/packages/wallet/src/components/gating/GatingOverrides.tsx b/packages/wallet/src/components/gating/GatingOverrides.tsx index 6a0ea3c6ed1..f3b31aca42d 100644 --- a/packages/wallet/src/components/gating/GatingOverrides.tsx +++ b/packages/wallet/src/components/gating/GatingOverrides.tsx @@ -2,11 +2,7 @@ import React from 'react' import { Accordion, Button, Flex, Separator, Text, isWeb } from 'ui/src' import { RotatableChevron } from 'ui/src/components/icons' import { Experiments } from 'uniswap/src/features/gating/experiments' -import { - FeatureFlags, - WALLET_FEATURE_FLAG_NAMES, - getFeatureFlagName, -} from 'uniswap/src/features/gating/flags' +import { FeatureFlags, WALLET_FEATURE_FLAG_NAMES, getFeatureFlagName } from 'uniswap/src/features/gating/flags' import { useFeatureFlagWithExposureLoggingDisabled } from 'uniswap/src/features/gating/hooks' import { Statsig } from 'uniswap/src/features/gating/sdk/statsig' import { Switch, WebSwitch } from 'wallet/src/components/buttons/Switch' @@ -60,7 +56,8 @@ export function GatingOverrides(): JSX.Element { Statsig.removeGateOverride() Statsig.removeConfigOverride() Statsig.removeLayerOverride() - }}> + }} + > Clear all gating overrides @@ -116,7 +113,8 @@ function ExperimentRow({ experiment }: { experiment: Experiments }): JSX.Element alignItems="center" gap="$spacing16" justifyContent="space-between" - paddingStart="$spacing16"> + paddingStart="$spacing16" + > {/* TODO(WEB-4164): implement experiment groups overrides */} diff --git a/packages/wallet/src/components/icons/Arrow.tsx b/packages/wallet/src/components/icons/Arrow.tsx index e9417007baa..a020e91236a 100644 --- a/packages/wallet/src/components/icons/Arrow.tsx +++ b/packages/wallet/src/components/icons/Arrow.tsx @@ -33,13 +33,7 @@ export function _Arrow({ size = 24, color = '#000000', direction = 'e' }: Props) } return ( - + ) } diff --git a/packages/wallet/src/components/icons/PlusCircle.tsx b/packages/wallet/src/components/icons/PlusCircle.tsx index 2437f514cd3..8fa293e4542 100644 --- a/packages/wallet/src/components/icons/PlusCircle.tsx +++ b/packages/wallet/src/components/icons/PlusCircle.tsx @@ -17,7 +17,8 @@ export function PlusCircle(): JSX.Element { shadowColor={isDarkMode ? '$shadowColor' : '$surface3'} shadowOffset={{ width: 0, height: 0 }} shadowRadius={10} - width={iconSizes.icon40}> + width={iconSizes.icon40} + > ) diff --git a/packages/wallet/src/components/input/AmountInput.test.tsx b/packages/wallet/src/components/input/AmountInput.test.tsx index be482b595a9..b9a0cf737e3 100644 --- a/packages/wallet/src/components/input/AmountInput.test.tsx +++ b/packages/wallet/src/components/input/AmountInput.test.tsx @@ -80,7 +80,7 @@ describe(parseValue, () => { parseValue({ value: ' 1234 ', ...defaultParams, - }) + }), ).toBe('1234') }) @@ -89,7 +89,7 @@ describe(parseValue, () => { parseValue({ value: '1,234.56', ...defaultParams, - }) + }), ).toBe('1234.56') expect( @@ -98,7 +98,7 @@ describe(parseValue, () => { ...defaultParams, decimalSeparator: ',', groupingSeparator: '.', - }) + }), ).toBe('1234.56') expect( @@ -107,7 +107,7 @@ describe(parseValue, () => { ...defaultParams, decimalSeparator: '.', groupingSeparator: ' ', - }) + }), ).toBe('1234.56') }) @@ -116,7 +116,7 @@ describe(parseValue, () => { parseValue({ value: ' example $1,234,567.123456789 example ', ...defaultParams, - }) + }), ).toBe('1234567.123456789') }) @@ -126,7 +126,7 @@ describe(parseValue, () => { value: '1,234.123456789123456789 WBTC', ...defaultParams, maxDecimals: 8, - }) + }), ).toBe('1234.12345678') expect( @@ -134,7 +134,7 @@ describe(parseValue, () => { value: '1,234.123456789123456789123456789 ETH', ...defaultParams, maxDecimals: 18, - }) + }), ).toBe('1234.123456789123456789') }) }) diff --git a/packages/wallet/src/components/input/AmountInput.tsx b/packages/wallet/src/components/input/AmountInput.tsx index d1a955ac0ac..31aa7b825c1 100644 --- a/packages/wallet/src/components/input/AmountInput.tsx +++ b/packages/wallet/src/components/input/AmountInput.tsx @@ -33,7 +33,7 @@ export function replaceSeparators({ if (groupingSeparator && groupingOverride != null) { outputParts = outputParts.map((part) => // eslint-disable-next-line security/detect-non-literal-regexp - part.replace(new RegExp(`\\${groupingSeparator}`, 'g'), groupingOverride) + part.replace(new RegExp(`\\${groupingSeparator}`, 'g'), groupingOverride), ) } return outputParts.join(decimalOverride) @@ -97,7 +97,7 @@ export const AmountInput = forwardRef(function _AmountIn fiatCurrencyInfo, ...rest }, - ref + ref, ) { const appFiatCurrencyInfo = useAppFiatCurrencyInfo() const targetFiatCurrencyInfo = fiatCurrencyInfo || appFiatCurrencyInfo @@ -123,7 +123,7 @@ export const AmountInput = forwardRef(function _AmountIn showSoftInputOnFocus, nativeKeyboardDecimalSeparator, maxDecimals, - }) + }), ) }, [ @@ -133,7 +133,7 @@ export const AmountInput = forwardRef(function _AmountIn nativeKeyboardDecimalSeparator, onChangeText, showSoftInputOnFocus, - ] + ], ) const formattedValue = replaceSeparators({ @@ -155,7 +155,7 @@ export const AmountInput = forwardRef(function _AmountIn ...rest, ...(adjustWidthToContent ? { width } : {}), }), - [ref, value, dimTextColor, formattedValue, handleChange, rest, width, adjustWidthToContent] + [ref, value, dimTextColor, formattedValue, handleChange, rest, width, adjustWidthToContent], ) useEffect(() => { const subscription = AppState.addEventListener('change', (nextAppState) => { @@ -199,10 +199,11 @@ export const AmountInput = forwardRef(function _AmountIn setWidth( Math.min( e.nativeEvent.layout.width, - typeof textInputProps.maxWidth === 'number' ? textInputProps.maxWidth : +Infinity - ) + typeof textInputProps.maxWidth === 'number' ? textInputProps.maxWidth : +Infinity, + ), ) - }> + } + > {value || textInputProps.placeholder} {textInputElement} @@ -213,8 +214,9 @@ export const AmountInput = forwardRef(function _AmountIn return textInputElement }) -const TextInputWithNativeKeyboard = forwardRef( - function _TextInputWithNativeKeyboard(props: TextInputProps, ref) { - return - } -) +const TextInputWithNativeKeyboard = forwardRef(function _TextInputWithNativeKeyboard( + props: TextInputProps, + ref, +) { + return +}) diff --git a/packages/wallet/src/components/input/MaxAmountButton.tsx b/packages/wallet/src/components/input/MaxAmountButton.tsx index c9da97d3411..4eabd6e06fd 100644 --- a/packages/wallet/src/components/input/MaxAmountButton.tsx +++ b/packages/wallet/src/components/input/MaxAmountButton.tsx @@ -28,13 +28,12 @@ export function MaxAmountButton({ // Disable max button if max already set or when balance is not sufficient const disableMaxButton = - !maxInputAmount || - !maxInputAmount.greaterThan(0) || - currencyAmount?.toExact() === maxInputAmount.toExact() + !maxInputAmount || !maxInputAmount.greaterThan(0) || currencyAmount?.toExact() === maxInputAmount.toExact() const onPress = (event: GestureResponderEvent): void => { + event.stopPropagation() + if (!disableMaxButton) { - event.stopPropagation() onSetMax(maxInputAmount.toExact()) } } @@ -42,23 +41,20 @@ export function MaxAmountButton({ return ( + element={currencyField === CurrencyField.INPUT ? ElementName.SetMaxInput : ElementName.SetMaxOutput} + > - + testID={currencyField === CurrencyField.INPUT ? ElementName.SetMaxInput : ElementName.SetMaxOutput} + onPress={onPress} + > + {t('swap.button.max')} diff --git a/packages/wallet/src/components/input/RecipientInputPanel.tsx b/packages/wallet/src/components/input/RecipientInputPanel.tsx index 7a254b9422c..71c22aad848 100644 --- a/packages/wallet/src/components/input/RecipientInputPanel.tsx +++ b/packages/wallet/src/components/input/RecipientInputPanel.tsx @@ -24,7 +24,8 @@ export function RecipientInputPanel({ px="$spacing32" py="$spacing16" testID={ElementName.SelectRecipient} - onPress={onToggleShowRecipientSelector}> + onPress={onToggleShowRecipientSelector} + > diff --git a/apps/mobile/src/components/gradients/LandingBackground.mock.tsx b/packages/wallet/src/components/landing/LandingBackground.mock.tsx similarity index 100% rename from apps/mobile/src/components/gradients/LandingBackground.mock.tsx rename to packages/wallet/src/components/landing/LandingBackground.mock.tsx diff --git a/apps/mobile/src/components/gradients/LandingBackground.tsx b/packages/wallet/src/components/landing/LandingBackground.tsx similarity index 60% rename from apps/mobile/src/components/gradients/LandingBackground.tsx rename to packages/wallet/src/components/landing/LandingBackground.tsx index 504b542ae14..363e408c878 100644 --- a/apps/mobile/src/components/gradients/LandingBackground.tsx +++ b/packages/wallet/src/components/landing/LandingBackground.tsx @@ -1,5 +1,5 @@ -import { useFocusEffect } from '@react-navigation/core' -import React, { ReactElement, useCallback, useEffect, useState } from 'react' +import { EventConsumer, EventMapBase } from '@react-navigation/core' +import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react' import { LayoutChangeEvent, Platform } from 'react-native' import Animated, { Easing, @@ -12,72 +12,38 @@ import Animated, { withSpring, withTiming, } from 'react-native-reanimated' -import { Circle, Defs, LinearGradient, Stop, Svg } from 'react-native-svg' -import { useAppStackNavigation } from 'src/app/navigation/types' -import { OpenseaElement } from 'src/features/unitags/ConfirmationElements' +import { Circle, Defs, Svg } from 'react-native-svg' +import { Flex, FlexProps, Image, isWeb, useIsDarkMode } from 'ui/src' +import { Jiggly } from 'ui/src/animations' +import { ONBOARDING_LANDING_DARK, ONBOARDING_LANDING_LIGHT, UNISWAP_LOGO } from 'ui/src/assets' +import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' +import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' +import { imageSizes } from 'ui/src/theme' +import { isAndroid } from 'utilities/src/platform' +import { ONE_SECOND_MS } from 'utilities/src/time/time' +import { useTimeout } from 'utilities/src/time/timing' import { BuyElement, FroggyElement, HeartElement, + OpenseaElement, PolygonElement, ReceiveUSDCElement, SendElement, SwapElement, UniconElement, -} from 'src/screens/Onboarding/OnboardingElements' -import { Flex, Image, useIsDarkMode } from 'ui/src' -import { Jiggly } from 'ui/src/animations' -import { ONBOARDING_LANDING_DARK, ONBOARDING_LANDING_LIGHT, UNISWAP_APP_ICON } from 'ui/src/assets' -import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' -import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' -import { imageSizes } from 'ui/src/theme' -import { isAndroid } from 'utilities/src/platform' -import { ONE_SECOND_MS } from 'utilities/src/time/time' -import { useTimeout } from 'utilities/src/time/timing' +} from 'wallet/src/components/landing/elements' +import { InnerCircleGradient, OuterCircleGradient } from 'wallet/src/components/landing/landingBackgroundGradients' import { Language } from 'wallet/src/features/language/constants' import { useCurrentLanguage } from 'wallet/src/features/language/hooks' -const INNER_CIRCLE_SIZE = 120 -const OUTER_CIRCLE_SIZE = 215 - -const INNER_PROPS = { radius: INNER_CIRCLE_SIZE, speed: -1, isInner: true } -const OUTER_PROPS = { radius: OUTER_CIRCLE_SIZE, speed: 1 } +const DEFAULT_INNER_CIRCLE_SIZE = 120 +const DEFAULT_OUTER_CIRCLE_SIZE = 215 const ROTATION_DURATION = 150000 const ACCELERATION_DURATION = ROTATION_DURATION / 50 -const ANIMATED_ELEMENTS_INNER = [ - { - element: , - coordinates: { deg: 3.2, ...INNER_PROPS }, - }, - { - element: , - coordinates: { deg: 2.0, ...INNER_PROPS }, - }, - { - element: , - coordinates: { deg: 0.3, ...INNER_PROPS }, - }, - { - element: , - coordinates: { deg: 5.5, ...INNER_PROPS }, - }, -] -const ANIMATED_ELEMENTS_OUTER = [ - { - element: , - coordinates: { radius: OUTER_CIRCLE_SIZE + 40, deg: 1, speed: 1, flatteningY: 0.85 }, - }, - { element: , coordinates: { deg: 2.2, ...OUTER_PROPS } }, - { element: , coordinates: { deg: 3.8, ...OUTER_PROPS } }, - { element: , coordinates: { deg: 4.8, ...OUTER_PROPS } }, - { - element: , - coordinates: { deg: 5.7, ...OUTER_PROPS }, - }, -] - +const LOGO_SIZE_WEB = 80 const LOGO_SCALE_DELAY = 0.5 * ONE_SECOND_MS const LOGO_SCALE_DURATION = 0.7 * ONE_SECOND_MS const ANIMATED_ELEMENTS_DELAY = LOGO_SCALE_DELAY + LOGO_SCALE_DURATION - 750 @@ -86,7 +52,15 @@ const OUTER_CIRCLE_SHOW_DELAY = 0.5 * ONE_SECOND_MS export const LANDING_ANIMATION_DURATION = ANIMATED_ELEMENTS_DELAY -const OnboardingAnimation = (): JSX.Element => { +const OnboardingAnimation = ({ + elementsStyle, + innerCircleSize, + outerCircleSize, +}: { + elementsStyle: FlexProps['style'] + innerCircleSize: number + outerCircleSize: number +}): JSX.Element => { const [boxWidth, setBoxWidth] = useState(0) const [showAnimatedElements, setShowAnimatedElements] = useState(false) @@ -95,10 +69,7 @@ const OnboardingAnimation = (): JSX.Element => { }, []) const uniswapLogoScale = useSharedValue(1.5) - const animatedStyle = useAnimatedStyle( - () => ({ transform: [{ scale: uniswapLogoScale.value }] }), - [uniswapLogoScale] - ) + const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: uniswapLogoScale.value }] }), [uniswapLogoScale]) useEffect(() => { uniswapLogoScale.value = withDelay( @@ -106,7 +77,7 @@ const OnboardingAnimation = (): JSX.Element => { withTiming(1, { duration: LOGO_SCALE_DURATION, easing: Easing.elastic(1.1), - }) + }), ) }, [uniswapLogoScale]) @@ -115,15 +86,19 @@ const OnboardingAnimation = (): JSX.Element => { }, ANIMATED_ELEMENTS_DELAY) return ( - - {showAnimatedElements ? : null} + + {showAnimatedElements ? ( + + + + ) : null} @@ -133,7 +108,57 @@ const OnboardingAnimation = (): JSX.Element => { const INITIAL_ANIMATION_LENGTH = 0.1 -const AnimatedElements = ({ width }: { width: number }): JSX.Element | null => { +const AnimatedElements = ({ + width, + innerCircleSize, + outerCircleSize, +}: { + width: number + innerCircleSize: number + outerCircleSize: number +}): JSX.Element | null => { + const isDarkMode = useIsDarkMode() + + const animatedElementsInner = useMemo(() => { + const innerProps = { radius: innerCircleSize, speed: -1, isInner: true } + + return [ + { + element: , + coordinates: { deg: 3.2, ...innerProps }, + }, + { + element: , + coordinates: { deg: 2.0, ...innerProps }, + }, + { + element: , + coordinates: { deg: 0.3, ...innerProps }, + }, + { + element: , + coordinates: { deg: 5.5, ...innerProps }, + }, + ] + }, [innerCircleSize]) + + const animatedElementsOuter = useMemo(() => { + const outerProps = { radius: outerCircleSize, speed: 1 } + return [ + { + element: , + coordinates: { radius: outerCircleSize + 40, deg: 1, speed: 1, flatteningY: 0.85 }, + }, + { element: , coordinates: { deg: 2.2, ...outerProps } }, + { element: , coordinates: { deg: 3.8, ...outerProps } }, + { element: , coordinates: { deg: 4.8, ...outerProps } }, + { + element: , + coordinates: { deg: 5.7, ...outerProps }, + }, + ] + }, [outerCircleSize]) + const rotation = useSharedValue(0) const innerAnimation = useSharedValue(0) const outerAnimation = useSharedValue(0) @@ -151,69 +176,61 @@ const AnimatedElements = ({ width }: { width: number }): JSX.Element | null => { duration: ROTATION_DURATION, easing: Easing.linear, }), - -1 - ) - ) + -1, + ), + ), ) - innerAnimation.value = withDelay(INNER_CIRCLE_SHOW_DELAY, withSpring(1)) - outerAnimation.value = withDelay(OUTER_CIRCLE_SHOW_DELAY, withSpring(1)) + innerAnimation.value = withDelay(INNER_CIRCLE_SHOW_DELAY, withSpring(0.8)) + outerAnimation.value = withDelay(OUTER_CIRCLE_SHOW_DELAY, withSpring(0.8)) }, [innerAnimation, outerAnimation, rotation]) const innerCircleStyle = useAnimatedStyle(() => { return { opacity: innerAnimation.value, } - }) + }, [innerAnimation]) const outerCircleStyle = useAnimatedStyle(() => { return { opacity: outerAnimation.value, } - }) + }, [outerAnimation]) return ( - - - - - - + - - - - - - + - {ANIMATED_ELEMENTS_INNER.map(({ element, coordinates }, index) => ( + {animatedElementsInner.map(({ element, coordinates }, index) => ( { rotation={rotation} /> ))} - {ANIMATED_ELEMENTS_OUTER.map(({ element, coordinates }, index) => ( + {animatedElementsOuter.map(({ element, coordinates }, index) => ( @@ -286,18 +302,35 @@ const RotateElement = ({ ) } -export const LandingBackground = (): JSX.Element | null => { - const navigation = useAppStackNavigation() +export const LandingBackground = ({ + navigationEventConsumer, + elementsStyle, + innerCircleSize = DEFAULT_INNER_CIRCLE_SIZE, + outerCircleSize = DEFAULT_OUTER_CIRCLE_SIZE, +}: { + navigationEventConsumer?: EventConsumer + elementsStyle?: FlexProps['style'] + innerCircleSize?: number + outerCircleSize?: number +}): JSX.Element | null => { const [blurred, setBlurred] = useState(false) const [hideAnimation, setHideAnimation] = useState(false) const language = useCurrentLanguage() useEffect(() => { - return navigation.addListener('blur', () => { + return navigationEventConsumer?.addListener('blur', () => { // set this flag on blur (when navigating to another screen) setBlurred(true) }) - }, [navigation]) + }, [navigationEventConsumer]) + + useEffect(() => { + return navigationEventConsumer?.addListener('focus', () => { + // reset animation when focusing on this screen again + setBlurred(false) + setHideAnimation(false) + }) + }, [navigationEventConsumer]) // callback to turn off the animation (so that we can turn it back // on on focus) @@ -312,12 +345,6 @@ export const LandingBackground = (): JSX.Element | null => { // transition animation happens useTimeout(turnAnimationOff, 500) - // reset animation when focusing on this screen again - useFocusEffect(() => { - setBlurred(false) - setHideAnimation(false) - }) - if (hideAnimation) { // resets the animation to restart when the screen is mounted again (eg. going back) return null @@ -333,7 +360,13 @@ export const LandingBackground = (): JSX.Element | null => { return } - return + return ( + + ) } const OnboardingStaticImage = (): JSX.Element => { @@ -341,11 +374,7 @@ const OnboardingStaticImage = (): JSX.Element => { const { fullHeight, fullWidth } = useDeviceDimensions() return ( ) diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/BuyElement.tsx b/packages/wallet/src/components/landing/elements/BuyElement.tsx similarity index 93% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/BuyElement.tsx rename to packages/wallet/src/components/landing/elements/BuyElement.tsx index 3761ac74dcb..3a4342510ed 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/BuyElement.tsx +++ b/packages/wallet/src/components/landing/elements/BuyElement.tsx @@ -15,7 +15,8 @@ export const BuyElement = (): JSX.Element => { px="$spacing12" py="$spacing8" style={{ backgroundColor: opacify(20, colors.orange200) }} - transform={[{ rotateZ: '-1deg' }]}> + transform={[{ rotateZ: '-1deg' }]} + > {t('common.button.buy')} diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/ENSElement.tsx b/packages/wallet/src/components/landing/elements/ENSElement.tsx similarity index 64% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/ENSElement.tsx rename to packages/wallet/src/components/landing/elements/ENSElement.tsx index 145c3079922..bae86afdaa8 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/ENSElement.tsx +++ b/packages/wallet/src/components/landing/elements/ENSElement.tsx @@ -8,13 +8,9 @@ export const ENSElement = (): JSX.Element => { borderRadius="$rounded12" p="$spacing12" style={{ backgroundColor: opacify(20, colors.blue300) }} - transform={[{ rotateZ: '8deg' }]}> - + transform={[{ rotateZ: '8deg' }]} + > + ) } diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/EmojiElement.tsx b/packages/wallet/src/components/landing/elements/EmojiElement.tsx similarity index 90% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/EmojiElement.tsx rename to packages/wallet/src/components/landing/elements/EmojiElement.tsx index 95933ab4fbc..de93a0776d1 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/EmojiElement.tsx +++ b/packages/wallet/src/components/landing/elements/EmojiElement.tsx @@ -7,7 +7,8 @@ export const EmojiElement = ({ emoji }: { emoji: string }): JSX.Element => { borderRadius="$roundedFull" p="$spacing8" style={{ backgroundColor: opacify(20, colors.yellow200) }} - transform={[{ rotateZ: '5deg' }]}> + transform={[{ rotateZ: '5deg' }]} + > {emoji} diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/FroggyElement.tsx b/packages/wallet/src/components/landing/elements/FroggyElement.tsx similarity index 66% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/FroggyElement.tsx rename to packages/wallet/src/components/landing/elements/FroggyElement.tsx index a704aff2eaa..2c7148d28ed 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/FroggyElement.tsx +++ b/packages/wallet/src/components/landing/elements/FroggyElement.tsx @@ -5,12 +5,7 @@ import { imageSizes } from 'ui/src/theme' export const FroggyElement = (): JSX.Element => { return ( - + ) } diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/HeartElement.tsx b/packages/wallet/src/components/landing/elements/HeartElement.tsx similarity index 89% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/HeartElement.tsx rename to packages/wallet/src/components/landing/elements/HeartElement.tsx index 1cdb0eb29cd..53a037cbf59 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/HeartElement.tsx +++ b/packages/wallet/src/components/landing/elements/HeartElement.tsx @@ -8,7 +8,8 @@ export const HeartElement = (): JSX.Element => { borderRadius="$rounded12" p="$spacing12" style={{ backgroundColor: opacify(20, colors.red200) }} - transform={[{ rotateZ: '-20deg' }]}> + transform={[{ rotateZ: '-20deg' }]} + > ) diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/OpenseaElement.tsx b/packages/wallet/src/components/landing/elements/OpenseaElement.tsx similarity index 50% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/OpenseaElement.tsx rename to packages/wallet/src/components/landing/elements/OpenseaElement.tsx index 28d8d9b54d4..e000a717df1 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/OpenseaElement.tsx +++ b/packages/wallet/src/components/landing/elements/OpenseaElement.tsx @@ -4,13 +4,8 @@ import { imageSizes } from 'ui/src/theme' export const OpenseaElement = (): JSX.Element => { return ( - - + + ) } diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/PolygonElement.tsx b/packages/wallet/src/components/landing/elements/PolygonElement.tsx similarity index 89% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/PolygonElement.tsx rename to packages/wallet/src/components/landing/elements/PolygonElement.tsx index 0c4f021b222..52ca70b754b 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/PolygonElement.tsx +++ b/packages/wallet/src/components/landing/elements/PolygonElement.tsx @@ -8,7 +8,8 @@ export const PolygonElement = (): JSX.Element => { borderRadius="$rounded12" p="$spacing12" style={{ backgroundColor: opacify(20, colors.violet300) }} - transform={[{ rotateZ: '-20deg' }]}> + transform={[{ rotateZ: '-20deg' }]} + > ) diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/ReceiveUSDCElement.tsx b/packages/wallet/src/components/landing/elements/ReceiveUSDCElement.tsx similarity index 73% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/ReceiveUSDCElement.tsx rename to packages/wallet/src/components/landing/elements/ReceiveUSDCElement.tsx index 18ea6b39973..1a3079f327d 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/ReceiveUSDCElement.tsx +++ b/packages/wallet/src/components/landing/elements/ReceiveUSDCElement.tsx @@ -12,16 +12,12 @@ export const ReceiveUSDCElement = (): JSX.Element => { px="$spacing12" py="$spacing8" style={{ backgroundColor: opacify(20, colors.blue300) }} - transform={[{ rotateZ: '-1deg' }]}> + transform={[{ rotateZ: '-1deg' }]} + > +100 - + ) } diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/SendElement.tsx b/packages/wallet/src/components/landing/elements/SendElement.tsx similarity index 89% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/SendElement.tsx rename to packages/wallet/src/components/landing/elements/SendElement.tsx index 198fb1ff69e..8a7fb9a61f9 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/SendElement.tsx +++ b/packages/wallet/src/components/landing/elements/SendElement.tsx @@ -8,7 +8,8 @@ export const SendElement = (): JSX.Element => { borderRadius="$rounded12" p="$spacing8" style={{ backgroundColor: opacify(20, colors.green300) }} - transform={[{ rotateZ: '-4deg' }]}> + transform={[{ rotateZ: '-4deg' }]} + > ) diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/SwapElement.tsx b/packages/wallet/src/components/landing/elements/SwapElement.tsx similarity index 68% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/SwapElement.tsx rename to packages/wallet/src/components/landing/elements/SwapElement.tsx index 36c925d02b0..1079111d551 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/SwapElement.tsx +++ b/packages/wallet/src/components/landing/elements/SwapElement.tsx @@ -6,26 +6,16 @@ import { colors, iconSizes, imageSizes, opacify } from 'ui/src/theme' export const SwapElement = (): JSX.Element => { const sporeColors = useSporeColors() return ( - + - + style={{ backgroundColor: opacify(20, colors.blue200) }} + > + ETH @@ -37,13 +27,9 @@ export const SwapElement = (): JSX.Element => { borderRadius="$roundedFull" gap="$spacing8" p="$spacing8" - style={{ backgroundColor: opacify(20, colors.yellow100) }}> - + style={{ backgroundColor: opacify(20, colors.yellow100) }} + > + DAI diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/TextElement.tsx b/packages/wallet/src/components/landing/elements/TextElement.tsx similarity index 100% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/TextElement.tsx rename to packages/wallet/src/components/landing/elements/TextElement.tsx diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/UniconElement.tsx b/packages/wallet/src/components/landing/elements/UniconElement.tsx similarity index 90% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/UniconElement.tsx rename to packages/wallet/src/components/landing/elements/UniconElement.tsx index 0d749a825a1..46f4be7f056 100644 --- a/apps/mobile/src/screens/Onboarding/OnboardingElements/UniconElement.tsx +++ b/packages/wallet/src/components/landing/elements/UniconElement.tsx @@ -8,7 +8,8 @@ export const UniconElement = (): JSX.Element => { borderRadius="$roundedFull" p="$spacing8" style={{ backgroundColor: opacify(20, colors.violet400) }} - transform={[{ rotateZ: '-4deg' }]}> + transform={[{ rotateZ: '-4deg' }]} + > ) diff --git a/apps/mobile/src/screens/Onboarding/OnboardingElements/index.tsx b/packages/wallet/src/components/landing/elements/index.tsx similarity index 100% rename from apps/mobile/src/screens/Onboarding/OnboardingElements/index.tsx rename to packages/wallet/src/components/landing/elements/index.tsx diff --git a/packages/wallet/src/components/landing/landingBackgroundGradients.tsx b/packages/wallet/src/components/landing/landingBackgroundGradients.tsx new file mode 100644 index 00000000000..56b7a24261e --- /dev/null +++ b/packages/wallet/src/components/landing/landingBackgroundGradients.tsx @@ -0,0 +1,42 @@ +import { ReactElement } from 'react' +import { LinearGradient, Stop } from 'react-native-svg' +import { useIsDarkMode } from 'ui/src' + +// Exported from figma +export function InnerCircleGradient({ id }: { id: string }): ReactElement { + const isDarkMode = useIsDarkMode() + + return isDarkMode ? ( + + + + + + ) : ( + + + + + + + ) +} + +export function OuterCircleGradient({ id }: { id: string }): ReactElement { + const isDarkMode = useIsDarkMode() + + return isDarkMode ? ( + + + + + + ) : ( + + + + + + + ) +} diff --git a/packages/wallet/src/components/legacy/CurrencyInputPanelLegacy.tsx b/packages/wallet/src/components/legacy/CurrencyInputPanelLegacy.tsx index defe9f67a9f..9eb1daf52e3 100644 --- a/packages/wallet/src/components/legacy/CurrencyInputPanelLegacy.tsx +++ b/packages/wallet/src/components/legacy/CurrencyInputPanelLegacy.tsx @@ -1,12 +1,7 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { memo, useCallback, useEffect, useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { - NativeSyntheticEvent, - TextInput, - TextInputProps, - TextInputSelectionChangeEventData, -} from 'react-native' +import { NativeSyntheticEvent, TextInput, TextInputProps, TextInputSelectionChangeEventData } from 'react-native' import { Flex, FlexProps, SpaceTokens, Text, TouchableArea } from 'ui/src' import { fonts } from 'ui/src/theme' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' @@ -61,7 +56,7 @@ interface DynamicSwapPanelPaddingValues { const getSwapPanelPaddingValues = ( isOutputBox: boolean, - hasCurrencyValue: boolean + hasCurrencyValue: boolean, ): { outerPadding: DynamicSwapPanelPaddingValues; innerPadding: DynamicSwapPanelPaddingValues } => { const outerPadding: DynamicSwapPanelPaddingValues = hasCurrencyValue ? { @@ -119,16 +114,11 @@ export function _CurrencyInputPanel(props: CurrentInputPanelProps): JSX.Element const inputRef = useRef(null) const { convertFiatAmountFormatted, formatCurrencyAmount } = useLocalizationContext() - const insufficientBalanceWarning = warnings.find( - (warning) => warning.type === WarningLabel.InsufficientFunds - ) + const insufficientBalanceWarning = warnings.find((warning) => warning.type === WarningLabel.InsufficientFunds) const showInsufficientBalanceWarning = insufficientBalanceWarning && !isOutput - const formattedFiatValue = convertFiatAmountFormatted( - usdValue?.toExact(), - NumberType.FiatTokenQuantity - ) + const formattedFiatValue = convertFiatAmountFormatted(usdValue?.toExact(), NumberType.FiatTokenQuantity) const formattedCurrencyAmount = currencyAmount ? formatCurrencyAmount({ value: currencyAmount, type: NumberType.TokenTx }) : '' @@ -148,7 +138,7 @@ export function _CurrencyInputPanel(props: CurrentInputPanelProps): JSX.Element const { onLayout, fontSize, onSetFontSize } = useDynamicFontSizing( MAX_CHAR_PIXEL_WIDTH, MAX_INPUT_FONT_SIZE, - MIN_INPUT_FONT_SIZE + MIN_INPUT_FONT_SIZE, ) // Handle native numpad keyboard input @@ -157,7 +147,7 @@ export function _CurrencyInputPanel(props: CurrentInputPanelProps): JSX.Element onSetFontSize(newAmount) onSetExactAmount(newAmount) }, - [onSetFontSize, onSetExactAmount] + [onSetFontSize, onSetExactAmount], ) // This is needed to ensure that the text resizes when modified from outside the component (e.g. custom numpad) @@ -176,7 +166,7 @@ export function _CurrencyInputPanel(props: CurrentInputPanelProps): JSX.Element onSetMax(amount) onChangeText(amount) }, - [onChangeText, onSetMax] + [onChangeText, onSetMax], ) const onSelectionChange = useCallback( @@ -184,14 +174,13 @@ export function _CurrencyInputPanel(props: CurrentInputPanelProps): JSX.Element nativeEvent: { selection: { start, end }, }, - }: NativeSyntheticEvent) => - selectionChange && selectionChange(start, end), - [selectionChange] + }: NativeSyntheticEvent) => selectionChange && selectionChange(start, end), + [selectionChange], ) const paddingStyles = useMemo( () => getSwapPanelPaddingValues(isOutput, Boolean(currencyInfo)), - [isOutput, currencyInfo] + [isOutput, currencyInfo], ) const handleToggleFiatInput = useCallback(() => { @@ -212,7 +201,8 @@ export function _CurrencyInputPanel(props: CurrentInputPanelProps): JSX.Element alignItems="center" justifyContent={!currencyInfo ? 'center' : 'space-between'} pb={innerPaddingBottom} - pt={innerPaddingTop}> + pt={innerPaddingTop} + > {isFiatInput && ( + mr="$spacing2" + > {fiatCurrencySymbol} )} {currencyInfo && ( - + - + {t('swap.form.balance')}:{' '} {formatCurrencyAmount({ value: currencyBalance, diff --git a/packages/wallet/src/components/legacy/DecimalPadLegacy.tsx b/packages/wallet/src/components/legacy/DecimalPadLegacy.tsx index 7a23c7d034e..93d5bb9db80 100644 --- a/packages/wallet/src/components/legacy/DecimalPadLegacy.tsx +++ b/packages/wallet/src/components/legacy/DecimalPadLegacy.tsx @@ -10,20 +10,7 @@ enum KeyAction { Delete = 'delete', } -export type KeyLabel = - | '1' - | '2' - | '3' - | '4' - | '5' - | '6' - | '7' - | '8' - | '9' - | '.' - | ',' - | '0' - | 'backspace' +export type KeyLabel = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '.' | ',' | '0' | 'backspace' type KeyProps = { action: KeyAction @@ -122,7 +109,7 @@ export function _DecimalPad({ setValue={setValue} value={value} /> - ) + ), )} ) @@ -159,8 +146,7 @@ function KeyButton({ // should only be deleting/inserting at position 2 of "5.13" // except in the case where start === 0 then also just treat it as start of the non-prefixed string (to avoid -1 index) const prefixLength = hasCurrencyPrefix ? 1 : 0 - const start = - selection && selection.start > 0 && hasCurrencyPrefix ? selection.start - 1 : selection?.start + const start = selection && selection.start > 0 && hasCurrencyPrefix ? selection.start - 1 : selection?.start const end = selection?.end && hasCurrencyPrefix ? selection.end - 1 : selection?.end // TODO(MOB-140): in USD mode, prevent user from typing in more than 2 decimals @@ -224,7 +210,8 @@ function KeyButton({ testID={'decimal-pad-' + label} width={index % 3 === 1 ? '50%' : '25%'} onLongPress={onLongPress} - onPress={onPress}> + onPress={onPress} + > {label === 'backspace' ? ( I18nManager.isRTL ? ( diff --git a/packages/wallet/src/components/modals/WarningModal/WarningInfo.tsx b/packages/wallet/src/components/modals/WarningModal/WarningInfo.tsx index 49808191596..1ad8dfd3794 100644 --- a/packages/wallet/src/components/modals/WarningModal/WarningInfo.tsx +++ b/packages/wallet/src/components/modals/WarningModal/WarningInfo.tsx @@ -1,10 +1,7 @@ import { PropsWithChildren, ReactNode, useState } from 'react' import { Flex, TouchableArea, isWeb } from 'ui/src' import { InfoCircle } from 'ui/src/components/icons' -import { - WarningModal, - WarningModalProps, -} from 'wallet/src/components/modals/WarningModal/WarningModal' +import { WarningModal, WarningModalProps } from 'wallet/src/components/modals/WarningModal/WarningModal' import { WarningTooltip } from 'wallet/src/components/modals/WarningModal/WarningTooltip.web' import { WarningTooltipProps } from 'wallet/src/components/modals/WarningModal/WarningTooltipProps' @@ -31,11 +28,7 @@ export function WarningInfo({ if (isWeb) { return ( - + {children} ) diff --git a/packages/wallet/src/components/modals/WarningModal/WarningModal.tsx b/packages/wallet/src/components/modals/WarningModal/WarningModal.tsx index 1b26f53a88a..a4adeb79e0a 100644 --- a/packages/wallet/src/components/modals/WarningModal/WarningModal.tsx +++ b/packages/wallet/src/components/modals/WarningModal/WarningModal.tsx @@ -57,14 +57,16 @@ export function WarningModal({ isDismissible={isDismissible} maxWidth={maxWidth} name={modalName} - onClose={onClose}> + onClose={onClose} + > + px={isWeb ? '$none' : '$spacing24'} + > {!hideIcon && ( + } + > {icon ?? } )} @@ -91,12 +93,7 @@ export function WarningModal({ )} {children} - + {closeText && ( )} diff --git a/packages/wallet/src/components/modals/WarningModal/WarningTooltip.tsx b/packages/wallet/src/components/modals/WarningModal/WarningTooltip.tsx index ffaec260afb..e4c1235f4e7 100644 --- a/packages/wallet/src/components/modals/WarningModal/WarningTooltip.tsx +++ b/packages/wallet/src/components/modals/WarningModal/WarningTooltip.tsx @@ -3,7 +3,5 @@ import { NotImplementedError } from 'utilities/src/errors' import { WarningTooltipProps } from 'wallet/src/components/modals/WarningModal/WarningTooltipProps' export function WarningTooltip(_props: PropsWithChildren): JSX.Element { - throw new NotImplementedError( - 'current tooltip implementation does not work properly for native mobile' - ) + throw new NotImplementedError('current tooltip implementation does not work properly for native mobile') } diff --git a/packages/wallet/src/components/modals/WarningModal/WarningTooltip.web.tsx b/packages/wallet/src/components/modals/WarningModal/WarningTooltip.web.tsx index a39e2eb77c5..d377c39ebf0 100644 --- a/packages/wallet/src/components/modals/WarningModal/WarningTooltip.web.tsx +++ b/packages/wallet/src/components/modals/WarningModal/WarningTooltip.web.tsx @@ -18,10 +18,7 @@ export function WarningTooltip({ return ( {triggerPlacement === 'end' && children} - + {trigger} diff --git a/packages/wallet/src/components/network/NetworkFee.test.tsx b/packages/wallet/src/components/network/NetworkFee.test.tsx index b8c96d3fec9..84b4dea95eb 100644 --- a/packages/wallet/src/components/network/NetworkFee.test.tsx +++ b/packages/wallet/src/components/network/NetworkFee.test.tsx @@ -16,9 +16,7 @@ jest.mock('wallet/src/features/transactions/swap/hooks/useGasFeeHighRelativeToVa describe(NetworkFee, () => { it('renders a NetworkFee normally', () => { - const tree = render( - - ) + const tree = render() expect(tree).toMatchSnapshot() }) @@ -28,9 +26,7 @@ describe(NetworkFee, () => { }) it('renders a NetworkFee in an error state', () => { - const tree = render( - - ) + const tree = render() expect(tree).toMatchSnapshot() }) }) diff --git a/packages/wallet/src/components/network/NetworkFee.tsx b/packages/wallet/src/components/network/NetworkFee.tsx index 63d72401ddc..c71cf060e8e 100644 --- a/packages/wallet/src/components/network/NetworkFee.tsx +++ b/packages/wallet/src/components/network/NetworkFee.tsx @@ -45,10 +45,9 @@ export function NetworkFee({ ) : ( + color={isLoading ? '$neutral3' : gasFeeHighRelativeToValue ? '$statusCritical' : '$neutral1'} + variant="body3" + > {gasFeeFormatted} )} diff --git a/packages/wallet/src/components/network/NetworkLogos.tsx b/packages/wallet/src/components/network/NetworkLogos.tsx index b55ea0c9360..92a901178c3 100644 --- a/packages/wallet/src/components/network/NetworkLogos.tsx +++ b/packages/wallet/src/components/network/NetworkLogos.tsx @@ -32,7 +32,7 @@ export function NetworkLogos({ ) : ( - + )} ) diff --git a/packages/wallet/src/components/network/NetworkPill.tsx b/packages/wallet/src/components/network/NetworkPill.tsx index 2b4a0732beb..b54a71780cb 100644 --- a/packages/wallet/src/components/network/NetworkPill.tsx +++ b/packages/wallet/src/components/network/NetworkPill.tsx @@ -36,13 +36,5 @@ export function NetworkPill({ } export function InlineNetworkPill(props: NetworkPillProps): JSX.Element { - return ( - - ) + return } diff --git a/packages/wallet/src/components/nfts/NFTHiddenRow.tsx b/packages/wallet/src/components/nfts/NFTHiddenRow.tsx index 41974c8d0dd..3ce2fc484b0 100644 --- a/packages/wallet/src/components/nfts/NFTHiddenRow.tsx +++ b/packages/wallet/src/components/nfts/NFTHiddenRow.tsx @@ -10,14 +10,7 @@ export function HiddenNftsRowLeft({ numHidden }: { numHidden: number }): JSX.Ele const { t } = useTranslation() return ( - + {t('tokens.nfts.hidden.label', { numHidden })} @@ -25,13 +18,7 @@ export function HiddenNftsRowLeft({ numHidden }: { numHidden: number }): JSX.Ele ) } -export function HiddenNftsRowRight({ - isExpanded, - onPress, -}: { - isExpanded: boolean - onPress: () => void -}): JSX.Element { +export function HiddenNftsRowRight({ isExpanded, onPress }: { isExpanded: boolean; onPress: () => void }): JSX.Element { const { t } = useTranslation() const chevronRotate = useSharedValue(isExpanded ? 180 : 0) @@ -51,11 +38,7 @@ export function HiddenNftsRowRight({ }, [chevronRotate, onPress]) return ( - + + py="$spacing4" + > {isExpanded ? t('common.button.hide') : t('common.button.show')} - + diff --git a/packages/wallet/src/components/nfts/NFTTransfer.tsx b/packages/wallet/src/components/nfts/NFTTransfer.tsx index 9af8464a4ea..079a5eadef9 100644 --- a/packages/wallet/src/components/nfts/NFTTransfer.tsx +++ b/packages/wallet/src/components/nfts/NFTTransfer.tsx @@ -3,24 +3,14 @@ import { iconSizes } from 'ui/src/theme' import { NFTViewer } from 'wallet/src/features/images/NFTViewer' import { GQLNftAsset } from 'wallet/src/features/nfts/hooks' -export function NFTTransfer({ - asset, - nftSize, -}: { - asset: GQLNftAsset - nftSize?: number -}): JSX.Element { +export function NFTTransfer({ asset, nftSize }: { asset: GQLNftAsset; nftSize?: number }): JSX.Element { return ( - + {asset?.name} diff --git a/packages/wallet/src/components/nfts/NftsList.tsx b/packages/wallet/src/components/nfts/NftsList.tsx index 2028b93ff3e..44366e9cf24 100644 --- a/packages/wallet/src/components/nfts/NftsList.tsx +++ b/packages/wallet/src/components/nfts/NftsList.tsx @@ -5,10 +5,7 @@ import { useTranslation } from 'react-i18next' import { ListRenderItemInfo, StyleProp, ViewStyle } from 'react-native' import { SharedValue } from 'react-native-reanimated' import { Flex, Loader } from 'ui/src' -import { - AnimatedBottomSheetFlashList, - AnimatedFlashList, -} from 'ui/src/components/AnimatedFlashList/AnimatedFlashList' +import { AnimatedBottomSheetFlashList, AnimatedFlashList } from 'ui/src/components/AnimatedFlashList/AnimatedFlashList' import { NoNfts } from 'ui/src/components/icons' import { useDeviceDimensions } from 'ui/src/hooks/useDeviceDimensions' import { BaseCard } from 'uniswap/src/components/BaseCard/BaseCard' @@ -74,7 +71,7 @@ export const NftsList = forwardRef, NftsListProps>(function _ onRefresh, ...rest }, - ref + ref, ) { const { t } = useTranslation() const { fullHeight } = useDeviceDimensions() @@ -145,7 +142,7 @@ export const NftsList = forwardRef, NftsListProps>(function _ return null } }, - [hiddenNftsExpanded, numHidden, onHiddenRowPressed, renderNFTItem] + [hiddenNftsExpanded, numHidden, onHiddenRowPressed, renderNFTItem], ) const onRetry = useCallback(() => refetch(), [refetch]) @@ -174,11 +171,7 @@ export const NftsList = forwardRef, NftsListProps>(function _ // empty view , NftsListProps>(function _ // we add a footer to cover any possible space, so user can scroll the top menu all the way to the top ListFooterComponent={ <> - {nfts.length > 0 && networkStatus === NetworkStatus.fetchMore && ( - - )} + {nfts.length > 0 && networkStatus === NetworkStatus.fetchMore && } {ListFooterComponent} } diff --git a/packages/wallet/src/components/settings/AnalyticsToggleLineSwitch.tsx b/packages/wallet/src/components/settings/AnalyticsToggleLineSwitch.tsx new file mode 100644 index 00000000000..db3ac66f91e --- /dev/null +++ b/packages/wallet/src/components/settings/AnalyticsToggleLineSwitch.tsx @@ -0,0 +1,30 @@ +import { useTranslation } from 'react-i18next' +import { Flex, Text, isWeb } from 'ui/src' +import { Switch, WebSwitch } from 'wallet/src/components/buttons/Switch' +import { selectAllowAnalytics } from 'wallet/src/features/telemetry/selectors' +import { setAllowAnalytics } from 'wallet/src/features/telemetry/slice' +import { useAppDispatch, useAppSelector } from 'wallet/src/state' + +export function AnalyticsToggleLineSwitch(): JSX.Element { + const { t } = useTranslation() + const dispatch = useAppDispatch() + const analyticsAllowed = useAppSelector(selectAllowAnalytics) + + const onChangeAllowAnalytics = (enabled: boolean): void => { + dispatch(setAllowAnalytics({ enabled })) + } + + const SwitchComponent = isWeb ? WebSwitch : Switch + + return ( + + + {t('settings.setting.privacy.analytics.title')} + + {t('settings.setting.privacy.analytics.description')} + + + + + ) +} diff --git a/packages/wallet/src/components/settings/language/SettingsLanguageModal.native.tsx b/packages/wallet/src/components/settings/language/SettingsLanguageModal.native.tsx index 795dba09dcd..866ea7dda5a 100644 --- a/packages/wallet/src/components/settings/language/SettingsLanguageModal.native.tsx +++ b/packages/wallet/src/components/settings/language/SettingsLanguageModal.native.tsx @@ -23,10 +23,7 @@ export function SettingsLanguageModal({ onClose }: SettingsLanguageModalProps): return ( - + @@ -39,10 +36,7 @@ export function SettingsLanguageModal({ onClose }: SettingsLanguageModalProps): {t('settings.setting.language.description.mobile')} - diff --git a/packages/wallet/src/components/settings/language/SettingsLanguageModal.web.tsx b/packages/wallet/src/components/settings/language/SettingsLanguageModal.web.tsx index cd803c1864e..56718d1a47c 100644 --- a/packages/wallet/src/components/settings/language/SettingsLanguageModal.web.tsx +++ b/packages/wallet/src/components/settings/language/SettingsLanguageModal.web.tsx @@ -17,7 +17,8 @@ export function SettingsLanguageModal({ onClose }: SettingsLanguageModalProps): + style={{ backgroundColor: opacify(10, colors.DEP_blue300.val) }} + > diff --git a/packages/wallet/src/components/text/RelativeChange.test.tsx b/packages/wallet/src/components/text/RelativeChange.test.tsx index c691005922d..f6405bb87e7 100644 --- a/packages/wallet/src/components/text/RelativeChange.test.tsx +++ b/packages/wallet/src/components/text/RelativeChange.test.tsx @@ -38,7 +38,7 @@ it('renders a relative change', () => { const tree = renderer.create( - + , ) expect(tree).toMatchSnapshot() }) @@ -47,7 +47,7 @@ it('renders placeholders without a change', () => { const tree = renderer.create( - + , ) expect(tree).toMatchSnapshot() }) @@ -56,7 +56,7 @@ it('renders placeholders with absolute change', () => { const tree = renderer.create( - + , ) expect(tree).toMatchSnapshot() }) diff --git a/packages/wallet/src/components/text/RelativeChange.tsx b/packages/wallet/src/components/text/RelativeChange.tsx index 9082e9b82cd..a49383cf475 100644 --- a/packages/wallet/src/components/text/RelativeChange.tsx +++ b/packages/wallet/src/components/text/RelativeChange.tsx @@ -50,18 +50,16 @@ export function RelativeChange(props: RelativeChangeProps): JSX.Element { alignItems="center" gap="$spacing2" justifyContent={alignRight ? 'flex-end' : 'flex-start'} - testID="relative-change"> - {change !== undefined && ( - - )} + testID="relative-change" + > + {change !== undefined && } + variant={variant} + > {absoluteChange ? `${formattedAbsChange} (${formattedChange})` : formattedChange} diff --git a/packages/wallet/src/constants/tokens.ts b/packages/wallet/src/constants/tokens.ts index 73a8646075c..b0e42f2c485 100644 --- a/packages/wallet/src/constants/tokens.ts +++ b/packages/wallet/src/constants/tokens.ts @@ -1,64 +1,64 @@ // Copied from https://github.com/Uniswap/interface/blob/main/src/constants/tokens.ts import { Token } from '@uniswap/sdk-core' +import { UNI_ADDRESS } from 'uniswap/src/constants/addresses' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' -import { UNI_ADDRESS } from 'wallet/src/constants/addresses' export const DAI = new Token( UniverseChainId.Mainnet, '0x6b175474e89094c44da98b954eedeac495271d0f', 18, 'DAI', - 'Dai Stablecoin' + 'Dai Stablecoin', ) export const DAI_ARBITRUM_ONE = new Token( UniverseChainId.ArbitrumOne, '0xda10009cbd5d07dd0cecc66161fc93d7c9000da1', 18, 'DAI', - 'Dai stable coin' + 'Dai stable coin', ) export const USDC = new Token( UniverseChainId.Mainnet, '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_ARBITRUM = new Token( UniverseChainId.ArbitrumOne, '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDBC_BASE = new Token( UniverseChainId.Base, '0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca', 6, 'USDbC', - 'USD Base Coin' + 'USD Base Coin', ) export const USDT_BNB = new Token( UniverseChainId.Bnb, '0x55d398326f99059ff775485246999027b3197955', 18, 'USDT', - 'TetherUSD' + 'TetherUSD', ) export const USDC_OPTIMISM = new Token( UniverseChainId.Optimism, '0x7f5c764cbc14f9669b88837ca1490cca17c31607', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_POLYGON = new Token( UniverseChainId.Polygon, '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_GOERLI = new Token( @@ -66,7 +66,7 @@ export const USDC_GOERLI = new Token( '0x07865c6e87b9f70255377e024ace6630c1eaa37f', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDT = new Token( @@ -74,31 +74,19 @@ export const USDT = new Token( '0xdac17f958d2ee523a2206206994597c13d831ec7', 6, 'USDT', - 'Tether USD' + 'Tether USD', ) -export const USDB = new Token( - UniverseChainId.Blast, - '0x4300000000000000000000000000000000000003', - 18, - 'USDB', - 'USDB' -) +export const USDB = new Token(UniverseChainId.Blast, '0x4300000000000000000000000000000000000003', 18, 'USDB', 'USDB') -export const CUSD = new Token( - UniverseChainId.Celo, - '0x765de816845861e75a25fca122bb6898b8b1282a', - 18, - 'CUSD', - 'CUSD' -) +export const CUSD = new Token(UniverseChainId.Celo, '0x765de816845861e75a25fca122bb6898b8b1282a', 18, 'CUSD', 'CUSD') export const USDC_AVALANCHE = new Token( UniverseChainId.Avalanche, '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', 6, 'USDC', - 'USDC' + 'USDC', ) export const USDzC = new Token( @@ -106,7 +94,7 @@ export const USDzC = new Token( '0xCccCCccc7021b32EBb4e8C08314bD62F7c653EC4', 6, 'USDzC', - 'USD Coin' + 'USD Coin', ) export const USDC_ZKSYNC = new Token( @@ -114,7 +102,7 @@ export const USDC_ZKSYNC = new Token( '0x1d17CBcF0D6D143135aE902365D2E5e2A16538D4', 6, 'USDC', - 'USDC' + 'USDC', ) export const WBTC = new Token( @@ -122,7 +110,7 @@ export const WBTC = new Token( '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', 8, 'WBTC', - 'Wrapped BTC' + 'Wrapped BTC', ) export const UNI = { @@ -131,14 +119,14 @@ export const UNI = { UNI_ADDRESS[UniverseChainId.Mainnet], 18, 'UNI', - 'Uniswap' + 'Uniswap', ), [UniverseChainId.Goerli]: new Token( UniverseChainId.Goerli, UNI_ADDRESS[UniverseChainId.Goerli], 18, 'UNI', - 'Uniswap' + 'Uniswap', ), } @@ -149,6 +137,6 @@ export function wrappedNativeCurrency(chainId: WalletChainId): Token { wrappedCurrencyInfo.address, wrappedCurrencyInfo.decimals, wrappedCurrencyInfo.symbol, - wrappedCurrencyInfo.name + wrappedCurrencyInfo.name, ) } diff --git a/packages/wallet/src/contexts/WalletNavigationContext.tsx b/packages/wallet/src/contexts/WalletNavigationContext.tsx index e31f61c88ff..37cb16361c1 100644 --- a/packages/wallet/src/contexts/WalletNavigationContext.tsx +++ b/packages/wallet/src/contexts/WalletNavigationContext.tsx @@ -2,10 +2,7 @@ import { createContext, ReactNode, useContext } from 'react' import { WalletChainId } from 'uniswap/src/types/chains' import { NFTItem } from 'wallet/src/features/nfts/types' import { getSwapPrefilledState } from 'wallet/src/features/transactions/swap/hooks/useSwapPrefilledState' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { getSendPrefilledState } from 'wallet/src/features/transactions/transfer/getSendPrefilledState' type NavigateToTransactionFlowTransactionState = { @@ -34,43 +31,33 @@ export type NavigateToSendFlowArgs = | undefined function isNavigateToTransactionFlowArgsInitialState( - args: NavigateToSwapFlowArgs | NavigateToSendFlowArgs + args: NavigateToSwapFlowArgs | NavigateToSendFlowArgs, ): args is NavigateToTransactionFlowTransactionState { - return Boolean( - args && (args as NavigateToTransactionFlowTransactionState).initialState !== undefined - ) + return Boolean(args && (args as NavigateToTransactionFlowTransactionState).initialState !== undefined) } -function isNavigateToSwapFlowArgsPartialState( - args: NavigateToSwapFlowArgs -): args is NavigateToSwapFlowPartialState { +function isNavigateToSwapFlowArgsPartialState(args: NavigateToSwapFlowArgs): args is NavigateToSwapFlowPartialState { return Boolean(args && (args as NavigateToSwapFlowPartialState).currencyAddress !== undefined) } -function isNavigateToSendFlowArgsPartialState( - args: NavigateToSendFlowArgs -): args is NavigateToSendFlowPartialState { +function isNavigateToSendFlowArgsPartialState(args: NavigateToSendFlowArgs): args is NavigateToSendFlowPartialState { return Boolean(args && (args as NavigateToSendFlowPartialState).chainId !== undefined) } -export function getNavigateToSwapFlowArgsInitialState( - args: NavigateToSwapFlowArgs -): TransactionState | undefined { +export function getNavigateToSwapFlowArgsInitialState(args: NavigateToSwapFlowArgs): TransactionState | undefined { return isNavigateToTransactionFlowArgsInitialState(args) ? args.initialState : isNavigateToSwapFlowArgsPartialState(args) - ? getSwapPrefilledState(args) - : undefined + ? getSwapPrefilledState(args) + : undefined } -export function getNavigateToSendFlowArgsInitialState( - args: NavigateToSendFlowArgs -): TransactionState | undefined { +export function getNavigateToSendFlowArgsInitialState(args: NavigateToSendFlowArgs): TransactionState | undefined { return isNavigateToTransactionFlowArgsInitialState(args) ? args.initialState : isNavigateToSendFlowArgsPartialState(args) - ? getSendPrefilledState(args) - : undefined + ? getSendPrefilledState(args) + : undefined } export type NavigateToNftItemArgs = { @@ -81,6 +68,10 @@ export type NavigateToNftItemArgs = { fallbackData?: NFTItem } +export type NavigateToNftCollectionArgs = { + collectionAddress: Address +} + export type ShareTokenArgs = { currencyId: string } @@ -96,6 +87,7 @@ export type WalletNavigationContextState = { // Action that should be taken when the user presses the "Buy crypto" or "Receive tokens" button when they open the Send flow with an empty wallet. navigateToBuyOrReceiveWithEmptyWallet: () => void navigateToNftDetails: (args: NavigateToNftItemArgs) => void + navigateToNftCollection: (args: NavigateToNftCollectionArgs) => void navigateToSwapFlow: (args: NavigateToSwapFlowArgs) => void navigateToTokenDetails: (currencyId: string) => void navigateToReceive: () => void @@ -104,9 +96,7 @@ export type WalletNavigationContextState = { handleShareToken: (args: ShareTokenArgs) => void } -export const WalletNavigationContext = createContext( - undefined -) +export const WalletNavigationContext = createContext(undefined) export function WalletNavigationProvider({ children, @@ -114,9 +104,7 @@ export function WalletNavigationProvider({ }: { children: ReactNode } & WalletNavigationContextState): JSX.Element { - return ( - {children} - ) + return {children} } export const useWalletNavigation = (): WalletNavigationContextState => { diff --git a/packages/wallet/src/data/apollo/usePersistedApolloClient.tsx b/packages/wallet/src/data/apollo/usePersistedApolloClient.tsx index 113bbc8c320..19bc09522d0 100644 --- a/packages/wallet/src/data/apollo/usePersistedApolloClient.tsx +++ b/packages/wallet/src/data/apollo/usePersistedApolloClient.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from 'react' import { MMKV } from 'react-native-mmkv' import { WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' -import { isNonJestDev } from 'utilities/src/environment' +import { isNonJestDev } from 'utilities/src/environment/constants' import { logger } from 'utilities/src/logger/logger' import { isMobileApp } from 'utilities/src/platform' import { useAsyncData } from 'utilities/src/react/hooks' @@ -28,9 +28,7 @@ export const apolloClientRef: ApolloClientRef = ((): ApolloClientRef => { let apolloClient: ApolloClient | null = null const listeners: Array< - ( - value: ApolloClient | PromiseLike> - ) => void + (value: ApolloClient | PromiseLike>) => void > = [] const ref: ApolloClientRef = { @@ -77,9 +75,7 @@ export const usePersistedApolloClient = ({ }): ApolloClient | undefined => { const [client, setClient] = useState>() - const apolloLink = customEndpoint - ? getCustomGraphqlHttpLink(customEndpoint) - : getGraphqlHttpLink() + const apolloLink = customEndpoint ? getCustomGraphqlHttpLink(customEndpoint) : getGraphqlHttpLink() const init = useCallback(async () => { const cache = await initAndPersistCache({ storage: storageWrapper, maxCacheSizeInBytes }) @@ -88,7 +84,7 @@ export const usePersistedApolloClient = ({ logger.debug( 'usePersistedApolloClient', 'usePersistedApolloClient', - `Using custom endpoint ${customEndpoint.url}` + `Using custom endpoint ${customEndpoint.url}`, ) } @@ -100,9 +96,7 @@ export const usePersistedApolloClient = ({ getErrorLink(), // requires typing outside of wallet package // eslint-disable-next-line @typescript-eslint/no-explicit-any - getPerformanceLink((args: any) => - sendAnalyticsEvent(WalletEventName.PerformanceGraphql, args) - ), + getPerformanceLink((args: any) => sendAnalyticsEvent(WalletEventName.PerformanceGraphql, args)), restLink, apolloLink, ]), diff --git a/packages/wallet/src/data/links.ts b/packages/wallet/src/data/links.ts index a9fb8fe3f07..38b17d7c9e4 100644 --- a/packages/wallet/src/data/links.ts +++ b/packages/wallet/src/data/links.ts @@ -5,11 +5,7 @@ import { config } from 'uniswap/src/config' import { uniswapUrls } from 'uniswap/src/constants/urls' import { REQUEST_SOURCE, getVersionHeader } from 'uniswap/src/data/constants' import { logger } from 'utilities/src/logger/logger' -import { - EnsLookupParams, - STUB_ONCHAIN_ENS_ENDPOINT, - getOnChainEnsFetch, -} from 'wallet/src/features/ens/api' +import { EnsLookupParams, STUB_ONCHAIN_ENS_ENDPOINT, getOnChainEnsFetch } from 'wallet/src/features/ens/api' import { BalanceLookupParams, STUB_ONCHAIN_BALANCES_ENDPOINT, @@ -18,12 +14,8 @@ import { // mapping from endpoint to custom fetcher, when needed function getCustomFetcherMap( - restUri: string -): Record< - string, - | ((body: BalanceLookupParams) => Promise) - | ((body: EnsLookupParams) => Promise) -> { + restUri: string, +): Record Promise) | ((body: EnsLookupParams) => Promise)> { return { [restUri + STUB_ONCHAIN_BALANCES_ENDPOINT]: getOnChainBalancesFetch, [restUri + STUB_ONCHAIN_ENS_ENDPOINT]: getOnChainEnsFetch, @@ -107,7 +99,7 @@ export function sample(cb: () => void, rate: number): void { export function getErrorLink( graphqlErrorSamplingRate = APOLLO_GRAPHQL_ERROR_SAMPLING_RATE, - networkErrorSamplingRate = APOLLO_NETWORK_ERROR_SAMPLING_RATE + networkErrorSamplingRate = APOLLO_NETWORK_ERROR_SAMPLING_RATE, ): ApolloLink { // Log any GraphQL errors or network error that occurred const errorLink = onError(({ graphQLErrors, networkError }) => { @@ -122,15 +114,14 @@ export function getErrorLink( }, extra: { message, locations, path }, }), - graphqlErrorSamplingRate + graphqlErrorSamplingRate, ) }) } if (networkError) { sample( - () => - logger.error(networkError, { tags: { file: 'data/links', function: 'getErrorLink' } }), - networkErrorSamplingRate + () => logger.error(networkError, { tags: { file: 'data/links', function: 'getErrorLink' } }), + networkErrorSamplingRate, ) } }) @@ -140,7 +131,7 @@ export function getErrorLink( export function getPerformanceLink( sendAnalyticsEvent: (args: Record) => void, - samplingRate = APOLLO_PERFORMANCE_SAMPLING_RATE + samplingRate = APOLLO_PERFORMANCE_SAMPLING_RATE, ): ApolloLink { return new ApolloLink((operation, forward) => { const startTime = Date.now() @@ -156,7 +147,7 @@ export function getPerformanceLink( duration, operationName: operation.operationName, }), - samplingRate + samplingRate, ) return data diff --git a/packages/wallet/src/data/tradingApi/api.json b/packages/wallet/src/data/tradingApi/api.json index 0db1d175d20..246b3d54675 100644 --- a/packages/wallet/src/data/tradingApi/api.json +++ b/packages/wallet/src/data/tradingApi/api.json @@ -1 +1 @@ -{"openapi":"3.0.0","servers":[{"description":"Uniswap trading APIs Beta","url":"https://beta.trade-api.gateway.uniswap.org/v1"},{"description":"Uniswap trading APIs","url":"https://trade-api.gateway.uniswap.org/v1"}],"info":{"version":"1.0.0","title":"Token Trading","description":"Uniswap trading APIs for fungible tokens."},"paths":{"/check_approval":{"post":{"tags":["Approval"],"summary":"Check if token approval is required","description":"Checks if the swapper has the required approval. If the swapper does not have the required approval, then the response will include the transaction to approve the token. If the swapper has the required approval, then the response will be empty. If the parameter `includeGasInfo` is set to `true`, then the response will include the gas fee for the approval transaction.","operationId":"check_approval","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalRequest"}}}},"responses":{"200":{"$ref":"#/components/responses/ApprovalSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"401":{"$ref":"#/components/responses/ApprovalUnauthorized401"},"404":{"$ref":"#/components/responses/ApprovalNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/quote":{"post":{"tags":["Quote"],"summary":"Get a quote","description":"Get a quote according to the provided configuration. Optionally adds a fee to the quote according to the API key being used. The fee is **ALWAYS** taken from the output token. If there is a fee and the trade is `EXACT_INPUT`, then the output amount will **NOT** include the fee subtraction. For `EXACT_INPUT` swaps, use `portionBips` to calculate the fee from the quoted amount. If there is a fee and the trade is `EXACT_OUTPUT`, then the input amount will **NOT** include the fee addition to account for the fee. For `EXACT_OUTPUT` swaps, use `portionAmount` to get the fee. \n \n We also support Wrapping and Unwrapping of native tokens on their respective chains. Wrapping and Unwrapping only works for when `routingPreference` is `CLASSIC`, `BEST_PRICE`, or `BEST_PRICE_V2`. We do not support `UNISWAPX` or `UNISWAPX_V2` for these actions.","operationId":"aggregator_quote","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}}},"responses":{"200":{"$ref":"#/components/responses/QuoteSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"401":{"$ref":"#/components/responses/QuoteUnauthorized401"},"404":{"$ref":"#/components/responses/QuoteNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/order":{"post":{"tags":["Order"],"summary":"Create a gasless order","description":"Submits a new gasless encoded order. The order will be validated and if valid, will be submitted to the filler network. The network will try to fill the order at the quoted `startAmount`, and if not, the amount will start decaying until the `endAmount` is reached. While the order is within `decayEndTime`, the `orderStatus` is `open`. If the order does not get filled after the `decayEndTime` has passed, that is reflected in the `expired` `orderStatus`. then The order will be filled at the best price possible. Once the order is filled, `orderStatus` becomes `filled`.","operationId":"post_dutch_order","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"}}}},"responses":{"201":{"$ref":"#/components/responses/OrderSuccess201"},"400":{"$ref":"#/components/responses/BadRequest400"},"401":{"$ref":"#/components/responses/OrderUnauthorized401"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/orders":{"get":{"tags":["Order"],"summary":"Get gasless orders","description":"Retrieve gasless orders filtered by query param(s). Some fields on the order can be used as query param.","operationId":"get_dutch_order","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/orderIdParam"},{"$ref":"#/components/parameters/orderIdsParam"},{"$ref":"#/components/parameters/limitParam"},{"$ref":"#/components/parameters/orderStatusParam"},{"$ref":"#/components/parameters/swapperParam"},{"$ref":"#/components/parameters/sortKeyParam"},{"$ref":"#/components/parameters/sortParam"},{"$ref":"#/components/parameters/fillerParam"},{"$ref":"#/components/parameters/cursorParam"}],"responses":{"200":{"$ref":"#/components/responses/OrdersSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"404":{"$ref":"#/components/responses/OrdersNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/swap":{"post":{"tags":["Swap"],"summary":"Create swap calldata","description":"Create the calldata for a swap transaction (including wrap/unwrap) against the Uniswap Protocols. If the `quote` parameter includes the fee parameters, then the calldata will include the fee disbursement. The gas estimates will be **more precise** when the the response calldata would be valid if submitted on-chain.","operationId":"create_swap_transaction","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSwapRequest"}}}},"responses":{"200":{"$ref":"#/components/responses/CreateSwapSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"401":{"$ref":"#/components/responses/SwapUnauthorized401"},"404":{"$ref":"#/components/responses/SwapNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/swap/{txHash}":{"get":{"tags":["Swap"],"summary":"Get swap status","description":"Get the status of a swap transaction.","operationId":"get_swap_transaction","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/transactionHashParam"}],"responses":{"200":{"$ref":"#/components/responses/GetSwapSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"404":{"$ref":"#/components/responses/SwapNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}}},"components":{"responses":{"OrdersSuccess200":{"description":"The request orders matching the query parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrdersResponse"}}}},"OrderSuccess201":{"description":"Encoded order submitted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"QuoteSuccess200":{"description":"Quote request successful.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}},"ApprovalSuccess200":{"description":"Check approval successful.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalResponse"}}}},"CreateSwapSuccess200":{"description":"Create swap successful.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSwapResponse"}}}},"GetSwapSuccess200":{"description":"Get swap successful.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSwapResponse"}}}},"BadRequest400":{"description":"RequestValidationError, Bad Input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err400"}}}},"ApprovalUnauthorized401":{"description":"UnauthorizedError eg. Account is blocked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err401"}}}},"ApprovalNotFound404":{"description":"ResourceNotFound eg. Token allowance not found or Gas info not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err404"}}}},"QuoteUnauthorized401":{"description":"UnauthorizedError eg. Account is blocked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err401"}}}},"QuoteNotFound404":{"description":"ResourceNotFound eg. No quotes available or Gas fee/price not available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err404"}}}},"SwapBadRequest400":{"description":"RequestValidationError, Bad Input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err400"}}}},"SwapUnauthorized401":{"description":"UnauthorizedError eg. Account is blocked or Fee is not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err401"}}}},"SwapNotFound404":{"description":"ResourceNotFound eg. No quotes available or Gas fee/price not available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err404"}}}},"OrderUnauthorized401":{"description":"UnauthorizedError eg. Account is blocked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err401"}}}},"OrdersNotFound404":{"description":"Orders not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err404"}}}},"RateLimitedErr419":{"description":"Ratelimited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err419"}}}},"InternalErr500":{"description":"Unexpected error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err500"}}}},"Timeout504":{"description":"Request duration limit reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err504"}}}}},"schemas":{"NullablePermit":{"allOf":[{"$ref":"#/components/schemas/Permit"},{"type":"object","nullable":true}]},"TokenAmount":{"type":"string"},"SwapStatus":{"type":"string","enum":["pending","success","error"]},"GetSwapResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"status":{"$ref":"#/components/schemas/SwapStatus"}},"required":["requestId","status"]},"CreateSwapRequest":{"type":"object","description":"The parameters **signature** and **permitData** should only be included if *permitData* was returned from **/quote**.","properties":{"quote":{"oneOf":[{"$ref":"#/components/schemas/ClassicQuote"},{"$ref":"#/components/schemas/WrapUnwrapQuote"}]},"signature":{"type":"string","description":"The signed permit."},"includeGasInfo":{"type":"boolean","default":false,"deprecated":true,"description":"Use `refreshGasPrice` instead."},"refreshGasPrice":{"type":"boolean","default":false,"description":"If true, the gas price will be re-fetched from the network."},"simulateTransaction":{"type":"boolean","default":false,"description":"If true, the transaction will be simulated. If the simulation results on an onchain error, endpoint will return an error."},"permitData":{"allOf":[{"$ref":"#/components/schemas/Permit"}]},"safetyMode":{"$ref":"#/components/schemas/SwapSafetyMode"},"deadline":{"type":"integer","description":"The deadline for the swap in unix timestamp format. If the deadline is not defined OR in the past then the default deadline is 30 minutes."}},"required":["quote"]},"Address":{"type":"string","pattern":"^(0x)?[0-9a-fA-F]{40}$"},"CreateSwapResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"swap":{"$ref":"#/components/schemas/TransactionRequest"},"gasFee":{"type":"string"}},"required":["requestId","swap"]},"QuoteResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"quote":{"$ref":"#/components/schemas/Quote"},"routing":{"$ref":"#/components/schemas/Routing"},"permitData":{"$ref":"#/components/schemas/NullablePermit"}},"required":["routing","quote","permitData","requestId"]},"QuoteRequest":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/TradeType"},"amount":{"type":"string"},"tokenInChainId":{"$ref":"#/components/schemas/ChainId"},"tokenOutChainId":{"$ref":"#/components/schemas/ChainId"},"tokenIn":{"type":"string"},"tokenOut":{"type":"string"},"swapper":{"$ref":"#/components/schemas/Address"},"slippageTolerance":{"description":"For **Classic** swaps, the slippage tolerance is the maximum amount the price can change between the time the transaction is submitted and the time it is executed. The slippage tolerance is represented as a percentage of the total value of the swap. \n\n Slippage tolerance works differently in **DutchLimit** swaps, it does not set a limit on the Spread in an order. See [here](https://uniswap-docs.readme.io/reference/faqs#why-do-the-uniswapx-quotes-have-more-slippage-than-the-tolerance-i-set) for more information. \n\n **NOTE**: slippage is in terms of trade type. If the trade type is `EXACT_INPUT`, then the slippage is in terms of the output token. If the trade type is `EXACT_OUTPUT`, then the slippage is in terms of the input token.","type":"number"},"autoSlippage":{"$ref":"#/components/schemas/AutoSlippage"},"routingPreference":{"$ref":"#/components/schemas/RoutingPreference"},"spreadOptimization":{"$ref":"#/components/schemas/SpreadOptimization"}},"required":["type","amount","tokenInChainId","tokenOutChainId","tokenIn","tokenOut","swapper"]},"GetOrdersResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/UniswapXOrder"}},"cursor":{"type":"string"}},"required":["orders","requestId"]},"OrderResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"orderId":{"type":"string"},"orderStatus":{"$ref":"#/components/schemas/OrderStatus"}},"required":["requestId","orderId","orderStatus"]},"OrderRequest":{"type":"object","properties":{"signature":{"type":"string","description":"The signed permit."},"quote":{"oneOf":[{"$ref":"#/components/schemas/DutchQuote"},{"$ref":"#/components/schemas/DutchQuoteV2"}]},"routing":{"$ref":"#/components/schemas/Routing"}},"required":["signature","quote"]},"Urgency":{"type":"string","enum":["normal","fast","urgent"],"description":"The urgency determines the urgency of the approval transaction. The default value is `urgent`.","default":"urgent"},"Err400":{"type":"object","properties":{"errorCode":{"default":"RequestValidationError","type":"string"},"detail":{"type":"string"}}},"Err401":{"type":"object","properties":{"errorCode":{"default":"UnauthorizedError","type":"string"},"detail":{"type":"string"}}},"Err404":{"type":"object","properties":{"errorCode":{"default":"ResourceNotFound","type":"string"},"detail":{"type":"string"}}},"Err419":{"type":"object","properties":{"errorCode":{"default":"Ratelimited","type":"string"},"detail":{"type":"string"}}},"Err500":{"type":"object","properties":{"errorCode":{"default":"InternalServerError","type":"string"},"detail":{"type":"string"}}},"Err504":{"type":"object","properties":{"errorCode":{"default":"Timeout","type":"string"},"detail":{"type":"string"}}},"ChainId":{"type":"number","enum":[1,10,56,137,8453,42161,81457,43114,42220,7777777,324]},"OrderInput":{"type":"object","properties":{"token":{"type":"string"},"startAmount":{"type":"string"},"endAmount":{"type":"string"}},"required":["token"]},"OrderOutput":{"type":"object","properties":{"token":{"type":"string"},"startAmount":{"type":"string"},"endAmount":{"type":"string"},"isFeeOutput":{"type":"boolean"},"recipient":{"type":"string"}},"required":["token"]},"CosignerData":{"type":"object","properties":{"decayStartTime":{"type":"number"},"decayEndTime":{"type":"number"},"exclusiveFiller":{"type":"string"},"inputOverride":{"type":"string"},"outputOverrides":{"type":"array","items":{"type":"string"}}}},"SettledAmount":{"type":"object","properties":{"tokenOut":{"$ref":"#/components/schemas/Address"},"amountOut":{"type":"string"},"tokenIn":{"$ref":"#/components/schemas/Address"},"amountIn":{"type":"string"}}},"OrderType":{"type":"string","enum":["DutchLimit","Dutch","Dutch_V2"]},"UniswapXOrder":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/OrderType"},"encodedOrder":{"type":"string"},"signature":{"type":"string"},"nonce":{"type":"string"},"orderStatus":{"$ref":"#/components/schemas/OrderStatus"},"orderId":{"type":"string"},"chainId":{"$ref":"#/components/schemas/ChainId"},"quoteId":{"type":"string"},"swapper":{"type":"string"},"txHash":{"type":"string"},"input":{"$ref":"#/components/schemas/OrderInput"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/OrderOutput"}},"settledAmounts":{"type":"array","items":{"$ref":"#/components/schemas/SettledAmount"}},"cosignature":{"type":"string"},"cosignerData":{"$ref":"#/components/schemas/CosignerData"}},"required":["encodedOrder","signature","nonce","orderId","orderStatus","chainId","type"]},"SortKey":{"type":"string","enum":["createdAt"]},"OrderId":{"type":"string"},"OrderIds":{"type":"string"},"OrderStatus":{"type":"string","enum":["open","expired","error","cancelled","filled","unverified","insufficient-funds"]},"Permit":{"type":"object","properties":{"domain":{"type":"object"},"values":{"type":"object"},"types":{"type":"object"}}},"DutchInput":{"type":"object","properties":{"startAmount":{"type":"string"},"endAmount":{"type":"string"},"token":{"type":"string"}},"required":["startAmount","endAmount","type"]},"DutchOutput":{"type":"object","properties":{"startAmount":{"type":"string"},"endAmount":{"type":"string"},"token":{"type":"string"},"recipient":{"type":"string"}},"required":["startAmount","endAmount","token","recipient"]},"DutchOrderInfo":{"type":"object","properties":{"chainId":{"$ref":"#/components/schemas/ChainId"},"nonce":{"type":"string"},"reactor":{"type":"string"},"swapper":{"type":"string"},"deadline":{"type":"number"},"additionalValidationContract":{"type":"string"},"additionalValidationData":{"type":"string"},"decayStartTime":{"type":"number"},"decayEndTime":{"type":"number"},"exclusiveFiller":{"type":"string"},"exclusivityOverrideBps":{"type":"string"},"input":{"$ref":"#/components/schemas/DutchInput"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/DutchOutput"}}},"required":["chainId","nonce","reactor","swapper","deadline","validationContract","validationData","startTime","endTime","exclusiveFiller","exclusivityOverrideBps","input","outputs"]},"DutchOrderInfoV2":{"type":"object","properties":{"chainId":{"$ref":"#/components/schemas/ChainId"},"nonce":{"type":"string"},"reactor":{"type":"string"},"swapper":{"type":"string"},"deadline":{"type":"number"},"additionalValidationContract":{"type":"string"},"additionalValidationData":{"type":"string"},"input":{"$ref":"#/components/schemas/DutchInput"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/DutchOutput"}},"cosigner":{"$ref":"#/components/schemas/Address"}},"required":["chainId","nonce","reactor","swapper","deadline","validationContract","validationData","startTime","endTime","exclusiveFiller","exclusivityOverrideBps","input","outputs"]},"DutchQuote":{"type":"object","properties":{"encodedOrder":{"type":"string"},"orderId":{"type":"string"},"orderInfo":{"$ref":"#/components/schemas/DutchOrderInfo"},"portionBips":{"type":"number"},"portionAmount":{"type":"string"},"portionRecipient":{"$ref":"#/components/schemas/Address"},"quoteId":{"type":"string"},"slippageTolerance":{"type":"number"}},"required":["encodedOrder","orderInfo","orderId"]},"DutchQuoteV2":{"type":"object","properties":{"encodedOrder":{"type":"string"},"orderId":{"type":"string"},"orderInfo":{"$ref":"#/components/schemas/DutchOrderInfoV2"},"portionBips":{"type":"number"},"portionAmount":{"type":"string"},"portionRecipient":{"$ref":"#/components/schemas/Address"},"quoteId":{"type":"string"},"slippageTolerance":{"type":"number"},"deadlineBufferSecs":{"type":"number"}},"required":["encodedOrder","orderInfo","orderId"]},"TradeType":{"type":"string","enum":["EXACT_INPUT","EXACT_OUTPUT"]},"Routing":{"type":"string","enum":["DUTCH_LIMIT","CLASSIC","DUTCH_V2"]},"Quote":{"oneOf":[{"$ref":"#/components/schemas/DutchQuote"},{"$ref":"#/components/schemas/ClassicQuote"},{"$ref":"#/components/schemas/WrapUnwrapQuote"},{"$ref":"#/components/schemas/DutchQuoteV2"}]},"ApprovalRequest":{"type":"object","properties":{"walletAddress":{"$ref":"#/components/schemas/Address"},"token":{"$ref":"#/components/schemas/Address"},"amount":{"$ref":"#/components/schemas/TokenAmount"},"chainId":{"$ref":"#/components/schemas/ChainId"},"urgency":{"$ref":"#/components/schemas/Urgency"},"includeGasInfo":{"type":"boolean","default":false},"tokenOut":{"type":"string","pattern":"^(0x)?[0-9a-fA-F]{40}$","description":"relevant for if we go from a wrapped token to a native token (unwrapping)"},"tokenOutChainId":{"type":"number","enum":[1,10,56,137,8453,42161,81457,43114,42220,7777777,324],"default":1,"description":"relevant for if we go from a wrapped token to a native token (unwrapping)"}},"required":["walletAddress","token","amount"]},"ApprovalResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"approval":{"$ref":"#/components/schemas/TransactionRequest"},"gasFee":{"type":"string"}},"required":["requestId","approval"]},"ClassicQuote":{"type":"object","properties":{"input":{"$ref":"#/components/schemas/ClassicInput"},"output":{"$ref":"#/components/schemas/ClassicOutput"},"swapper":{"$ref":"#/components/schemas/Address"},"chainId":{"$ref":"#/components/schemas/ChainId"},"slippage":{"type":"number"},"tradeType":{"$ref":"#/components/schemas/TradeType"},"gasFee":{"type":"string","description":"The gas fee in terms of wei."},"gasFeeUSD":{"type":"string","description":"The gas fee in terms of USD."},"gasFeeQuote":{"type":"string","description":"The gas fee in terms of the quoted currency."},"route":{"type":"array","items":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/V3PoolInRoute"},{"$ref":"#/components/schemas/V2PoolInRoute"}]}}},"portionBips":{"type":"number","description":"The portion of the swap that will be taken as a fee. The fee will be taken from the output token."},"portionAmount":{"type":"string","description":"The amount of the swap that will be taken as a fee. The fee will be taken from the output token."},"portionRecipient":{"$ref":"#/components/schemas/Address"},"routeString":{"type":"string","description":"The route in string format."},"quoteId":{"type":"string","description":"The quote id. Used for analytics purposes."},"gasUseEstimate":{"type":"string","description":"The estimated gas use."},"blockNumber":{"type":"string","description":"The current block number."},"gasPrice":{"type":"string","description":"The gas price in terms of wei for pre EIP1559 transactions."},"maxFeePerGas":{"type":"string","description":"The maximum fee per gas in terms of wei for EIP1559 transactions."},"maxPriorityFeePerGas":{"type":"string","description":"The maximum priority fee per gas in terms of wei for EIP1559 transactions."},"txFailureReasons":{"type":"array","items":{"$ref":"#/components/schemas/TransactionFailureReason"}},"priceImpact":{"type":"number","description":"The impact the trade has on the market price of the pool, between 0-100 percent"}}},"WrapUnwrapQuote":{"type":"object","properties":{"swapper":{"$ref":"#/components/schemas/Address"},"input":{"$ref":"#/components/schemas/ClassicInput"},"output":{"$ref":"#/components/schemas/ClassicOutput"},"chainId":{"$ref":"#/components/schemas/ChainId"},"tradeType":{"$ref":"#/components/schemas/TradeType"},"gasFee":{"type":"string","description":"The gas fee in terms of wei."},"gasFeeUSD":{"type":"string","description":"The gas fee in terms of USD."},"gasFeeQuote":{"type":"string","description":"The gas fee in terms of the quoted currency."},"gasUseEstimate":{"type":"string","description":"The estimated gas use."},"gasPrice":{"type":"string","description":"The gas price in terms of wei for pre EIP1559 transactions."},"maxFeePerGas":{"type":"string","description":"The maximum fee per gas in terms of wei for EIP1559 transactions."},"maxPriorityFeePerGas":{"type":"string","description":"The maximum priority fee per gas in terms of wei for EIP1559 transactions."}}},"TokenInRoute":{"type":"object","properties":{"address":{"$ref":"#/components/schemas/Address"},"chainId":{"$ref":"#/components/schemas/ChainId"},"symbol":{"type":"string"},"decimals":{"type":"string"},"buyFeeBps":{"type":"string"},"sellFeeBps":{"type":"string"}}},"V2Reserve":{"type":"object","properties":{"token":{"$ref":"#/components/schemas/TokenInRoute"},"quotient":{"type":"string"}}},"V2PoolInRoute":{"type":"object","properties":{"type":{"type":"string","default":"v2-pool"},"address":{"$ref":"#/components/schemas/Address"},"tokenIn":{"$ref":"#/components/schemas/TokenInRoute"},"tokenOut":{"$ref":"#/components/schemas/TokenInRoute"},"reserve0":{"$ref":"#/components/schemas/V2Reserve"},"reserve1":{"$ref":"#/components/schemas/V2Reserve"},"amountIn":{"type":"string"},"amountOut":{"type":"string"}}},"V3PoolInRoute":{"type":"object","properties":{"type":{"type":"string","default":"v3-pool"},"address":{"$ref":"#/components/schemas/Address"},"tokenIn":{"$ref":"#/components/schemas/TokenInRoute"},"tokenOut":{"$ref":"#/components/schemas/TokenInRoute"},"sqrtRatioX96":{"type":"string"},"liquidity":{"type":"string"},"tickCurrent":{"type":"string"},"fee":{"type":"string"},"amountIn":{"type":"string"},"amountOut":{"type":"string"}}},"TransactionHash":{"type":"string","pattern":"^(0x)?[0-9a-fA-F]{64}$"},"ClassicInput":{"type":"object","properties":{"token":{"$ref":"#/components/schemas/Address"},"amount":{"type":"string"}}},"ClassicOutput":{"type":"object","properties":{"token":{"$ref":"#/components/schemas/Address"},"amount":{"type":"string"},"recipient":{"$ref":"#/components/schemas/Address"}}},"RequestId":{"type":"string"},"SpreadOptimization":{"type":"string","enum":["EXECUTION","PRICE"],"description":"For **Dutch Limit** orders only. When set to `EXECUTION`, quotes optimize for looser spreads at higher fill rates. When set to `PRICE`, quotes optimize for tighter spreads at lower fill rates","default":"EXECUTION"},"AutoSlippage":{"type":"string","enum":["DEFAULT"],"description":"For **Classic** swaps only. The auto slippage strategy to employ. If auto slippage is not defined then we don't compute it. If the auto slippage strategy is `DEFAULT`, then the swap will use the default slippage tolerance computation. You cannot define auto slippage and slippage tolerance at the same time. \n\n **NOTE**: slippage is in terms of trade type. If the trade type is `EXACT_INPUT`, then the slippage is in terms of the output token. If the trade type is `EXACT_OUTPUT`, then the slippage is in terms of the input token.","default":"undefined"},"RoutingPreference":{"type":"string","description":"The routing preference determines which protocol to use for the swap. If the routing preference is `UNISWAPX`, then the swap will be routed through the UniswapX Dutch Auction Protocol. If the routing preference is `CLASSIC`, then the swap will be routed through the Classic Protocol. If the routing preference is `BEST_PRICE`, then the swap will be routed through the protocol that provides the best price. When `UNIXWAPX_V2` is passed, the swap will be routed through the UniswapX V2 Dutch Auction Protocol. When `V3_ONLY` is passed, the swap will be routed ONLY through the Uniswap V3 Protocol. When `V2_ONLY` is passed, the swap will be routed ONLY through the Uniswap V2 Protocol.","enum":["CLASSIC","UNISWAPX","BEST_PRICE","BEST_PRICE_V2","UNISWAPX_V2","V3_ONLY","V2_ONLY"],"default":"BEST_PRICE"},"TransactionRequest":{"type":"object","properties":{"to":{"$ref":"#/components/schemas/Address"},"from":{"$ref":"#/components/schemas/Address"},"data":{"type":"string","description":"The calldata for the transaction."},"value":{"type":"string","description":"The value of the transaction in terms of wei in hex format."},"gasLimit":{"type":"string"},"chainId":{"type":"integer"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"gasPrice":{"type":"string"}},"required":["to","from","data","value","chainId"]},"TransactionFailureReason":{"type":"string","enum":["SIMULATION_ERROR"]},"SwapSafetyMode":{"type":"string","enum":["SAFE"],"description":"The safety mode determines the safety level of the swap. If the safety mode is `SAFE`, then the swap will include a SWEEP for the native token."}},"parameters":{"addressParam":{"name":"address","in":"path","schema":{"$ref":"#/components/schemas/Address"},"required":true},"tokenIdParam":{"name":"tokenId","in":"path","schema":{"type":"string"},"required":true},"cursorParam":{"name":"cursor","in":"query","schema":{"type":"string"},"required":false},"limitParam":{"name":"limit","in":"query","schema":{"type":"number"},"required":false},"chainParam":{"name":"chain","in":"query","schema":{"$ref":"#/components/schemas/ChainId"},"required":false},"addressPathParam":{"name":"address","in":"query","schema":{"$ref":"#/components/schemas/Address"},"required":false},"orderStatusParam":{"name":"orderStatus","in":"query","description":"Filter by order status.","required":false,"schema":{"$ref":"#/components/schemas/OrderStatus"}},"orderIdParam":{"name":"orderId","in":"query","required":false,"schema":{"$ref":"#/components/schemas/OrderId"}},"orderIdsParam":{"name":"orderIds","in":"query","required":false,"description":"ids split by commas","schema":{"$ref":"#/components/schemas/OrderIds"}},"swapperParam":{"name":"swapper","in":"query","description":"Filter by swapper address.","required":false,"schema":{"$ref":"#/components/schemas/Address"}},"fillerParam":{"name":"filler","in":"query","description":"Filter by filler address.","required":false,"schema":{"$ref":"#/components/schemas/Address"}},"sortKeyParam":{"name":"sortKey","in":"query","description":"Order the query results by the sort key.","required":false,"schema":{"$ref":"#/components/schemas/SortKey"}},"sortParam":{"name":"sort","in":"query","description":"Sort query. For example: `sort=gt(UNIX_TIMESTAMP)`, `sort=between(1675872827, 1675872930)`, or `lt(1675872930)`.","required":false,"schema":{"type":"string"}},"descParam":{"description":"Sort query results by sortKey in descending order.","name":"desc","in":"query","required":false,"schema":{"type":"string"}},"transactionHashParam":{"description":"The transaction hash.","name":"txHash","in":"path","required":true,"schema":{"$ref":"#/components/schemas/TransactionHash"}}},"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key"}}},"security":[{"apiKey":[]}]} \ No newline at end of file +{"openapi":"3.0.0","servers":[{"description":"Uniswap trading APIs Beta","url":"https://beta.trade-api.gateway.uniswap.org/v1"},{"description":"Uniswap trading APIs","url":"https://trade-api.gateway.uniswap.org/v1"}],"info":{"version":"1.0.0","title":"Token Trading","description":"Uniswap trading APIs for fungible tokens."},"paths":{"/check_approval":{"post":{"tags":["Approval"],"summary":"Check if token approval is required","description":"Checks if the swapper has the required approval. If the swapper does not have the required approval, then the response will include the transaction to approve the token. If the swapper has the required approval, then the response will be empty. If the parameter `includeGasInfo` is set to `true`, then the response will include the gas fee for the approval transaction.","operationId":"check_approval","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalRequest"}}}},"responses":{"200":{"$ref":"#/components/responses/ApprovalSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"401":{"$ref":"#/components/responses/ApprovalUnauthorized401"},"404":{"$ref":"#/components/responses/ApprovalNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/quote":{"post":{"tags":["Quote"],"summary":"Get a quote","description":"Get a quote according to the provided configuration. Optionally adds a fee to the quote according to the API key being used. The fee is **ALWAYS** taken from the output token. If there is a fee and the trade is `EXACT_INPUT`, then the output amount will **NOT** include the fee subtraction. For `EXACT_INPUT` swaps, use `portionBips` to calculate the fee from the quoted amount. If there is a fee and the trade is `EXACT_OUTPUT`, then the input amount will **NOT** include the fee addition to account for the fee. For `EXACT_OUTPUT` swaps, use `portionAmount` to get the fee. \n \n We also support Wrapping and Unwrapping of native tokens on their respective chains. Wrapping and Unwrapping only works for when `routingPreference` is `CLASSIC`, `BEST_PRICE`, or `BEST_PRICE_V2`. We do not support `UNISWAPX` or `UNISWAPX_V2` for these actions.","operationId":"aggregator_quote","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequest"}}}},"responses":{"200":{"$ref":"#/components/responses/QuoteSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"401":{"$ref":"#/components/responses/QuoteUnauthorized401"},"404":{"$ref":"#/components/responses/QuoteNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/order":{"post":{"tags":["Order"],"summary":"Create a gasless order","description":"Submits a new gasless encoded order. The order will be validated and if valid, will be submitted to the filler network. The network will try to fill the order at the quoted `startAmount`, and if not, the amount will start decaying until the `endAmount` is reached. While the order is within `decayEndTime`, the `orderStatus` is `open`. If the order does not get filled after the `decayEndTime` has passed, that is reflected in the `expired` `orderStatus`. then The order will be filled at the best price possible. Once the order is filled, `orderStatus` becomes `filled`.","operationId":"post_dutch_order","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderRequest"}}}},"responses":{"201":{"$ref":"#/components/responses/OrderSuccess201"},"400":{"$ref":"#/components/responses/BadRequest400"},"401":{"$ref":"#/components/responses/OrderUnauthorized401"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/orders":{"get":{"tags":["Order"],"summary":"Get gasless orders","description":"Retrieve gasless orders filtered by query param(s). Some fields on the order can be used as query param.","operationId":"get_dutch_order","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/orderIdParam"},{"$ref":"#/components/parameters/orderIdsParam"},{"$ref":"#/components/parameters/limitParam"},{"$ref":"#/components/parameters/orderStatusParam"},{"$ref":"#/components/parameters/swapperParam"},{"$ref":"#/components/parameters/sortKeyParam"},{"$ref":"#/components/parameters/sortParam"},{"$ref":"#/components/parameters/fillerParam"},{"$ref":"#/components/parameters/cursorParam"}],"responses":{"200":{"$ref":"#/components/responses/OrdersSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"404":{"$ref":"#/components/responses/OrdersNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/swap":{"post":{"tags":["Swap"],"summary":"Create swap calldata","description":"Create the calldata for a swap transaction (including wrap/unwrap) against the Uniswap Protocols. If the `quote` parameter includes the fee parameters, then the calldata will include the fee disbursement. The gas estimates will be **more precise** when the the response calldata would be valid if submitted on-chain.","operationId":"create_swap_transaction","security":[{"apiKey":[]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSwapRequest"}}}},"responses":{"200":{"$ref":"#/components/responses/CreateSwapSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"401":{"$ref":"#/components/responses/SwapUnauthorized401"},"404":{"$ref":"#/components/responses/SwapNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}},"/swap/{txHash}":{"get":{"tags":["Swap"],"summary":"Get swap status","description":"Get the status of a swap transaction.","operationId":"get_swap_transaction","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/transactionHashParam"}],"responses":{"200":{"$ref":"#/components/responses/GetSwapSuccess200"},"400":{"$ref":"#/components/responses/BadRequest400"},"404":{"$ref":"#/components/responses/SwapNotFound404"},"419":{"$ref":"#/components/responses/RateLimitedErr419"},"500":{"$ref":"#/components/responses/InternalErr500"},"504":{"$ref":"#/components/responses/Timeout504"}}}}},"components":{"responses":{"OrdersSuccess200":{"description":"The request orders matching the query parameters.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrdersResponse"}}}},"OrderSuccess201":{"description":"Encoded order submitted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderResponse"}}}},"QuoteSuccess200":{"description":"Quote request successful.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponse"}}}},"ApprovalSuccess200":{"description":"Check approval successful.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApprovalResponse"}}}},"CreateSwapSuccess200":{"description":"Create swap successful.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSwapResponse"}}}},"GetSwapSuccess200":{"description":"Get swap successful.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSwapResponse"}}}},"BadRequest400":{"description":"RequestValidationError, Bad Input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err400"}}}},"ApprovalUnauthorized401":{"description":"UnauthorizedError eg. Account is blocked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err401"}}}},"ApprovalNotFound404":{"description":"ResourceNotFound eg. Token allowance not found or Gas info not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err404"}}}},"QuoteUnauthorized401":{"description":"UnauthorizedError eg. Account is blocked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err401"}}}},"QuoteNotFound404":{"description":"ResourceNotFound eg. No quotes available or Gas fee/price not available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err404"}}}},"SwapBadRequest400":{"description":"RequestValidationError, Bad Input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err400"}}}},"SwapUnauthorized401":{"description":"UnauthorizedError eg. Account is blocked or Fee is not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err401"}}}},"SwapNotFound404":{"description":"ResourceNotFound eg. No quotes available or Gas fee/price not available","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err404"}}}},"OrderUnauthorized401":{"description":"UnauthorizedError eg. Account is blocked.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err401"}}}},"OrdersNotFound404":{"description":"Orders not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err404"}}}},"RateLimitedErr419":{"description":"Ratelimited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err419"}}}},"InternalErr500":{"description":"Unexpected error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err500"}}}},"Timeout504":{"description":"Request duration limit reached.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Err504"}}}}},"schemas":{"NullablePermit":{"allOf":[{"$ref":"#/components/schemas/Permit"},{"type":"object","nullable":true}]},"TokenAmount":{"type":"string"},"SwapStatus":{"type":"string","enum":["pending","success","error"]},"GetSwapResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"status":{"$ref":"#/components/schemas/SwapStatus"}},"required":["requestId","status"]},"CreateSwapRequest":{"type":"object","description":"The parameters **signature** and **permitData** should only be included if *permitData* was returned from **/quote**.","properties":{"quote":{"oneOf":[{"$ref":"#/components/schemas/ClassicQuote"},{"$ref":"#/components/schemas/WrapUnwrapQuote"}]},"signature":{"type":"string","description":"The signed permit."},"includeGasInfo":{"type":"boolean","default":false,"deprecated":true,"description":"Use `refreshGasPrice` instead."},"refreshGasPrice":{"type":"boolean","default":false,"description":"If true, the gas price will be re-fetched from the network."},"simulateTransaction":{"type":"boolean","default":false,"description":"If true, the transaction will be simulated. If the simulation results on an onchain error, endpoint will return an error."},"permitData":{"allOf":[{"$ref":"#/components/schemas/Permit"}]},"safetyMode":{"$ref":"#/components/schemas/SwapSafetyMode"},"deadline":{"type":"integer","description":"The deadline for the swap in unix timestamp format. If the deadline is not defined OR in the past then the default deadline is 30 minutes."},"urgency":{"$ref":"#/components/schemas/Urgency"}},"required":["quote"]},"Address":{"type":"string","pattern":"^(0x)?[0-9a-fA-F]{40}$"},"CreateSwapResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"swap":{"$ref":"#/components/schemas/TransactionRequest"},"gasFee":{"type":"string"}},"required":["requestId","swap"]},"QuoteResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"quote":{"$ref":"#/components/schemas/Quote"},"routing":{"$ref":"#/components/schemas/Routing"},"permitData":{"$ref":"#/components/schemas/NullablePermit"}},"required":["routing","quote","permitData","requestId"]},"QuoteRequest":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/TradeType"},"amount":{"type":"string"},"tokenInChainId":{"$ref":"#/components/schemas/ChainId"},"tokenOutChainId":{"$ref":"#/components/schemas/ChainId"},"tokenIn":{"type":"string"},"tokenOut":{"type":"string"},"swapper":{"$ref":"#/components/schemas/Address"},"slippageTolerance":{"description":"For **Classic** swaps, the slippage tolerance is the maximum amount the price can change between the time the transaction is submitted and the time it is executed. The slippage tolerance is represented as a percentage of the total value of the swap. \n\n Slippage tolerance works differently in **DutchLimit** swaps, it does not set a limit on the Spread in an order. See [here](https://uniswap-docs.readme.io/reference/faqs#why-do-the-uniswapx-quotes-have-more-slippage-than-the-tolerance-i-set) for more information. \n\n **NOTE**: slippage is in terms of trade type. If the trade type is `EXACT_INPUT`, then the slippage is in terms of the output token. If the trade type is `EXACT_OUTPUT`, then the slippage is in terms of the input token.","type":"number"},"autoSlippage":{"$ref":"#/components/schemas/AutoSlippage"},"routingPreference":{"$ref":"#/components/schemas/RoutingPreference"},"spreadOptimization":{"$ref":"#/components/schemas/SpreadOptimization"},"urgency":{"$ref":"#/components/schemas/Urgency"}},"required":["type","amount","tokenInChainId","tokenOutChainId","tokenIn","tokenOut","swapper"]},"GetOrdersResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/UniswapXOrder"}},"cursor":{"type":"string"}},"required":["orders","requestId"]},"OrderResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"orderId":{"type":"string"},"orderStatus":{"$ref":"#/components/schemas/OrderStatus"}},"required":["requestId","orderId","orderStatus"]},"OrderRequest":{"type":"object","properties":{"signature":{"type":"string","description":"The signed permit."},"quote":{"oneOf":[{"$ref":"#/components/schemas/DutchQuote"},{"$ref":"#/components/schemas/DutchQuoteV2"}]},"routing":{"$ref":"#/components/schemas/Routing"}},"required":["signature","quote"]},"Urgency":{"type":"string","enum":["normal","fast","urgent"],"description":"The urgency determines the urgency of the transaction. The default value is `urgent`.","default":"urgent"},"Err400":{"type":"object","properties":{"errorCode":{"default":"RequestValidationError","type":"string"},"detail":{"type":"string"}}},"Err401":{"type":"object","properties":{"errorCode":{"default":"UnauthorizedError","type":"string"},"detail":{"type":"string"}}},"Err404":{"type":"object","properties":{"errorCode":{"default":"ResourceNotFound","type":"string"},"detail":{"type":"string"}}},"Err419":{"type":"object","properties":{"errorCode":{"default":"Ratelimited","type":"string"},"detail":{"type":"string"}}},"Err500":{"type":"object","properties":{"errorCode":{"default":"InternalServerError","type":"string"},"detail":{"type":"string"}}},"Err504":{"type":"object","properties":{"errorCode":{"default":"Timeout","type":"string"},"detail":{"type":"string"}}},"ChainId":{"type":"number","enum":[1,10,56,137,8453,42161,81457,43114,42220,7777777,324]},"OrderInput":{"type":"object","properties":{"token":{"type":"string"},"startAmount":{"type":"string"},"endAmount":{"type":"string"}},"required":["token"]},"OrderOutput":{"type":"object","properties":{"token":{"type":"string"},"startAmount":{"type":"string"},"endAmount":{"type":"string"},"isFeeOutput":{"type":"boolean"},"recipient":{"type":"string"}},"required":["token"]},"CosignerData":{"type":"object","properties":{"decayStartTime":{"type":"number"},"decayEndTime":{"type":"number"},"exclusiveFiller":{"type":"string"},"inputOverride":{"type":"string"},"outputOverrides":{"type":"array","items":{"type":"string"}}}},"SettledAmount":{"type":"object","properties":{"tokenOut":{"$ref":"#/components/schemas/Address"},"amountOut":{"type":"string"},"tokenIn":{"$ref":"#/components/schemas/Address"},"amountIn":{"type":"string"}}},"OrderType":{"type":"string","enum":["DutchLimit","Dutch","Dutch_V2"]},"UniswapXOrder":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/OrderType"},"encodedOrder":{"type":"string"},"signature":{"type":"string"},"nonce":{"type":"string"},"orderStatus":{"$ref":"#/components/schemas/OrderStatus"},"orderId":{"type":"string"},"chainId":{"$ref":"#/components/schemas/ChainId"},"quoteId":{"type":"string"},"swapper":{"type":"string"},"txHash":{"type":"string"},"input":{"$ref":"#/components/schemas/OrderInput"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/OrderOutput"}},"settledAmounts":{"type":"array","items":{"$ref":"#/components/schemas/SettledAmount"}},"cosignature":{"type":"string"},"cosignerData":{"$ref":"#/components/schemas/CosignerData"}},"required":["encodedOrder","signature","nonce","orderId","orderStatus","chainId","type"]},"SortKey":{"type":"string","enum":["createdAt"]},"OrderId":{"type":"string"},"OrderIds":{"type":"string"},"OrderStatus":{"type":"string","enum":["open","expired","error","cancelled","filled","unverified","insufficient-funds"]},"Permit":{"type":"object","properties":{"domain":{"type":"object"},"values":{"type":"object"},"types":{"type":"object"}}},"DutchInput":{"type":"object","properties":{"startAmount":{"type":"string"},"endAmount":{"type":"string"},"token":{"type":"string"}},"required":["startAmount","endAmount","type"]},"DutchOutput":{"type":"object","properties":{"startAmount":{"type":"string"},"endAmount":{"type":"string"},"token":{"type":"string"},"recipient":{"type":"string"}},"required":["startAmount","endAmount","token","recipient"]},"DutchOrderInfo":{"type":"object","properties":{"chainId":{"$ref":"#/components/schemas/ChainId"},"nonce":{"type":"string"},"reactor":{"type":"string"},"swapper":{"type":"string"},"deadline":{"type":"number"},"additionalValidationContract":{"type":"string"},"additionalValidationData":{"type":"string"},"decayStartTime":{"type":"number"},"decayEndTime":{"type":"number"},"exclusiveFiller":{"type":"string"},"exclusivityOverrideBps":{"type":"string"},"input":{"$ref":"#/components/schemas/DutchInput"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/DutchOutput"}}},"required":["chainId","nonce","reactor","swapper","deadline","validationContract","validationData","startTime","endTime","exclusiveFiller","exclusivityOverrideBps","input","outputs"]},"DutchOrderInfoV2":{"type":"object","properties":{"chainId":{"$ref":"#/components/schemas/ChainId"},"nonce":{"type":"string"},"reactor":{"type":"string"},"swapper":{"type":"string"},"deadline":{"type":"number"},"additionalValidationContract":{"type":"string"},"additionalValidationData":{"type":"string"},"input":{"$ref":"#/components/schemas/DutchInput"},"outputs":{"type":"array","items":{"$ref":"#/components/schemas/DutchOutput"}},"cosigner":{"$ref":"#/components/schemas/Address"}},"required":["chainId","nonce","reactor","swapper","deadline","validationContract","validationData","startTime","endTime","exclusiveFiller","exclusivityOverrideBps","input","outputs"]},"DutchQuote":{"type":"object","properties":{"encodedOrder":{"type":"string"},"orderId":{"type":"string"},"orderInfo":{"$ref":"#/components/schemas/DutchOrderInfo"},"portionBips":{"type":"number"},"portionAmount":{"type":"string"},"portionRecipient":{"$ref":"#/components/schemas/Address"},"quoteId":{"type":"string"},"slippageTolerance":{"type":"number"}},"required":["encodedOrder","orderInfo","orderId"]},"DutchQuoteV2":{"type":"object","properties":{"encodedOrder":{"type":"string"},"orderId":{"type":"string"},"orderInfo":{"$ref":"#/components/schemas/DutchOrderInfoV2"},"portionBips":{"type":"number"},"portionAmount":{"type":"string"},"portionRecipient":{"$ref":"#/components/schemas/Address"},"quoteId":{"type":"string"},"slippageTolerance":{"type":"number"},"deadlineBufferSecs":{"type":"number"}},"required":["encodedOrder","orderInfo","orderId"]},"TradeType":{"type":"string","enum":["EXACT_INPUT","EXACT_OUTPUT"]},"Routing":{"type":"string","enum":["DUTCH_LIMIT","CLASSIC","DUTCH_V2"]},"Quote":{"oneOf":[{"$ref":"#/components/schemas/DutchQuote"},{"$ref":"#/components/schemas/ClassicQuote"},{"$ref":"#/components/schemas/WrapUnwrapQuote"},{"$ref":"#/components/schemas/DutchQuoteV2"}]},"ApprovalRequest":{"type":"object","properties":{"walletAddress":{"$ref":"#/components/schemas/Address"},"token":{"$ref":"#/components/schemas/Address"},"amount":{"$ref":"#/components/schemas/TokenAmount"},"chainId":{"$ref":"#/components/schemas/ChainId"},"urgency":{"$ref":"#/components/schemas/Urgency"},"includeGasInfo":{"type":"boolean","default":false},"tokenOut":{"type":"string","pattern":"^(0x)?[0-9a-fA-F]{40}$","description":"relevant for if we go from a wrapped token to a native token (unwrapping)"},"tokenOutChainId":{"type":"number","enum":[1,10,56,137,8453,42161,81457,43114,42220,7777777,324],"default":1,"description":"relevant for if we go from a wrapped token to a native token (unwrapping)"}},"required":["walletAddress","token","amount"]},"ApprovalResponse":{"type":"object","properties":{"requestId":{"$ref":"#/components/schemas/RequestId"},"approval":{"$ref":"#/components/schemas/TransactionRequest"},"gasFee":{"type":"string"}},"required":["requestId","approval"]},"ClassicQuote":{"type":"object","properties":{"input":{"$ref":"#/components/schemas/ClassicInput"},"output":{"$ref":"#/components/schemas/ClassicOutput"},"swapper":{"$ref":"#/components/schemas/Address"},"chainId":{"$ref":"#/components/schemas/ChainId"},"slippage":{"type":"number"},"tradeType":{"$ref":"#/components/schemas/TradeType"},"gasFee":{"type":"string","description":"The gas fee in terms of wei. It does NOT include the additional gas for token approvals."},"gasFeeUSD":{"type":"string","description":"The gas fee in terms of USD. It does NOT include the additional gas for token approvals."},"gasFeeQuote":{"type":"string","description":"The gas fee in terms of the quoted currency. It does NOT include the additional gas for token approvals."},"route":{"type":"array","items":{"type":"array","items":{"oneOf":[{"$ref":"#/components/schemas/V3PoolInRoute"},{"$ref":"#/components/schemas/V2PoolInRoute"}]}}},"portionBips":{"type":"number","description":"The portion of the swap that will be taken as a fee. The fee will be taken from the output token."},"portionAmount":{"type":"string","description":"The amount of the swap that will be taken as a fee. The fee will be taken from the output token."},"portionRecipient":{"$ref":"#/components/schemas/Address"},"routeString":{"type":"string","description":"The route in string format."},"quoteId":{"type":"string","description":"The quote id. Used for analytics purposes."},"gasUseEstimate":{"type":"string","description":"The estimated gas use. It does NOT include the additional gas for token approvals."},"blockNumber":{"type":"string","description":"The current block number."},"gasPrice":{"type":"string","description":"The gas price in terms of wei for pre EIP1559 transactions."},"maxFeePerGas":{"type":"string","description":"The maximum fee per gas in terms of wei for EIP1559 transactions."},"maxPriorityFeePerGas":{"type":"string","description":"The maximum priority fee per gas in terms of wei for EIP1559 transactions."},"txFailureReasons":{"type":"array","items":{"$ref":"#/components/schemas/TransactionFailureReason"}},"priceImpact":{"type":"number","description":"The impact the trade has on the market price of the pool, between 0-100 percent"}}},"WrapUnwrapQuote":{"type":"object","properties":{"swapper":{"$ref":"#/components/schemas/Address"},"input":{"$ref":"#/components/schemas/ClassicInput"},"output":{"$ref":"#/components/schemas/ClassicOutput"},"chainId":{"$ref":"#/components/schemas/ChainId"},"tradeType":{"$ref":"#/components/schemas/TradeType"},"gasFee":{"type":"string","description":"The gas fee in terms of wei."},"gasFeeUSD":{"type":"string","description":"The gas fee in terms of USD."},"gasFeeQuote":{"type":"string","description":"The gas fee in terms of the quoted currency."},"gasUseEstimate":{"type":"string","description":"The estimated gas use."},"gasPrice":{"type":"string","description":"The gas price in terms of wei for pre EIP1559 transactions."},"maxFeePerGas":{"type":"string","description":"The maximum fee per gas in terms of wei for EIP1559 transactions."},"maxPriorityFeePerGas":{"type":"string","description":"The maximum priority fee per gas in terms of wei for EIP1559 transactions."}}},"TokenInRoute":{"type":"object","properties":{"address":{"$ref":"#/components/schemas/Address"},"chainId":{"$ref":"#/components/schemas/ChainId"},"symbol":{"type":"string"},"decimals":{"type":"string"},"buyFeeBps":{"type":"string"},"sellFeeBps":{"type":"string"}}},"V2Reserve":{"type":"object","properties":{"token":{"$ref":"#/components/schemas/TokenInRoute"},"quotient":{"type":"string"}}},"V2PoolInRoute":{"type":"object","properties":{"type":{"type":"string","default":"v2-pool"},"address":{"$ref":"#/components/schemas/Address"},"tokenIn":{"$ref":"#/components/schemas/TokenInRoute"},"tokenOut":{"$ref":"#/components/schemas/TokenInRoute"},"reserve0":{"$ref":"#/components/schemas/V2Reserve"},"reserve1":{"$ref":"#/components/schemas/V2Reserve"},"amountIn":{"type":"string"},"amountOut":{"type":"string"}}},"V3PoolInRoute":{"type":"object","properties":{"type":{"type":"string","default":"v3-pool"},"address":{"$ref":"#/components/schemas/Address"},"tokenIn":{"$ref":"#/components/schemas/TokenInRoute"},"tokenOut":{"$ref":"#/components/schemas/TokenInRoute"},"sqrtRatioX96":{"type":"string"},"liquidity":{"type":"string"},"tickCurrent":{"type":"string"},"fee":{"type":"string"},"amountIn":{"type":"string"},"amountOut":{"type":"string"}}},"TransactionHash":{"type":"string","pattern":"^(0x)?[0-9a-fA-F]{64}$"},"ClassicInput":{"type":"object","properties":{"token":{"$ref":"#/components/schemas/Address"},"amount":{"type":"string"}}},"ClassicOutput":{"type":"object","properties":{"token":{"$ref":"#/components/schemas/Address"},"amount":{"type":"string"},"recipient":{"$ref":"#/components/schemas/Address"}}},"RequestId":{"type":"string"},"SpreadOptimization":{"type":"string","enum":["EXECUTION","PRICE"],"description":"For **Dutch Limit** orders only. When set to `EXECUTION`, quotes optimize for looser spreads at higher fill rates. When set to `PRICE`, quotes optimize for tighter spreads at lower fill rates","default":"EXECUTION"},"AutoSlippage":{"type":"string","enum":["DEFAULT"],"description":"For **Classic** swaps only. The auto slippage strategy to employ. If auto slippage is not defined then we don't compute it. If the auto slippage strategy is `DEFAULT`, then the swap will use the default slippage tolerance computation. You cannot define auto slippage and slippage tolerance at the same time. \n\n **NOTE**: slippage is in terms of trade type. If the trade type is `EXACT_INPUT`, then the slippage is in terms of the output token. If the trade type is `EXACT_OUTPUT`, then the slippage is in terms of the input token.","default":"undefined"},"RoutingPreference":{"type":"string","description":"The routing preference determines which protocol to use for the swap. If the routing preference is `UNISWAPX`, then the swap will be routed through the UniswapX Dutch Auction Protocol. If the routing preference is `CLASSIC`, then the swap will be routed through the Classic Protocol. If the routing preference is `BEST_PRICE`, then the swap will be routed through the protocol that provides the best price. When `UNIXWAPX_V2` is passed, the swap will be routed through the UniswapX V2 Dutch Auction Protocol. When `V3_ONLY` is passed, the swap will be routed ONLY through the Uniswap V3 Protocol. When `V2_ONLY` is passed, the swap will be routed ONLY through the Uniswap V2 Protocol.","enum":["CLASSIC","UNISWAPX","BEST_PRICE","BEST_PRICE_V2","UNISWAPX_V2","V3_ONLY","V2_ONLY"],"default":"BEST_PRICE"},"TransactionRequest":{"type":"object","properties":{"to":{"$ref":"#/components/schemas/Address"},"from":{"$ref":"#/components/schemas/Address"},"data":{"type":"string","description":"The calldata for the transaction."},"value":{"type":"string","description":"The value of the transaction in terms of wei in hex format."},"gasLimit":{"type":"string"},"chainId":{"type":"integer"},"maxFeePerGas":{"type":"string"},"maxPriorityFeePerGas":{"type":"string"},"gasPrice":{"type":"string"}},"required":["to","from","data","value","chainId"]},"TransactionFailureReason":{"type":"string","enum":["SIMULATION_ERROR","UNSUPPORTED_SIMULATION"]},"SwapSafetyMode":{"type":"string","enum":["SAFE"],"description":"The safety mode determines the safety level of the swap. If the safety mode is `SAFE`, then the swap will include a SWEEP for the native token."}},"parameters":{"addressParam":{"name":"address","in":"path","schema":{"$ref":"#/components/schemas/Address"},"required":true},"tokenIdParam":{"name":"tokenId","in":"path","schema":{"type":"string"},"required":true},"cursorParam":{"name":"cursor","in":"query","schema":{"type":"string"},"required":false},"limitParam":{"name":"limit","in":"query","schema":{"type":"number"},"required":false},"chainParam":{"name":"chain","in":"query","schema":{"$ref":"#/components/schemas/ChainId"},"required":false},"addressPathParam":{"name":"address","in":"query","schema":{"$ref":"#/components/schemas/Address"},"required":false},"orderStatusParam":{"name":"orderStatus","in":"query","description":"Filter by order status.","required":false,"schema":{"$ref":"#/components/schemas/OrderStatus"}},"orderIdParam":{"name":"orderId","in":"query","required":false,"schema":{"$ref":"#/components/schemas/OrderId"}},"orderIdsParam":{"name":"orderIds","in":"query","required":false,"description":"ids split by commas","schema":{"$ref":"#/components/schemas/OrderIds"}},"swapperParam":{"name":"swapper","in":"query","description":"Filter by swapper address.","required":false,"schema":{"$ref":"#/components/schemas/Address"}},"fillerParam":{"name":"filler","in":"query","description":"Filter by filler address.","required":false,"schema":{"$ref":"#/components/schemas/Address"}},"sortKeyParam":{"name":"sortKey","in":"query","description":"Order the query results by the sort key.","required":false,"schema":{"$ref":"#/components/schemas/SortKey"}},"sortParam":{"name":"sort","in":"query","description":"Sort query. For example: `sort=gt(UNIX_TIMESTAMP)`, `sort=between(1675872827, 1675872930)`, or `lt(1675872930)`.","required":false,"schema":{"type":"string"}},"descParam":{"description":"Sort query results by sortKey in descending order.","name":"desc","in":"query","required":false,"schema":{"type":"string"}},"transactionHashParam":{"description":"The transaction hash.","name":"txHash","in":"path","required":true,"schema":{"$ref":"#/components/schemas/TransactionHash"}}},"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key"}}},"security":[{"apiKey":[]}]} \ No newline at end of file diff --git a/packages/wallet/src/data/utils.ts b/packages/wallet/src/data/utils.ts index ba715f1f87f..9c4f1b52605 100644 --- a/packages/wallet/src/data/utils.ts +++ b/packages/wallet/src/data/utils.ts @@ -31,26 +31,22 @@ export function isError(networkStatus: NetworkStatus, hasData: boolean): boolean } export function useRefetchQueries(): ( - include?: Parameters['refetchQueries']>[0]['include'] + include?: Parameters['refetchQueries']>[0]['include'], ) => void { const client = useApolloClient() return useCallback( - async ( - include: Parameters< - ApolloClient['refetchQueries'] - >[0]['include'] = 'active' - ) => { + async (include: Parameters['refetchQueries']>[0]['include'] = 'active') => { await client?.refetchQueries({ include }) }, - [client] + [client], ) } export async function createSignedRequestBody( data: T, account: Account, - signerManager: SignerManager + signerManager: SignerManager, ): Promise<{ requestBody: T & AuthData; signature: string }> { const requestBody: T & AuthData = { ...data, @@ -65,7 +61,7 @@ export async function createSignedRequestBody( export async function createSignedRequestParams( params: T, account: Account, - signerManager: SignerManager + signerManager: SignerManager, ): Promise<{ requestParams: T & AuthData; signature: string }> { const requestParams: T & AuthData = { ...params, @@ -80,12 +76,12 @@ export async function createSignedRequestParams( export async function createOnRampTransactionsAuth( limit: number, account: Account, - signerManager: SignerManager + signerManager: SignerManager, ): Promise { const { requestParams, signature } = await createSignedRequestParams( { limit }, // Parameter needed by graphql server when fetching onramp transactions account, - signerManager + signerManager, ) return { queryParams: objectToQueryString(requestParams), signature } } diff --git a/packages/wallet/src/features/activity/hooks.ts b/packages/wallet/src/features/activity/hooks.ts index f486ff5bd7a..bd8ddbb0f50 100644 --- a/packages/wallet/src/features/activity/hooks.ts +++ b/packages/wallet/src/features/activity/hooks.ts @@ -1,17 +1,12 @@ import { ApolloError, NetworkStatus } from '@apollo/client' import { useCallback, useMemo } from 'react' +import { PollingInterval } from 'uniswap/src/constants/misc' import { useFeedTransactionListQuery, useTransactionListQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { PollingInterval } from 'wallet/src/constants/misc' import { isNonPollingRequestInFlight } from 'wallet/src/data/utils' -import { - LoadingItem, - SectionHeader, - isLoadingItem, - isSectionHeader, -} from 'wallet/src/features/activity/utils' +import { LoadingItem, SectionHeader, isLoadingItem, isSectionHeader } from 'wallet/src/features/activity/utils' import { usePersistedError } from 'wallet/src/features/dataApi/utils' import { useLocalizedDayjs } from 'wallet/src/features/language/localizedDayjs' import { @@ -27,7 +22,7 @@ const LOADING_DATA = [LOADING_ITEM(1), LOADING_ITEM(2), LOADING_ITEM(3), LOADING export function useFormattedTransactionDataForFeed( addresses: Address[], - hideSpamTokens: boolean + hideSpamTokens: boolean, ): { hasData: boolean isLoading: boolean @@ -74,7 +69,7 @@ export function useFormattedTransactionDataForFeed( const localizedDayjs = useLocalizedDayjs() const { pending, last24hTransactionList, priorByMonthTransactionList } = useMemo( () => formatTransactionsByDate(transactions, localizedDayjs), - [transactions, localizedDayjs] + [transactions, localizedDayjs], ) const hasTransactions = transactions && transactions.length > 0 @@ -84,8 +79,7 @@ export function useFormattedTransactionDataForFeed( const isError = usePersistedError(requestLoading, requestError) // show loading if no data and fetching, or refetching when there is error (for UX when "retry" is clicked). - const showLoading = - (!hasData && isLoading) || (Boolean(isError) && networkStatus === NetworkStatus.refetch) + const showLoading = (!hasData && isLoading) || (Boolean(isError) && networkStatus === NetworkStatus.refetch) const sectionData = useMemo(() => { if (showLoading) { @@ -108,7 +102,7 @@ export function useFormattedTransactionDataForFeed( } return accum }, - [] + [], ), ] }, [showLoading, hasTransactions, pending, last24hTransactionList, priorByMonthTransactionList]) @@ -127,8 +121,8 @@ export function useFormattedTransactionDataForActivity( hideSpamTokens: boolean, useMergeLocalFunction: ( address: Address, - remoteTransactions: TransactionDetails[] | undefined - ) => TransactionDetails[] | undefined + remoteTransactions: TransactionDetails[] | undefined, + ) => TransactionDetails[] | undefined, ): { hasData: boolean isLoading: boolean @@ -165,7 +159,7 @@ export function useFormattedTransactionDataForActivity( // for transactions, use the transaction hash as the key return info.id }, - [address] + [address], ) const formattedTransactions = useMemo(() => { @@ -182,7 +176,7 @@ export function useFormattedTransactionDataForActivity( const localizedDayjs = useLocalizedDayjs() const { pending, last24hTransactionList, priorByMonthTransactionList } = useMemo( () => formatTransactionsByDate(transactions, localizedDayjs), - [transactions, localizedDayjs] + [transactions, localizedDayjs], ) const hasTransactions = transactions && transactions.length > 0 @@ -192,8 +186,7 @@ export function useFormattedTransactionDataForActivity( const isError = usePersistedError(requestLoading, requestError) // show loading if no data and fetching, or refetching when there is error (for UX when "retry" is clicked). - const showLoading = - (!hasData && isLoading) || (Boolean(isError) && networkStatus === NetworkStatus.refetch) + const showLoading = (!hasData && isLoading) || (Boolean(isError) && networkStatus === NetworkStatus.refetch) const sectionData = useMemo(() => { if (showLoading) { @@ -216,7 +209,7 @@ export function useFormattedTransactionDataForActivity( } return accum }, - [] + [], ), ] }, [showLoading, hasTransactions, pending, last24hTransactionList, priorByMonthTransactionList]) diff --git a/packages/wallet/src/features/activity/useActivityData.tsx b/packages/wallet/src/features/activity/useActivityData.tsx index 725b32cebe5..c6d3e7708cf 100644 --- a/packages/wallet/src/features/activity/useActivityData.tsx +++ b/packages/wallet/src/features/activity/useActivityData.tsx @@ -10,14 +10,8 @@ import { LoadingItem, SectionHeader } from 'wallet/src/features/activity/utils' import { AuthTrigger } from 'wallet/src/features/auth/types' import TransactionSummaryLayout from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout' import { SwapSummaryCallbacks } from 'wallet/src/features/transactions/SummaryCards/types' -import { - ActivityItemRenderer, - generateActivityItemRenderer, -} from 'wallet/src/features/transactions/SummaryCards/utils' -import { - useCreateSwapFormState, - useMergeLocalAndRemoteTransactions, -} from 'wallet/src/features/transactions/hooks' +import { ActivityItemRenderer, generateActivityItemRenderer } from 'wallet/src/features/transactions/SummaryCards/utils' +import { useCreateSwapFormState, useMergeLocalAndRemoteTransactions } from 'wallet/src/features/transactions/hooks' import { useMostRecentSwapTx } from 'wallet/src/features/transactions/swap/hooks/useMostRecentSwapTx' import { TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { TransactionDetails } from 'wallet/src/features/transactions/types' @@ -81,16 +75,15 @@ export function useActivityData({ , SectionTitle, swapCallbacks, - authTrigger + authTrigger, ) }, [swapCallbacks, authTrigger]) - const { onRetry, hasData, isLoading, isError, sectionData, keyExtractor } = - useFormattedTransactionDataForActivity( - owner, - hideSpamTokens, - useMergeLocalAndRemoteTransactions - ) + const { onRetry, hasData, isLoading, isError, sectionData, keyExtractor } = useFormattedTransactionDataForActivity( + owner, + hideSpamTokens, + useMergeLocalAndRemoteTransactions, + ) const errorCard = ( @@ -105,9 +98,7 @@ export function useActivityData({ const emptyListView = ( (objectsWithAddress: T[]): T[] { // the input array must be objects that have an obj.address field // had to cast to any because ts doesn't recognize it as HasAddress... maybe issue with unique - return unique( - objectsWithAddress, - (v, i, a) => a.findIndex((v2) => v2.address === v.address) === i - ) + return unique(objectsWithAddress, (v, i, a) => a.findIndex((v2) => v2.address === v.address) === i) } diff --git a/packages/wallet/src/features/auth/saga.ts b/packages/wallet/src/features/auth/saga.ts index 961be7b7862..6456653ef05 100644 --- a/packages/wallet/src/features/auth/saga.ts +++ b/packages/wallet/src/features/auth/saga.ts @@ -1,11 +1,6 @@ import { call } from 'typed-redux-saga' import { logger } from 'utilities/src/logger/logger' -import { - AuthActionType, - AuthBaseParams, - AuthSagaError, - UnlockParams, -} from 'wallet/src/features/auth/types' +import { AuthActionType, AuthBaseParams, AuthSagaError, UnlockParams } from 'wallet/src/features/auth/types' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' import { createMonitoredSaga } from 'wallet/src/utils/saga' diff --git a/packages/wallet/src/features/auth/types.ts b/packages/wallet/src/features/auth/types.ts index 2dba010b526..a70c60c62e6 100644 --- a/packages/wallet/src/features/auth/types.ts +++ b/packages/wallet/src/features/auth/types.ts @@ -20,7 +20,4 @@ export interface LockParams extends AuthBaseParams { type: AuthActionType.Lock } -export type AuthTrigger = (args: { - successCallback: () => void - failureCallback: () => void -}) => Promise +export type AuthTrigger = (args: { successCallback: () => void; failureCallback: () => void }) => Promise diff --git a/packages/wallet/src/features/behaviorHistory/selectors.ts b/packages/wallet/src/features/behaviorHistory/selectors.ts index 5b934deb1e1..18ca836bc8a 100644 --- a/packages/wallet/src/features/behaviorHistory/selectors.ts +++ b/packages/wallet/src/features/behaviorHistory/selectors.ts @@ -1,11 +1,7 @@ -import { - ExtensionBetaFeedbackState, - ExtensionOnboardingState, -} from 'wallet/src/features/behaviorHistory/slice' +import { ExtensionBetaFeedbackState, ExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/slice' import { SharedState } from 'wallet/src/state/reducer' -export const selectHasViewedReviewScreen = (state: SharedState): boolean => - state.behaviorHistory.hasViewedReviewScreen +export const selectHasViewedReviewScreen = (state: SharedState): boolean => state.behaviorHistory.hasViewedReviewScreen export const selectHasSubmittedHoldToSwap = (state: SharedState): boolean => state.behaviorHistory.hasSubmittedHoldToSwap @@ -19,6 +15,5 @@ export const selectHasCompletedUnitagsIntroModal = (state: SharedState): boolean export const selectExtensionOnboardingState = (state: SharedState): ExtensionOnboardingState => state.behaviorHistory.extensionOnboardingState -export const selectExtensionBetaFeedbackState = ( - state: SharedState -): ExtensionBetaFeedbackState | undefined => state.behaviorHistory.extensionBetaFeedbackState +export const selectExtensionBetaFeedbackState = (state: SharedState): ExtensionBetaFeedbackState | undefined => + state.behaviorHistory.extensionBetaFeedbackState diff --git a/packages/wallet/src/features/chains/utils.test.ts b/packages/wallet/src/features/chains/utils.test.ts deleted file mode 100644 index dc7b3ac4a60..00000000000 --- a/packages/wallet/src/features/chains/utils.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { BigNumber } from 'ethers' -import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { UniverseChainId } from 'uniswap/src/types/chains' -import { PollingInterval } from 'wallet/src/constants/misc' -import { - chainIdToHexadecimalString, - fromGraphQLChain, - fromMoonpayNetwork, - fromUniswapWebAppLink, - getPollingIntervalByBlocktime, - hexadecimalStringToInt, - toSupportedChainId, - toUniswapWebAppLink, -} from 'wallet/src/features/chains/utils' - -describe(toSupportedChainId, () => { - it('handles undefined input', () => { - expect(toSupportedChainId(undefined)).toEqual(null) - }) - - it('handles unsupported chain ID', () => { - expect(toSupportedChainId(BigNumber.from(6767))).toEqual(null) - }) - - it('handles supported chain ID', () => { - expect(toSupportedChainId(UniverseChainId.Polygon)).toEqual(137) - }) -}) - -describe(fromGraphQLChain, () => { - it('handles undefined', () => { - expect(fromGraphQLChain(undefined)).toEqual(null) - }) - - it('handles supported chain', () => { - expect(fromGraphQLChain(Chain.Arbitrum)).toEqual(UniverseChainId.ArbitrumOne) - }) - - it('handles unsupported chain', () => { - expect(fromGraphQLChain(Chain.UnknownChain)).toEqual(null) - }) -}) - -describe(fromMoonpayNetwork, () => { - it('handles supported chain', () => { - expect(fromMoonpayNetwork(undefined)).toEqual(UniverseChainId.Mainnet) - expect(fromMoonpayNetwork(Chain.Arbitrum.toLowerCase())).toEqual(UniverseChainId.ArbitrumOne) - expect(fromMoonpayNetwork(Chain.Optimism.toLowerCase())).toEqual(UniverseChainId.Optimism) - expect(fromMoonpayNetwork(Chain.Polygon.toLowerCase())).toEqual(UniverseChainId.Polygon) - expect(fromMoonpayNetwork(Chain.Base.toLowerCase())).toEqual(UniverseChainId.Base) - }) - - it('handle unsupported chain', () => { - expect(fromMoonpayNetwork('unknown')).toBeUndefined() - }) -}) - -describe(getPollingIntervalByBlocktime, () => { - it('returns the correct value for L1', () => { - expect(getPollingIntervalByBlocktime(UniverseChainId.Mainnet)).toEqual(PollingInterval.Fast) - }) - - it('returns the correct value for L2', () => { - expect(getPollingIntervalByBlocktime(UniverseChainId.Polygon)).toEqual( - PollingInterval.LightningMcQueen - ) - }) -}) - -describe(fromUniswapWebAppLink, () => { - it('handles supported chain', () => { - expect(fromUniswapWebAppLink(Chain.Ethereum.toLowerCase())).toEqual(UniverseChainId.Mainnet) - expect(fromUniswapWebAppLink(Chain.Arbitrum.toLowerCase())).toEqual(UniverseChainId.ArbitrumOne) - expect(fromUniswapWebAppLink(Chain.Optimism.toLowerCase())).toEqual(UniverseChainId.Optimism) - expect(fromUniswapWebAppLink(Chain.Polygon.toLowerCase())).toEqual(UniverseChainId.Polygon) - // TODO: add Base test once Chain includes Base (GQL reliant) - }) - - it('handle unsupported chain', () => { - expect(() => fromUniswapWebAppLink('unkwnown')).toThrow('Network "unkwnown" can not be mapped') - }) -}) - -describe(toUniswapWebAppLink, () => { - it('handles supported chain', () => { - expect(toUniswapWebAppLink(UniverseChainId.Mainnet)).toEqual(Chain.Ethereum.toLowerCase()) - expect(toUniswapWebAppLink(UniverseChainId.ArbitrumOne)).toEqual(Chain.Arbitrum.toLowerCase()) - expect(toUniswapWebAppLink(UniverseChainId.Optimism)).toEqual(Chain.Optimism.toLowerCase()) - expect(toUniswapWebAppLink(UniverseChainId.Polygon)).toEqual(Chain.Polygon.toLowerCase()) - // TODO: add Base test once Chain includes Base (GQL reliant) - }) - - it('handle unsupported chain', () => { - expect(() => fromUniswapWebAppLink('unkwnown')).toThrow('Network "unkwnown" can not be mapped') - }) -}) - -describe(chainIdToHexadecimalString, () => { - it('handles supported chain', () => { - expect(chainIdToHexadecimalString(UniverseChainId.ArbitrumOne)).toEqual('0xa4b1') - }) -}) - -describe('hexadecimalStringToInt', () => { - it('converts valid hexadecimal strings to integers', () => { - expect(hexadecimalStringToInt('1')).toEqual(1) - expect(hexadecimalStringToInt('a')).toEqual(10) - expect(hexadecimalStringToInt('A')).toEqual(10) - expect(hexadecimalStringToInt('10')).toEqual(16) - expect(hexadecimalStringToInt('FF')).toEqual(255) - expect(hexadecimalStringToInt('ff')).toEqual(255) - expect(hexadecimalStringToInt('100')).toEqual(256) - }) - - it('converts hexadecimal strings with prefix to integers', () => { - expect(hexadecimalStringToInt('0x1')).toEqual(1) - expect(hexadecimalStringToInt('0xa')).toEqual(10) - expect(hexadecimalStringToInt('0xA')).toEqual(10) - expect(hexadecimalStringToInt('0x10')).toEqual(16) - expect(hexadecimalStringToInt('0xFF')).toEqual(255) - expect(hexadecimalStringToInt('0xff')).toEqual(255) - expect(hexadecimalStringToInt('0x100')).toEqual(256) - }) - - it('handles invalid hexadecimal strings', () => { - expect(hexadecimalStringToInt('')).toBeNaN() - expect(hexadecimalStringToInt('g')).toBeNaN() - expect(hexadecimalStringToInt('0x')).toBeNaN() - expect(hexadecimalStringToInt('0xg')).toBeNaN() - }) -}) diff --git a/packages/wallet/src/features/chains/utils.ts b/packages/wallet/src/features/chains/utils.ts deleted file mode 100644 index ecc5e39f5ea..00000000000 --- a/packages/wallet/src/features/chains/utils.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { BigNumber, BigNumberish } from 'ethers' -import { L2ChainId, L2_CHAIN_IDS } from 'uniswap/src/constants/chains' -import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { - UniverseChainId, - WALLET_SUPPORTED_CHAIN_IDS, - WalletChainId, -} from 'uniswap/src/types/chains' -import { isJestRun } from 'utilities/src/environment' -import { PollingInterval } from 'wallet/src/constants/misc' - -// Some code from the web app uses chainId types as numbers -// This validates them as coerces into SupportedChainId -export function toSupportedChainId(chainId?: BigNumberish): WalletChainId | null { - // Support Goerli for testing - const ids = isJestRun - ? [UniverseChainId.Goerli, ...WALLET_SUPPORTED_CHAIN_IDS] - : WALLET_SUPPORTED_CHAIN_IDS - - if (!chainId || !ids.map((c) => c.toString()).includes(chainId.toString())) { - return null - } - return parseInt(chainId.toString(), 10) as WalletChainId -} - -export function chainIdToHexadecimalString(chainId: WalletChainId): string { - return BigNumber.from(chainId).toHexString() -} - -export function hexadecimalStringToInt(hex: string): number { - return parseInt(hex, 16) -} - -export const isL2Chain = (chainId?: UniverseChainId): boolean => - Boolean(chainId && L2_CHAIN_IDS.includes(chainId as L2ChainId)) - -export function fromGraphQLChain(chain: Chain | undefined): WalletChainId | null { - switch (chain) { - case Chain.Ethereum: - return UniverseChainId.Mainnet - case Chain.Arbitrum: - return UniverseChainId.ArbitrumOne - case Chain.EthereumGoerli: - return UniverseChainId.Goerli - case Chain.Optimism: - return UniverseChainId.Optimism - case Chain.Polygon: - return UniverseChainId.Polygon - case Chain.Base: - return UniverseChainId.Base - case Chain.Bnb: - return UniverseChainId.Bnb - case Chain.Blast: - return UniverseChainId.Blast - case Chain.Avalanche: - return UniverseChainId.Avalanche - case Chain.Celo: - return UniverseChainId.Celo - case Chain.Zora: - return UniverseChainId.Zora - case Chain.Zksync: - return UniverseChainId.Zksync - } - - return null -} - -export function getPollingIntervalByBlocktime(chainId?: WalletChainId): PollingInterval { - return isL2Chain(chainId) ? PollingInterval.LightningMcQueen : PollingInterval.Fast -} - -export function fromMoonpayNetwork(moonpayNetwork: string | undefined): WalletChainId | undefined { - switch (moonpayNetwork) { - case Chain.Arbitrum.toLowerCase(): - return UniverseChainId.ArbitrumOne - case Chain.Optimism.toLowerCase(): - return UniverseChainId.Optimism - case Chain.Polygon.toLowerCase(): - return UniverseChainId.Polygon - case Chain.Bnb.toLowerCase(): - return UniverseChainId.Bnb - // Moonpay still refers to BNB chain as BSC so including both BNB and BSC cases - case 'bsc': - return UniverseChainId.Bnb - case Chain.Base.toLowerCase(): - return UniverseChainId.Base - case Chain.Avalanche.toLowerCase(): - return UniverseChainId.Avalanche - case Chain.Celo.toLowerCase(): - return UniverseChainId.Celo - case undefined: - return UniverseChainId.Mainnet - default: - return undefined - } -} - -export function fromUniswapWebAppLink(network: string | null): WalletChainId | null { - switch (network) { - case Chain.Ethereum.toLowerCase(): - return UniverseChainId.Mainnet - case Chain.Arbitrum.toLowerCase(): - return UniverseChainId.ArbitrumOne - case Chain.Optimism.toLowerCase(): - return UniverseChainId.Optimism - case Chain.Polygon.toLowerCase(): - return UniverseChainId.Polygon - case Chain.Base.toLowerCase(): - return UniverseChainId.Base - case Chain.Bnb.toLowerCase(): - return UniverseChainId.Bnb - case Chain.Blast.toLowerCase(): - return UniverseChainId.Blast - case Chain.Avalanche.toLowerCase(): - return UniverseChainId.Avalanche - case Chain.Celo.toLowerCase(): - return UniverseChainId.Celo - case Chain.Zora.toLowerCase(): - return UniverseChainId.Zora - case Chain.Zksync.toLowerCase(): - return UniverseChainId.Zksync - default: - throw new Error(`Network "${network}" can not be mapped`) - } -} - -export function toUniswapWebAppLink(chainId: WalletChainId): string | null { - switch (chainId) { - case UniverseChainId.Mainnet: - return Chain.Ethereum.toLowerCase() - case UniverseChainId.ArbitrumOne: - return Chain.Arbitrum.toLowerCase() - case UniverseChainId.Optimism: - return Chain.Optimism.toLowerCase() - case UniverseChainId.Polygon: - return Chain.Polygon.toLowerCase() - case UniverseChainId.Base: - return Chain.Base.toLowerCase() - case UniverseChainId.Bnb: - return Chain.Bnb.toLowerCase() - case UniverseChainId.Blast: - return Chain.Blast.toLowerCase() - case UniverseChainId.Avalanche: - return Chain.Avalanche.toLowerCase() - case UniverseChainId.Celo: - return Chain.Celo.toLowerCase() - case UniverseChainId.Zora: - return Chain.Zora.toLowerCase() - case UniverseChainId.Zksync: - return Chain.Zksync.toLowerCase() - default: - throw new Error(`ChainID "${chainId}" can not be mapped`) - } -} diff --git a/packages/wallet/src/features/contracts/ContractManager.ts b/packages/wallet/src/features/contracts/ContractManager.ts index afcfe4371f9..a958957478f 100644 --- a/packages/wallet/src/features/contracts/ContractManager.ts +++ b/packages/wallet/src/features/contracts/ContractManager.ts @@ -1,9 +1,9 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { Contract, ContractInterface, providers } from 'ethers' import { WalletChainId } from 'uniswap/src/types/chains' +import { getValidAddress } from 'uniswap/src/utils/addresses' +import { isNativeCurrencyAddress } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' -import { getValidAddress } from 'wallet/src/utils/addresses' -import { isNativeCurrencyAddress } from 'wallet/src/utils/currencyId' export class ContractManager { private _contracts: Partial>> = {} @@ -12,7 +12,7 @@ export class ContractManager { chainId: WalletChainId, address: Address, provider: providers.Provider, - ABI: ContractInterface + ABI: ContractInterface, ): Contract { if (isNativeCurrencyAddress(chainId, address) || !getValidAddress(address, true)) { throw Error(`Invalid address for contract: ${address}`) @@ -21,11 +21,7 @@ export class ContractManager { if (this._contracts[chainId]?.[address]) { throw new Error(`Contract already exists for: ${chainId} ${address}`) } else { - logger.debug( - 'ContractManager', - 'createContract', - `Creating a new contract for: ${chainId} ${address}` - ) + logger.debug('ContractManager', 'createContract', `Creating a new contract for: ${chainId} ${address}`) const contract = new Contract(address, ABI, provider) this._contracts[chainId]![address] = contract return contract @@ -37,7 +33,7 @@ export class ContractManager { logger.warn( 'ContractManager', 'removeContract', - `Attempting to remove non-existing contract for: ${chainId} ${address}` + `Attempting to remove non-existing contract for: ${chainId} ${address}`, ) return } @@ -57,7 +53,7 @@ export class ContractManager { chainId: WalletChainId, address: Address, provider: providers.Provider, - ABI: ContractInterface + ABI: ContractInterface, ): T { const cachedContract = this.getContract(chainId, address) return (cachedContract ?? this.createContract(chainId, address, provider, ABI)) as T diff --git a/packages/wallet/src/features/contracts/hooks.ts b/packages/wallet/src/features/contracts/hooks.ts index 0db8c8e96ab..d6b502a98ed 100644 --- a/packages/wallet/src/features/contracts/hooks.ts +++ b/packages/wallet/src/features/contracts/hooks.ts @@ -16,12 +16,7 @@ export function useIsErc20Contract(address: string | undefined, chainId: WalletC } const contract = new Contract(address, ERC20_ABI, provider) try { - await Promise.all([ - contract.name(), - contract.symbol(), - contract.decimals(), - contract.totalSupply(), - ]) + await Promise.all([contract.name(), contract.symbol(), contract.decimals(), contract.totalSupply()]) return true } catch (e) { return false diff --git a/packages/wallet/src/features/dataApi/balances.ts b/packages/wallet/src/features/dataApi/balances.ts index 731bce86612..8ed6a53d6ec 100644 --- a/packages/wallet/src/features/dataApi/balances.ts +++ b/packages/wallet/src/features/dataApi/balances.ts @@ -1,5 +1,6 @@ import { NetworkStatus, Reference, useApolloClient, WatchQueryFetchPolicy } from '@apollo/client' import { useCallback, useMemo } from 'react' +import { PollingInterval } from 'uniswap/src/constants/misc' import { ContractInput, IAmount, @@ -9,22 +10,14 @@ import { usePortfolioBalancesQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GqlResult } from 'uniswap/src/data/types' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { CurrencyInfo, PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { CurrencyId } from 'uniswap/src/types/currency' +import { currencyId } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' -import { PollingInterval } from 'wallet/src/constants/misc' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' -import { - buildCurrency, - currencyIdToContractInput, - usePersistedError, -} from 'wallet/src/features/dataApi/utils' +import { buildCurrency, currencyIdToContractInput, usePersistedError } from 'wallet/src/features/dataApi/utils' import { useCurrencyIdToVisibility } from 'wallet/src/features/transactions/selectors' -import { - useHideSmallBalancesSetting, - useHideSpamTokensSetting, -} from 'wallet/src/features/wallet/hooks' -import { currencyId } from 'wallet/src/utils/currencyId' +import { useHideSmallBalancesSetting, useHideSpamTokensSetting } from 'wallet/src/features/wallet/hooks' type SortedPortfolioBalances = { balances: PortfolioBalance[] @@ -44,22 +37,15 @@ interface TokenOverrides { export type PortfolioCacheUpdater = (hidden: boolean, portfolioBalance?: PortfolioBalance) => void -export function usePortfolioValueModifiers( - address?: Address | Address[] -): PortfolioValueModifier[] | undefined { +export function usePortfolioValueModifiers(address?: Address | Address[]): PortfolioValueModifier[] | undefined { // Memoize array creation if passed a string to avoid recomputing at every render - const addressArray = useMemo( - () => (!address ? [] : Array.isArray(address) ? address : [address]), - [address] - ) + const addressArray = useMemo(() => (!address ? [] : Array.isArray(address) ? address : [address]), [address]) const currencyIdToTokenVisibility = useCurrencyIdToVisibility() const hideSpamTokens = useHideSpamTokensSetting() const hideSmallBalances = useHideSmallBalancesSetting() - const { tokenIncludeOverrides, tokenExcludeOverrides } = Object.entries( - currencyIdToTokenVisibility - ).reduce( + const { tokenIncludeOverrides, tokenExcludeOverrides } = Object.entries(currencyIdToTokenVisibility).reduce( (acc: TokenOverrides, [key, tokenVisibility]) => { const contractInput = currencyIdToContractInput(key) if (tokenVisibility.isVisible) { @@ -72,7 +58,7 @@ export function usePortfolioValueModifiers( { tokenIncludeOverrides: [], tokenExcludeOverrides: [], - } + }, ) const modifiers = useMemo(() => { @@ -83,13 +69,7 @@ export function usePortfolioValueModifiers( includeSmallBalances: !hideSmallBalances, includeSpamTokens: !hideSpamTokens, })) - }, [ - addressArray, - tokenIncludeOverrides, - tokenExcludeOverrides, - hideSmallBalances, - hideSpamTokens, - ]) + }, [addressArray, tokenIncludeOverrides, tokenExcludeOverrides, hideSmallBalances, hideSpamTokens]) return modifiers.length > 0 ? modifiers : undefined } @@ -204,7 +184,7 @@ export function usePortfolioBalances({ const retry = useCallback( () => refetch({ ownerAddress: address, valueModifiers }), - [address, valueModifiers, refetch] + [address, valueModifiers, refetch], ) return { @@ -260,7 +240,7 @@ export function usePortfolioTotalValue({ const retry = useCallback( () => refetch({ ownerAddress: address, valueModifiers }), - [address, valueModifiers, refetch] + [address, valueModifiers, refetch], ) return { @@ -281,8 +261,7 @@ export function usePortfolioTotalValue({ */ export function useHighestBalanceNativeCurrencyId(address: Address): CurrencyId | undefined { const { data } = useSortedPortfolioBalances({ address }) - return data?.balances.find((balance) => balance.currencyInfo.currency.isNative)?.currencyInfo - .currencyId + return data?.balances.find((balance) => balance.currencyInfo.currency.isNative)?.currencyInfo.currencyId } /** @@ -322,7 +301,7 @@ export function useTokenBalancesGroupedByVisibility({ } return acc }, - { shown: [], hidden: [] } + { shown: [], hidden: [] }, ) return { shownTokens: shown.length ? shown : undefined, @@ -471,7 +450,7 @@ export function usePortfolioCacheUpdater(address: string): PortfolioCacheUpdater }, }) }, - [apolloClient, address] + [apolloClient, address], ) return updater diff --git a/packages/wallet/src/features/dataApi/searchTokens.ts b/packages/wallet/src/features/dataApi/searchTokens.ts index 6d8bcb3d02d..e5c338e52be 100644 --- a/packages/wallet/src/features/dataApi/searchTokens.ts +++ b/packages/wallet/src/features/dataApi/searchTokens.ts @@ -1,8 +1,5 @@ import { useCallback, useMemo } from 'react' -import { - Chain, - useSearchTokensQuery, -} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { Chain, useSearchTokensQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GqlResult } from 'uniswap/src/data/types' import { toGraphQLChain } from 'uniswap/src/features/chains/utils' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' @@ -14,7 +11,7 @@ export const ALL_GQL_CHAINS = Object.values(Chain) export function useSearchTokens( searchQuery: string | null, chainFilter: UniverseChainId | null, - skip: boolean + skip: boolean, ): GqlResult { const gqlChainFilter = chainFilter ? toGraphQLChain(chainFilter) : null const { data, loading, error, refetch } = useSearchTokensQuery({ @@ -43,13 +40,10 @@ export function useSearchTokens( .filter((c): c is CurrencyInfo => Boolean(c)) }, [data]) - const retry = useCallback( - () => !skip && refetch({ searchQuery: searchQuery ?? '' }), - [refetch, searchQuery, skip] - ) + const retry = useCallback(() => !skip && refetch({ searchQuery: searchQuery ?? '' }), [refetch, searchQuery, skip]) return useMemo( () => ({ data: formattedData, loading, error: persistedError, refetch: retry }), - [formattedData, loading, retry, persistedError] + [formattedData, loading, retry, persistedError], ) } diff --git a/packages/wallet/src/features/dataApi/tokenProjects.ts b/packages/wallet/src/features/dataApi/tokenProjects.ts index 20f9ba7ca58..e82a047b2cb 100644 --- a/packages/wallet/src/features/dataApi/tokenProjects.ts +++ b/packages/wallet/src/features/dataApi/tokenProjects.ts @@ -3,20 +3,14 @@ import { useTokenProjectsQuery } from 'uniswap/src/data/graphql/uniswap-data-api import { GqlResult } from 'uniswap/src/data/types' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { CurrencyId } from 'uniswap/src/types/currency' -import { - currencyIdToContractInput, - tokenProjectToCurrencyInfos, -} from 'wallet/src/features/dataApi/utils' +import { currencyIdToContractInput, tokenProjectToCurrencyInfos } from 'wallet/src/features/dataApi/utils' /** * Fetches token information as CurrencyInfo from currencyIds. When used, wrap component * with Suspense. */ export function useTokenProjects(currencyIds: CurrencyId[]): GqlResult { - const contracts = useMemo( - () => currencyIds.map((id) => currencyIdToContractInput(id)), - [currencyIds] - ) + const contracts = useMemo(() => currencyIds.map((id) => currencyIdToContractInput(id)), [currencyIds]) const { data, loading, error, refetch } = useTokenProjectsQuery({ variables: { contracts }, diff --git a/packages/wallet/src/features/dataApi/topTokens.ts b/packages/wallet/src/features/dataApi/topTokens.ts index 08244427d82..00469edf707 100644 --- a/packages/wallet/src/features/dataApi/topTokens.ts +++ b/packages/wallet/src/features/dataApi/topTokens.ts @@ -40,6 +40,6 @@ export function usePopularTokens(chainFilter: UniverseChainId): GqlResult ({ data: formattedData, loading, error: persistedError, refetch }), - [formattedData, loading, persistedError, refetch] + [formattedData, loading, persistedError, refetch], ) } diff --git a/packages/wallet/src/features/dataApi/utils.test.ts b/packages/wallet/src/features/dataApi/utils.test.ts index 469b9517b37..c8dd0019369 100644 --- a/packages/wallet/src/features/dataApi/utils.test.ts +++ b/packages/wallet/src/features/dataApi/utils.test.ts @@ -5,9 +5,9 @@ import { Token as GQLToken, TokenProject, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { UniverseChainId } from 'uniswap/src/types/chains' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { buildCurrency, currencyIdToContractInput, diff --git a/packages/wallet/src/features/dataApi/utils.ts b/packages/wallet/src/features/dataApi/utils.ts index 9afb3e46fec..db2fb79ae87 100644 --- a/packages/wallet/src/features/dataApi/utils.ts +++ b/packages/wallet/src/features/dataApi/utils.ts @@ -9,18 +9,17 @@ import { TokenProjectsQuery, TokenQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { toGraphQLChain } from 'uniswap/src/features/chains/utils' +import { fromGraphQLChain, toGraphQLChain } from 'uniswap/src/features/chains/utils' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' import { CurrencyId } from 'uniswap/src/types/currency' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' -import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { currencyId, currencyIdToChain, currencyIdToGraphQLAddress, isNativeCurrencyAddress, -} from 'wallet/src/utils/currencyId' +} from 'uniswap/src/utils/currencyId' +import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' type BuildCurrencyParams = { chainId?: Nullable @@ -43,7 +42,7 @@ export function currencyIdToContractInput(id: CurrencyId): ContractInput { export function tokenProjectToCurrencyInfos( tokenProjects: TokenProjectsQuery['tokenProjects'], - chainFilter?: WalletChainId | null + chainFilter?: WalletChainId | null, ): CurrencyInfo[] { return tokenProjects ?.flatMap((project) => @@ -76,7 +75,7 @@ export function tokenProjectToCurrencyInfos( } return currencyInfo - }) + }), ) .filter(Boolean) as CurrencyInfo[] } @@ -112,28 +111,15 @@ export function buildCurrency({ return undefined } - const buyFee = - buyFeeBps && BigNumber.from(buyFeeBps).gt(0) ? BigNumber.from(buyFeeBps) : undefined - const sellFee = - sellFeeBps && BigNumber.from(sellFeeBps).gt(0) ? BigNumber.from(sellFeeBps) : undefined + const buyFee = buyFeeBps && BigNumber.from(buyFeeBps).gt(0) ? BigNumber.from(buyFeeBps) : undefined + const sellFee = sellFeeBps && BigNumber.from(sellFeeBps).gt(0) ? BigNumber.from(sellFeeBps) : undefined return isNonNativeAddress(chainId, address) - ? new Token( - chainId, - address, - decimals, - symbol ?? undefined, - name ?? undefined, - bypassChecksum, - buyFee, - sellFee - ) + ? new Token(chainId, address, decimals, symbol ?? undefined, name ?? undefined, bypassChecksum, buyFee, sellFee) : NativeCurrency.onChain(chainId) } -export function gqlTokenToCurrencyInfo( - token: NonNullable> -): CurrencyInfo | null { +export function gqlTokenToCurrencyInfo(token: NonNullable>): CurrencyInfo | null { const { chain, address, decimals, symbol, project, feeData } = token const chainId = fromGraphQLChain(chain) diff --git a/packages/wallet/src/features/ens/api.ts b/packages/wallet/src/features/ens/api.ts index 567da73ddfb..5bca8337ef2 100644 --- a/packages/wallet/src/features/ens/api.ts +++ b/packages/wallet/src/features/ens/api.ts @@ -3,9 +3,9 @@ import { providers } from 'ethers' import { useMemo } from 'react' import { useRestQuery } from 'uniswap/src/data/rest' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { ONE_MINUTE_MS } from 'utilities/src/time/time' import { createEthersProvider } from 'wallet/src/features/providers/createEthersProvider' -import { areAddressesEqual } from 'wallet/src/utils/addresses' // stub endpoint to conform to REST endpoint styles // Rest link should intercept and use custom fetcher instead @@ -89,7 +89,7 @@ export const getOnChainEnsFetch = async (params: EnsLookupParams): Promise; timestamp: number }, EnsLookupParams>( STUB_ONCHAIN_ENS_ENDPOINT, // will invoke `getOnChainEnsFetch` @@ -97,7 +97,7 @@ function useEnsQuery( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion { type, nameOrAddress: nameOrAddress!, chainId }, ['data'], - { ttlMs: 5 * ONE_MINUTE_MS, skip: !nameOrAddress } + { ttlMs: 5 * ONE_MINUTE_MS, skip: !nameOrAddress }, ) const { data, error } = result @@ -108,34 +108,22 @@ function useEnsQuery( data: data?.data, error, }), - [data, error, result] + [data, error, result], ) } export function useENSName(address?: Address, chainId: WalletChainId = UniverseChainId.Mainnet) { return useEnsQuery(EnsLookupType.Name, address, chainId) } -export function useAddressFromEns( - maybeName: string | null, - chainId: WalletChainId = UniverseChainId.Mainnet -) { +export function useAddressFromEns(maybeName: string | null, chainId: WalletChainId = UniverseChainId.Mainnet) { return useEnsQuery(EnsLookupType.Address, maybeName, chainId) } -export function useENSAvatar( - address?: string | null, - chainId: WalletChainId = UniverseChainId.Mainnet -) { +export function useENSAvatar(address?: string | null, chainId: WalletChainId = UniverseChainId.Mainnet) { return useEnsQuery(EnsLookupType.Avatar, address, chainId) } -export function useENSDescription( - name?: string | null, - chainId: WalletChainId = UniverseChainId.Mainnet -) { +export function useENSDescription(name?: string | null, chainId: WalletChainId = UniverseChainId.Mainnet) { return useEnsQuery(EnsLookupType.Description, name, chainId) } -export function useENSTwitterUsername( - name?: string | null, - chainId: WalletChainId = UniverseChainId.Mainnet -) { +export function useENSTwitterUsername(name?: string | null, chainId: WalletChainId = UniverseChainId.Mainnet) { return useEnsQuery(EnsLookupType.TwitterUsername, name, chainId) } diff --git a/packages/wallet/src/features/ens/parseENSAddress.ts b/packages/wallet/src/features/ens/parseENSAddress.ts index 3dba8acae14..2be5a1d9b07 100644 --- a/packages/wallet/src/features/ens/parseENSAddress.ts +++ b/packages/wallet/src/features/ens/parseENSAddress.ts @@ -2,9 +2,7 @@ const ENS_DOMAIN_REGEX = /^[a-zA-Z0-9-.]+\.eth$/ -export function parseENSAddress( - ensAddress: string -): { ensName: string; ensPath: string | undefined } | undefined { +export function parseENSAddress(ensAddress: string): { ensName: string; ensPath: string | undefined } | undefined { // Note: this was refactored from a regex into a function as the regex in question // contained a sequence of rules that result in O(n^2) complexity, thus potentially opening up // the mobile client to a DoS attack. diff --git a/packages/wallet/src/features/ens/useENS.ts b/packages/wallet/src/features/ens/useENS.ts index 96bc498ac2f..113890ac8da 100644 --- a/packages/wallet/src/features/ens/useENS.ts +++ b/packages/wallet/src/features/ens/useENS.ts @@ -1,9 +1,9 @@ // Copied from https://github.com/Uniswap/interface/blob/main/src/hooks/useENS.ts import { WalletChainId } from 'uniswap/src/types/chains' +import { getValidAddress } from 'uniswap/src/utils/addresses' import { useDebounce } from 'utilities/src/time/timing' import { useAddressFromEns, useENSName } from 'wallet/src/features/ens/api' -import { getValidAddress } from 'wallet/src/utils/addresses' /** * Given a name or address, does a lookup to resolve to an address and name @@ -12,7 +12,7 @@ import { getValidAddress } from 'wallet/src/utils/addresses' export function useENS( chainId: WalletChainId, nameOrAddress?: string | null, - autocompleteDomain?: boolean + autocompleteDomain?: boolean, ): { loading: boolean address?: string | null @@ -25,7 +25,7 @@ export function useENS( const { data: name, loading: nameFetching } = useENSName(validAddress ?? undefined, chainId) const { data: address, loading: addressFetching } = useAddressFromEns( autocompleteDomain ? getCompletedENSName(maybeName) : maybeName, - chainId + chainId, ) return { diff --git a/packages/wallet/src/features/favorites/selectors.ts b/packages/wallet/src/features/favorites/selectors.ts index 94bfc3386c4..64da22d7138 100644 --- a/packages/wallet/src/features/favorites/selectors.ts +++ b/packages/wallet/src/features/favorites/selectors.ts @@ -5,29 +5,20 @@ import { RootState } from 'wallet/src/state' export const selectFavoriteTokens = (state: RootState): string[] => unique(state.favorites.tokens) -export const selectHasFavoriteTokens = createSelector(selectFavoriteTokens, (tokens) => - Boolean(tokens?.length > 0) -) +export const selectHasFavoriteTokens = createSelector(selectFavoriteTokens, (tokens) => Boolean(tokens?.length > 0)) export const makeSelectHasTokenFavorited = (): Selector => createSelector( selectFavoriteTokens, (_: RootState, currencyId: string) => currencyId, - (tokens, currencyId) => tokens?.includes(currencyId.toLowerCase()) + (tokens, currencyId) => tokens?.includes(currencyId.toLowerCase()), ) const selectWatchedAddresses = (state: RootState): string[] => state.favorites.watchedAddresses -export const selectWatchedAddressSet = createSelector( - selectWatchedAddresses, - (watched) => new Set(watched) -) +export const selectWatchedAddressSet = createSelector(selectWatchedAddresses, (watched) => new Set(watched)) -export const selectHasWatchedWallets = createSelector(selectWatchedAddresses, (watched) => - Boolean(watched?.length > 0) -) +export const selectHasWatchedWallets = createSelector(selectWatchedAddresses, (watched) => Boolean(watched?.length > 0)) -export const selectNftsVisibility = (state: RootState): NFTKeyToVisibility => - state.favorites.nftsVisibility +export const selectNftsVisibility = (state: RootState): NFTKeyToVisibility => state.favorites.nftsVisibility -export const selectTokensVisibility = (state: RootState): CurrencyIdToVisibility => - state.favorites.tokensVisibility +export const selectTokensVisibility = (state: RootState): CurrencyIdToVisibility => state.favorites.tokensVisibility diff --git a/packages/wallet/src/features/favorites/slice.ts b/packages/wallet/src/features/favorites/slice.ts index 988c1f4bcf3..16159d39d92 100644 --- a/packages/wallet/src/features/favorites/slice.ts +++ b/packages/wallet/src/features/favorites/slice.ts @@ -2,9 +2,9 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { Ether } from '@uniswap/sdk-core' import { UniverseChainId } from 'uniswap/src/types/chains' import { CurrencyId } from 'uniswap/src/types/currency' +import { currencyId as idFromCurrency } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' import { WBTC } from 'wallet/src/constants/tokens' -import { currencyId as idFromCurrency } from 'wallet/src/utils/currencyId' export type Visibility = { isVisible: boolean } export type CurrencyIdToVisibility = Record @@ -36,24 +36,14 @@ export const slice = createSlice({ name: 'favorites', initialState: initialFavoritesState, reducers: { - addFavoriteToken: ( - state, - { payload: { currencyId } }: PayloadAction<{ currencyId: string }> - ) => { + addFavoriteToken: (state, { payload: { currencyId } }: PayloadAction<{ currencyId: string }>) => { if (state.tokens.indexOf(currencyId) === -1) { state.tokens.push(currencyId.toLowerCase()) // normalize all IDs } else { - logger.warn( - 'slice', - 'addFavoriteToken', - `Attempting to favorite a token twice (${currencyId})` - ) + logger.warn('slice', 'addFavoriteToken', `Attempting to favorite a token twice (${currencyId})`) } }, - removeFavoriteToken: ( - state, - { payload: { currencyId } }: PayloadAction<{ currencyId: string }> - ) => { + removeFavoriteToken: (state, { payload: { currencyId } }: PayloadAction<{ currencyId: string }>) => { const newTokens = state.tokens.filter((c) => { return c.toLocaleLowerCase() !== currencyId.toLocaleLowerCase() }) @@ -62,59 +52,46 @@ export const slice = createSlice({ logger.warn( 'slice', 'removeFavoriteToken', - `Attempting to un-favorite a token that was not in favorites (${currencyId})` + `Attempting to un-favorite a token that was not in favorites (${currencyId})`, ) } state.tokens = newTokens }, - setFavoriteTokens: ( - state, - { payload: { currencyIds } }: PayloadAction<{ currencyIds: string[] }> - ) => { + setFavoriteTokens: (state, { payload: { currencyIds } }: PayloadAction<{ currencyIds: string[] }>) => { state.tokens = currencyIds }, addWatchedAddress: (state, { payload: { address } }: PayloadAction<{ address: Address }>) => { if (!state.watchedAddresses.includes(address)) { state.watchedAddresses.push(address) } else { - logger.warn( - 'slice', - 'addWatchedAddress', - `Attempting to watch an address twice (${address})` - ) + logger.warn('slice', 'addWatchedAddress', `Attempting to watch an address twice (${address})`) } }, - removeWatchedAddress: ( - state, - { payload: { address } }: PayloadAction<{ address: Address }> - ) => { + removeWatchedAddress: (state, { payload: { address } }: PayloadAction<{ address: Address }>) => { const newWatched = state.watchedAddresses.filter((a) => a !== address) if (newWatched.length === state.watchedAddresses.length) { logger.warn( 'slice', 'removeWatchedAddress', - `Attempting to remove an address not found in watched list (${address})` + `Attempting to remove an address not found in watched list (${address})`, ) } state.watchedAddresses = newWatched }, - setFavoriteWallets: ( - state, - { payload: { addresses } }: PayloadAction<{ addresses: Address[] }> - ) => { + setFavoriteWallets: (state, { payload: { addresses } }: PayloadAction<{ addresses: Address[] }>) => { state.watchedAddresses = addresses }, toggleTokenVisibility: ( state, - { payload: { currencyId, isSpam } }: PayloadAction<{ currencyId: string; isSpam?: boolean }> + { payload: { currencyId, isSpam } }: PayloadAction<{ currencyId: string; isSpam?: boolean }>, ) => { const isVisible = state.tokensVisibility[currencyId]?.isVisible ?? isSpam === false state.tokensVisibility[currencyId] = { isVisible: !isVisible } }, toggleNftVisibility: ( state, - { payload: { nftKey, isSpam } }: PayloadAction<{ nftKey: string; isSpam?: boolean }> + { payload: { nftKey, isSpam } }: PayloadAction<{ nftKey: string; isSpam?: boolean }>, ) => { const isVisible = state.nftsVisibility[nftKey]?.isVisible ?? isSpam === false state.nftsVisibility[nftKey] = { isVisible: !isVisible } diff --git a/packages/wallet/src/features/fiatCurrency/conversion.ts b/packages/wallet/src/features/fiatCurrency/conversion.ts index aeb8cf9d3ad..db513fc721a 100644 --- a/packages/wallet/src/features/fiatCurrency/conversion.ts +++ b/packages/wallet/src/features/fiatCurrency/conversion.ts @@ -1,12 +1,9 @@ import { useCallback, useMemo } from 'react' -import { - Currency, - useConvertQuery, -} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { PollingInterval } from 'uniswap/src/constants/misc' +import { Currency, useConvertQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { FeatureFlags } from 'uniswap/src/features/gating/flags' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { FiatNumberType } from 'utilities/src/format/types' -import { PollingInterval } from 'wallet/src/constants/misc' import { FiatCurrency } from 'wallet/src/features/fiatCurrency/constants' import { getFiatCurrencyCode, useAppFiatCurrency } from 'wallet/src/features/fiatCurrency/hooks' import { LocalizationContextState } from 'wallet/src/features/language/LocalizationContext' @@ -79,18 +76,18 @@ const mapFiatCurrencyToServerCurrency: Record { amount: number; currency: FiatCurrency } + convertFiatAmount: (amount: number) => { amount: number; currency: FiatCurrency } convertFiatAmountFormatted: ( fromAmount: Maybe, numberType: FiatNumberType, - placeholder?: string + placeholder?: string, ) => string } // Temporary function for feature turned off -function convertFiatAmountDefault(_amount?: number): { amount: number; currency: FiatCurrency } { +function convertFiatAmountDefault(amount: number): { amount: number; currency: FiatCurrency } { return { - amount: 1, + amount, currency: FiatCurrency.UnitedStatesDollar, } } @@ -123,12 +120,10 @@ export function useFiatConverter({ const conversion = latestConversion || prevConversion const conversionRate = conversion?.convert?.value const conversionCurrency = conversion?.convert?.currency - const outputCurrency = conversionCurrency - ? mapServerCurrencyToFiatCurrency[conversionCurrency] - : undefined + const outputCurrency = conversionCurrency ? mapServerCurrencyToFiatCurrency[conversionCurrency] : undefined const convertFiatAmountInner = useCallback( - (amount = 1): { amount: number; currency: FiatCurrency } => { + (amount: number): { amount: number; currency: FiatCurrency } => { const defaultResult = { amount, currency: FiatCurrency.UnitedStatesDollar } if (SOURCE_CURRENCY === toCurrency || !conversionRate || !outputCurrency) { @@ -140,7 +135,7 @@ export function useFiatConverter({ currency: outputCurrency, } }, - [conversionRate, outputCurrency, toCurrency] + [conversionRate, outputCurrency, toCurrency], ) const convertFiatAmountFormattedInner = useCallback( (fromAmount: Maybe, numberType: FiatNumberType, placeholder = '-'): string => { @@ -159,7 +154,7 @@ export function useFiatConverter({ placeholder, }) }, - [convertFiatAmountInner, formatNumberOrString] + [convertFiatAmountInner, formatNumberOrString], ) return useMemo( @@ -167,6 +162,6 @@ export function useFiatConverter({ convertFiatAmount: featureEnabled ? convertFiatAmountInner : convertFiatAmountDefault, convertFiatAmountFormatted: convertFiatAmountFormattedInner, }), - [convertFiatAmountFormattedInner, convertFiatAmountInner, featureEnabled] + [convertFiatAmountFormattedInner, convertFiatAmountInner, featureEnabled], ) } diff --git a/packages/wallet/src/features/fiatCurrency/hooks.ts b/packages/wallet/src/features/fiatCurrency/hooks.ts index 2aa6f458a7e..e88d3e51a4a 100644 --- a/packages/wallet/src/features/fiatCurrency/hooks.ts +++ b/packages/wallet/src/features/fiatCurrency/hooks.ts @@ -40,10 +40,7 @@ export function useFiatCurrencyComponents(currency: FiatCurrency): FiatCurrencyC * @param currency target currency * @returns currency name */ -export function getFiatCurrencyName( - t: AppTFunction, - currency: FiatCurrency -): { name: string; shortName: string } { +export function getFiatCurrencyName(t: AppTFunction, currency: FiatCurrency): { name: string; shortName: string } { const currencyToCurrencyName = { [FiatCurrency.AustrialianDollor]: t('currency.aud'), [FiatCurrency.BrazilianReal]: t('currency.brl'), @@ -143,6 +140,6 @@ export const useLocalFiatToUSDConverter = (): ((fiatAmount: number) => number | const { amount: USDInLocalCurrency } = convertFiatAmount(1) return USDInLocalCurrency ? fiatAmount / USDInLocalCurrency : undefined }, - [convertFiatAmount] + [convertFiatAmount], ) } diff --git a/packages/wallet/src/features/fiatOnRamp/api.ts b/packages/wallet/src/features/fiatOnRamp/api.ts index f77fc39bbbd..f048ecf205c 100644 --- a/packages/wallet/src/features/fiatOnRamp/api.ts +++ b/packages/wallet/src/features/fiatOnRamp/api.ts @@ -103,8 +103,7 @@ export const fiatOnRampApi = createApi({ c.type === 'crypto' && c[moonpaySupportField] && (!isUserInUS || - (c.isSupportedInUS && - (!stateInUS || c.notAllowedUSStates.indexOf(stateInUS) === -1))) + (c.isSupportedInUS && (!stateInUS || c.notAllowedUSStates.indexOf(stateInUS) === -1))), ), } }) @@ -124,14 +123,12 @@ export const fiatOnRampApi = createApi({ queryFn: ({ quoteCurrencyCode, baseCurrencyCode, baseCurrencyAmount, areFeesIncluded }) => // TODO: [MOB-223] consider a reverse proxy for privacy reasons fetch( - `${ - config.moonpayApiUrl - }/v3/currencies/${quoteCurrencyCode}/buy_quote?${serializeQueryParams({ + `${config.moonpayApiUrl}/v3/currencies/${quoteCurrencyCode}/buy_quote?${serializeQueryParams({ baseCurrencyCode, baseCurrencyAmount, areFeesIncluded, apiKey: config.moonpayApiKey, - })}` + })}`, ) .then((response) => response.json()) .then((response: MoonpayBuyQuoteResponse) => { @@ -152,13 +149,11 @@ export const fiatOnRampApi = createApi({ queryFn: ({ quoteCurrencyCode, baseCurrencyCode, areFeesIncluded }) => // TODO: [MOB-223] consider a reverse proxy for privacy reasons fetch( - `${config.moonpayApiUrl}/v3/currencies/${quoteCurrencyCode}/limits?${serializeQueryParams( - { - baseCurrencyCode, - areFeesIncluded, - apiKey: config.moonpayApiKey, - } - )}` + `${config.moonpayApiUrl}/v3/currencies/${quoteCurrencyCode}/limits?${serializeQueryParams({ + baseCurrencyCode, + areFeesIncluded, + apiKey: config.moonpayApiKey, + })}`, ) .then((response) => response.json()) .then((response: MoonpayLimitsResponse) => { @@ -192,7 +187,7 @@ export const fiatOnRampApi = createApi({ supportedCurrencyCodes.reduce>((acc, code: string) => { acc[code] = ownerAddress return acc - }, {}) + }, {}), ), }, method: 'POST', @@ -226,11 +221,7 @@ export const fiatOnRampAggregatorApi = createApi({ if (!account) { throw new Error('No active account') } - const { requestParams, signature } = await createSignedRequestParams( - args, - account, - signerManager - ) + const { requestParams, signature } = await createSignedRequestParams(args, account, signerManager) const result = await baseQuery({ url: `/transaction?${objectToQueryString(requestParams)}`, method: 'GET', @@ -256,10 +247,10 @@ export const { useFiatOnRampAggregatorTransactionQuery } = fiatOnRampAggregatorA * Utility to fetch fiat onramp transactions from moonpay */ export function fetchMoonpayTransaction( - previousTransactionDetails: FiatOnRampTransactionDetails + previousTransactionDetails: FiatOnRampTransactionDetails, ): Promise { return fetch( - `${config.moonpayApiUrl}/v1/transactions/ext/${previousTransactionDetails.id}?${COMMON_QUERY_PARAMS}` + `${config.moonpayApiUrl}/v1/transactions/ext/${previousTransactionDetails.id}?${COMMON_QUERY_PARAMS}`, ).then((res) => { if (res.status === TRANSACTION_NOT_FOUND) { // If Moonpay API returned 404 for the given external transaction id @@ -268,14 +259,14 @@ export function fetchMoonpayTransaction( // to avoid leaving placeholders as "pending" for too long, we mark them // as "unknown" after some time const isStale = dayjs(previousTransactionDetails.addedTime).isBefore( - dayjs().subtract(FIAT_ONRAMP_STALE_TX_TIMEOUT, 'ms') + dayjs().subtract(FIAT_ONRAMP_STALE_TX_TIMEOUT, 'ms'), ) if (isStale) { logger.debug( 'fiatOnRamp/api', 'fetchFiatOnRampTransaction', - `Transaction with id ${previousTransactionDetails.id} not found.` + `Transaction with id ${previousTransactionDetails.id} not found.`, ) return { @@ -288,11 +279,9 @@ export function fetchMoonpayTransaction( logger.debug( 'fiatOnRamp/api', 'fetchFiatOnRampTransaction', - `Transaction with id ${ - previousTransactionDetails.id - } not found, but not stale yet (${dayjs() + `Transaction with id ${previousTransactionDetails.id} not found, but not stale yet (${dayjs() .subtract(previousTransactionDetails.addedTime, 'ms') - .unix()}s old).` + .unix()}s old).`, ) return previousTransactionDetails @@ -302,8 +291,8 @@ export function fetchMoonpayTransaction( return res.json().then((transactions: MoonpayTransactionsResponse) => extractMoonpayTransactionDetails( // log while we have the full moonpay tx response - transactions.sort((a, b) => (dayjs(a.createdAt).isAfter(dayjs(b.createdAt)) ? 1 : -1))?.[0] - ) + transactions.sort((a, b) => (dayjs(a.createdAt).isAfter(dayjs(b.createdAt)) ? 1 : -1))?.[0], + ), ) }) } @@ -315,7 +304,7 @@ export async function fetchFiatOnRampTransaction( previousTransactionDetails: FiatOnRampTransactionDetails, forceFetch: boolean, account: Account, - signerManager: SignerManager + signerManager: SignerManager, ): Promise { // Force fetch if requested or for the first 3 minutes after the transaction was added const shouldForceFetch = shouldForceFetchTransaction(previousTransactionDetails, forceFetch) @@ -323,28 +312,25 @@ export async function fetchFiatOnRampTransaction( const { requestParams, signature } = await createSignedRequestParams( { sessionId: previousTransactionDetails.id, forceFetch: shouldForceFetch }, account, - signerManager - ) - const res = await fetch( - `${uniswapUrls.fiatOnRampApiUrl}/transaction?${objectToQueryString(requestParams)}`, - { - headers: { - 'x-uni-sig': signature, - ...FOR_API_HEADERS, - }, - } + signerManager, ) + const res = await fetch(`${uniswapUrls.fiatOnRampApiUrl}/transaction?${objectToQueryString(requestParams)}`, { + headers: { + 'x-uni-sig': signature, + ...FOR_API_HEADERS, + }, + }) const { transaction }: FORTransactionResponse = await res.json() if (!transaction) { const isStale = dayjs(previousTransactionDetails.addedTime).isBefore( - dayjs().subtract(FIAT_ONRAMP_STALE_TX_TIMEOUT, 'ms') + dayjs().subtract(FIAT_ONRAMP_STALE_TX_TIMEOUT, 'ms'), ) if (isStale) { logger.debug( 'fiatOnRamp/api', 'fetchFiatOnRampTransaction', - `Transaction with id ${previousTransactionDetails.id} not found.` + `Transaction with id ${previousTransactionDetails.id} not found.`, ) return { @@ -357,11 +343,9 @@ export async function fetchFiatOnRampTransaction( logger.debug( 'fiatOnRamp/api', 'fetchFiatOnRampTransaction', - `Transaction with id ${ - previousTransactionDetails.id - } not found, but not stale yet (${dayjs() + `Transaction with id ${previousTransactionDetails.id} not found, but not stale yet (${dayjs() .subtract(previousTransactionDetails.addedTime, 'ms') - .unix()}s old).` + .unix()}s old).`, ) return previousTransactionDetails @@ -373,10 +357,10 @@ export async function fetchFiatOnRampTransaction( function shouldForceFetchTransaction( previousTransactionDetails: FiatOnRampTransactionDetails, - forceFetch: boolean + forceFetch: boolean, ): boolean { const isRecent = dayjs(previousTransactionDetails.addedTime).isAfter( - dayjs().subtract(FIAT_ONRAMP_FORCE_FETCH_TX_TIMEOUT, 'ms') + dayjs().subtract(FIAT_ONRAMP_FORCE_FETCH_TX_TIMEOUT, 'ms'), ) const isSyncedWithBackend = previousTransactionDetails.typeInfo?.syncedWithBackend return forceFetch || (isRecent && !isSyncedWithBackend) diff --git a/packages/wallet/src/features/fiatOnRamp/types.ts b/packages/wallet/src/features/fiatOnRamp/types.ts index ac1b1e6318b..9908407f404 100644 --- a/packages/wallet/src/features/fiatOnRamp/types.ts +++ b/packages/wallet/src/features/fiatOnRamp/types.ts @@ -1,7 +1,4 @@ -import { - FiatPurchaseTransactionInfo, - TransactionDetails, -} from 'wallet/src/features/transactions/types' +import { FiatPurchaseTransactionInfo, TransactionDetails } from 'wallet/src/features/transactions/types' export type FiatOnRampTransactionDetails = TransactionDetails & { typeInfo: FiatPurchaseTransactionInfo @@ -144,11 +141,7 @@ export type MoonpayTransactionResponseItem = MoonpayQuote & { state: string // An array of four objects, each representing one of the four stages of the purchase process. The attributes of each stage are described below. stages: Array<{ - stage: - | 'stage_one_ordering' - | 'stage_two_verification' - | 'stage_three_processing' - | 'stage_four_delivery' + stage: 'stage_one_ordering' | 'stage_two_verification' | 'stage_three_processing' | 'stage_four_delivery' status: 'not_started' | 'in_progress' | 'success' | 'failed' failureReason: | 'card_not_supported' diff --git a/packages/wallet/src/features/fiatOnRamp/utils.test.ts b/packages/wallet/src/features/fiatOnRamp/utils.test.ts deleted file mode 100644 index 6ffa0404858..00000000000 --- a/packages/wallet/src/features/fiatOnRamp/utils.test.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { - isFiatOnRampApiError, - isInvalidRequestAmountTooHigh, - isInvalidRequestAmountTooLow, -} from 'wallet/src/features/fiatOnRamp/utils' - -describe('isFiatOnRampApiError', () => { - test('returns true', () => { - const error = { - data: { - statusCode: 400, - errorName: 'InvalidRequestAmountTooLow', - message: 'Source amount is below the minimum allowed, which is 50.00 USD', - }, - } - const result = isFiatOnRampApiError(error) - expect(result).toBe(true) - }) - - test('returns false', () => { - const error = { - data: { - message: 'Source amount is below the minimum allowed, which is 50.00 USD', - }, - } - const result = isFiatOnRampApiError(error) - expect(result).toBe(false) - }) -}) - -describe('isInvalidRequestAmountTooHigh', () => { - test('returns true', () => { - const error = { - data: { - statusCode: 400, - errorName: 'InvalidRequestAmountTooHigh', - message: 'Source amount is above the maximum allowed, which is 50000.00 USD', - context: { - maximumAllowed: 50000, - }, - }, - } - const result = isInvalidRequestAmountTooHigh(error) - expect(result).toBe(true) - }) - - test('returns false when context has unexpected type', () => { - const error = { - data: { - statusCode: 400, - errorName: 'InvalidRequestAmountTooHigh', - message: 'Source amount is above the maximum allowed, which is 50000.00 USD', - context: { - randomProperty: 50000, - }, - }, - } - const result = isInvalidRequestAmountTooHigh(error) - expect(result).toBe(false) - }) - - test('returns false when statusCode is not 400', () => { - const error = { - data: { - statusCode: 404, - errorName: 'InvalidRequestAmountTooHigh', - message: 'Source amount is above the maximum allowed, which is 50000.00 USD', - context: { - maximumAllowed: 50000, - }, - }, - } - const result = isInvalidRequestAmountTooHigh(error) - expect(result).toBe(false) - }) - - test('returns false when errorName is not InvalidRequestAmountTooHigh', () => { - const error = { - data: { - statusCode: 400, - errorName: 'InvalidRequestAmountTooBig', - message: 'Source amount is above the maximum allowed, which is 50000.00 USD', - context: { - maximumAllowed: 50000, - }, - }, - } - const result = isInvalidRequestAmountTooHigh(error) - expect(result).toBe(false) - }) -}) - -describe('isInvalidRequestAmountTooLow', () => { - test('returns true', () => { - const error = { - data: { - statusCode: 400, - errorName: 'InvalidRequestAmountTooLow', - message: 'Source amount is below the minimum allowed, which is 50.00 USD', - context: { - minimumAllowed: 50, - }, - }, - } - const result = isInvalidRequestAmountTooLow(error) - expect(result).toBe(true) - }) - - test('returns false when context has unexpected type', () => { - const error = { - data: { - statusCode: 400, - errorName: 'InvalidRequestAmountTooLow', - message: 'Source amount is below the minimum allowed, which is 50.00 USD', - context: { - randomProperty: 50, - }, - }, - } - const result = isInvalidRequestAmountTooLow(error) - expect(result).toBe(false) - }) - - test('returns false when statusCode is not 400', () => { - const error = { - data: { - statusCode: 404, - errorName: 'InvalidRequestAmountTooLow', - message: 'Source amount is below the minimum allowed, which is 50.00 USD', - context: { - minimumAllowed: 50, - }, - }, - } - const result = isInvalidRequestAmountTooLow(error) - expect(result).toBe(false) - }) - - test('returns false when errorName is not InvalidRequestAmountTooLow', () => { - const error = { - data: { - statusCode: 400, - errorName: 'InvalidRequestAmountTooSmall', - message: 'Source amount is below the minimum allowed, which is 50.00 USD', - context: { - minimumAllowed: 50, - }, - }, - } - const result = isInvalidRequestAmountTooLow(error) - expect(result).toBe(false) - }) -}) diff --git a/packages/wallet/src/features/fiatOnRamp/utils.ts b/packages/wallet/src/features/fiatOnRamp/utils.ts deleted file mode 100644 index 1e7ed9ae2e5..00000000000 --- a/packages/wallet/src/features/fiatOnRamp/utils.ts +++ /dev/null @@ -1,88 +0,0 @@ -import { FORLogo } from 'uniswap/src/features/fiatOnRamp/types' - -export interface FORApiError { - data: { - statusCode: number - errorName: string - message: string - context: object | undefined - } -} - -export interface InvalidRequestAmountTooLow extends FORApiError { - data: FORApiError['data'] & { - statusCode: 400 - errorName: 'InvalidRequestAmountTooLow' - context: { - minimumAllowed: number - } - } -} - -export function isInvalidRequestAmountTooLow( - error: FORApiError -): error is InvalidRequestAmountTooLow { - const e = error as InvalidRequestAmountTooLow - return ( - e.data.statusCode === 400 && - e.data.errorName === 'InvalidRequestAmountTooLow' && - typeof e.data.context?.minimumAllowed === 'number' - ) -} - -export interface InvalidRequestAmountTooHigh extends FORApiError { - data: FORApiError['data'] & { - statusCode: 400 - errorName: 'InvalidRequestAmountTooHigh' - context: { - maximumAllowed: number - } - } -} - -export function isInvalidRequestAmountTooHigh( - error: FORApiError -): error is InvalidRequestAmountTooHigh { - const e = error as InvalidRequestAmountTooHigh - return ( - e.data.statusCode === 400 && - e.data.errorName === 'InvalidRequestAmountTooHigh' && - typeof e.data.context?.maximumAllowed === 'number' - ) -} - -export interface NoQuotesError extends FORApiError { - data: FORApiError['data'] & { - statusCode: 400 - errorName: 'NoQuotes' - } -} - -export function isNoQuotesError(error: FORApiError): error is InvalidRequestAmountTooHigh { - const e = error as NoQuotesError - return e.data.statusCode === 400 && e.data.errorName === 'NoQuotes' -} - -export function isFiatOnRampApiError(error: unknown): error is FORApiError { - if (typeof error === 'object' && error !== null) { - const e = error as FORApiError - return ( - typeof e.data === 'object' && - e.data !== null && - typeof e.data.statusCode === 'number' && - typeof e.data.errorName === 'string' - ) - } - return false -} - -export function getOptionalServiceProviderLogo( - logos: FORLogo | undefined, - isDarkMode: boolean -): string | undefined { - return isDarkMode ? logos?.darkLogo : logos?.lightLogo -} - -export function getServiceProviderLogo(logos: FORLogo, isDarkMode: boolean): string { - return isDarkMode ? logos.darkLogo : logos.lightLogo -} diff --git a/packages/wallet/src/features/gas/adjustGasFee.ts b/packages/wallet/src/features/gas/adjustGasFee.ts index 70ef601ba21..1d5cd879633 100644 --- a/packages/wallet/src/features/gas/adjustGasFee.ts +++ b/packages/wallet/src/features/gas/adjustGasFee.ts @@ -17,14 +17,12 @@ export type FeeDetails = export function getAdjustedGasFeeDetails( request: providers.TransactionRequest, currentGasFeeParams: NonNullable, - adjustmentFactor: number + adjustmentFactor: number, ): FeeDetails { // Txn needs to be submitted with legacy gas params if (request.gasPrice) { const currentGasPrice = - 'gasPrice' in currentGasFeeParams - ? currentGasFeeParams.gasPrice - : currentGasFeeParams.maxFeePerGas + 'gasPrice' in currentGasFeeParams ? currentGasFeeParams.gasPrice : currentGasFeeParams.maxFeePerGas return { type: FeeType.Legacy, @@ -37,14 +35,10 @@ export function getAdjustedGasFeeDetails( // Txn needs to be submitted with EIP-1559 params if (request.maxFeePerGas && request.maxPriorityFeePerGas) { const currentMaxFeePerGas = - 'maxFeePerGas' in currentGasFeeParams - ? currentGasFeeParams.maxFeePerGas - : currentGasFeeParams.gasPrice + 'maxFeePerGas' in currentGasFeeParams ? currentGasFeeParams.maxFeePerGas : currentGasFeeParams.gasPrice const currentMaxPriorityFeePerGas = - 'maxFeePerGas' in currentGasFeeParams - ? currentGasFeeParams.maxPriorityFeePerGas - : currentGasFeeParams.gasPrice + 'maxFeePerGas' in currentGasFeeParams ? currentGasFeeParams.maxPriorityFeePerGas : currentGasFeeParams.gasPrice return { type: FeeType.Eip1559, @@ -53,7 +47,7 @@ export function getAdjustedGasFeeDetails( maxPriorityFeePerGas: multiplyByFactor( request.maxPriorityFeePerGas, currentMaxPriorityFeePerGas, - adjustmentFactor + adjustmentFactor, ), }, } @@ -64,13 +58,11 @@ export function getAdjustedGasFeeDetails( function determineError( request: providers.TransactionRequest, - currentGasFeeParams: NonNullable + currentGasFeeParams: NonNullable, ): Error { - const isEIP1559Transaction = - request.maxFeePerGas !== undefined && request.maxPriorityFeePerGas !== undefined + const isEIP1559Transaction = request.maxFeePerGas !== undefined && request.maxPriorityFeePerGas !== undefined - const isEIP1559Params = - 'maxFeePerGas' in currentGasFeeParams && 'maxPriorityFeePerGas' in currentGasFeeParams + const isEIP1559Params = 'maxFeePerGas' in currentGasFeeParams && 'maxPriorityFeePerGas' in currentGasFeeParams const isLegacyTransaction = request.gasPrice !== undefined const isLegacyParams = 'gasPrice' in currentGasFeeParams @@ -84,12 +76,10 @@ function determineError( const transactionMissingGasInfo = !isEIP1559Transaction && !isLegacyTransaction if (transactionMissingGasInfo) { if (isEIP1559Params) { - return new Error( - 'Transaction is missing gas values, but gasParams were provided for an EIP-1559 transaction.' - ) + return new Error('Transaction is missing gas values, but gasParams were provided for an EIP-1559 transaction.') } else { return new Error( - 'Transaction is missing gas values, but currentGasFeeParams were provided for a legacy transaction.' + 'Transaction is missing gas values, but currentGasFeeParams were provided for a legacy transaction.', ) } } @@ -99,12 +89,10 @@ function determineError( if (missingGasParams) { if (isEIP1559Transaction) { return new Error( - 'currentGasFeeParams is missing gas fee parameters. Required: maxFeePerGas and maxPriorityFeePerGas for EIP-1559 transactions.' + 'currentGasFeeParams is missing gas fee parameters. Required: maxFeePerGas and maxPriorityFeePerGas for EIP-1559 transactions.', ) } else { - return new Error( - 'currentGasFeeParams is missing gas fee parameters. Required: gasPrice for legacy transactions.' - ) + return new Error('currentGasFeeParams is missing gas fee parameters. Required: gasPrice for legacy transactions.') } } @@ -112,7 +100,7 @@ function determineError( const EIP1559RequestMissingGasParams = isEIP1559Transaction && !isEIP1559Params if (EIP1559RequestMissingGasParams) { return new Error( - 'Transaction request specifies EIP-1559 gas values, but currentGasFeeParams lacks corresponding EIP-1559 parameters.' + 'Transaction request specifies EIP-1559 gas values, but currentGasFeeParams lacks corresponding EIP-1559 parameters.', ) } @@ -120,7 +108,7 @@ function determineError( const legacyRequestMissingGasParams = isLegacyTransaction && !isLegacyParams if (legacyRequestMissingGasParams) { return new Error( - 'Transaction request specifies Legacy gasPrice, but currentGasFeeParams lacks a corresponding gasPrice.' + 'Transaction request specifies Legacy gasPrice, but currentGasFeeParams lacks a corresponding gasPrice.', ) } @@ -129,11 +117,7 @@ function determineError( return new Error('Unable to determine gas fee structure.') } -function multiplyByFactor( - value: BigNumberish, - minValue: BigNumberish | null, - adjustmentFactor: number -): string { +function multiplyByFactor(value: BigNumberish, minValue: BigNumberish | null, adjustmentFactor: number): string { const baseValue = BigNumberMax(BigNumber.from(value), BigNumber.from(minValue ?? 0)) return Math.floor(baseValue.toNumber() * adjustmentFactor).toString() } diff --git a/packages/wallet/src/features/gas/api.ts b/packages/wallet/src/features/gas/api.ts index 7e7821b391c..eae93595aad 100644 --- a/packages/wallet/src/features/gas/api.ts +++ b/packages/wallet/src/features/gas/api.ts @@ -1,14 +1,14 @@ import { providers } from 'ethers' +import { PollingInterval } from 'uniswap/src/constants/misc' import { uniswapUrls } from 'uniswap/src/constants/urls' import { useRestQuery } from 'uniswap/src/data/rest' -import { PollingInterval } from 'wallet/src/constants/misc' -import { getPollingIntervalByBlocktime } from 'wallet/src/features/chains/utils' +import { getPollingIntervalByBlocktime } from 'uniswap/src/features/chains/utils' import { GasFeeResponse } from 'wallet/src/features/gas/types' export function useGasFeeQuery( tx: Maybe, skip?: boolean, - pollingInterval?: PollingInterval + pollingInterval?: PollingInterval, ): ReturnType> { return useRestQuery( uniswapUrls.gasServicePath, @@ -20,6 +20,6 @@ export function useGasFeeQuery( pollInterval: pollingInterval ?? getPollingIntervalByBlocktime(tx?.chainId), skip: skip || !tx, ttlMs: pollingInterval ?? getPollingIntervalByBlocktime(tx?.chainId), - } + }, ) } diff --git a/packages/wallet/src/features/gas/formatExternalTxnWithGasEstimates.tsx b/packages/wallet/src/features/gas/formatExternalTxnWithGasEstimates.tsx new file mode 100644 index 00000000000..83a1652b7a1 --- /dev/null +++ b/packages/wallet/src/features/gas/formatExternalTxnWithGasEstimates.tsx @@ -0,0 +1,43 @@ +import { providers } from 'ethers' +import { GasFeeResult } from 'wallet/src/features/gas/types' + +/** + * This util should be used for formatting all external txn requests with gas estimates. This is + * primarily WC transactions and dapp transactions on extension. + * + * We should always be using the estimates from a dapp if they are provided. `gasLimit` will not + * always be included along with fee estimates - use our limit in that case if missing. + * + * If no valid fee combination is found (for legacy type 1, or eip1559 type 2), we should use our own + * estimates instead. Our estimates come from a request to our gas service in both WC and Dapp interaction + * flows. + * + */ + +export function formatExternalTxnWithGasEstimates({ + transaction, + gasFeeResult, +}: { + transaction: providers.TransactionRequest + gasFeeResult: GasFeeResult +}): providers.TransactionRequest { + const { gasLimit: gasLimitDapp, gasPrice, maxFeePerGas, maxPriorityFeePerGas } = transaction + const requestHasLegacyGasValues = !!gasPrice + const requestHasEIP1559GasValues = !!maxFeePerGas && !!maxPriorityFeePerGas + const requestHasValidGasEstimates = requestHasLegacyGasValues || requestHasEIP1559GasValues + + if (requestHasValidGasEstimates) { + return { + ...transaction, + // Avoid `??` in case dapp passes empty string + gasLimit: gasLimitDapp || gasFeeResult?.params?.gasLimit, + } + } + + const formattedTxnWithGasEstimates: providers.TransactionRequest = { + ...transaction, + ...gasFeeResult.params, + } + + return formattedTxnWithGasEstimates +} diff --git a/packages/wallet/src/features/gas/hooks.ts b/packages/wallet/src/features/gas/hooks.ts index 9a53ccf2890..8d5d512fd0d 100644 --- a/packages/wallet/src/features/gas/hooks.ts +++ b/packages/wallet/src/features/gas/hooks.ts @@ -1,8 +1,8 @@ import { BigNumber, providers } from 'ethers' import { useMemo } from 'react' +import { PollingInterval } from 'uniswap/src/constants/misc' import { WalletChainId } from 'uniswap/src/types/chains' import { logger } from 'utilities/src/logger/logger' -import { PollingInterval } from 'wallet/src/constants/misc' import { TRANSACTION_CANCELLATION_GAS_FACTOR } from 'wallet/src/constants/transactions' import { FeeDetails, getAdjustedGasFeeDetails } from 'wallet/src/features/gas/adjustGasFee' import { useGasFeeQuery } from 'wallet/src/features/gas/api' @@ -22,7 +22,7 @@ export function useTransactionGasFee( tx: Maybe, speed: GasSpeed = GasSpeed.Urgent, skip?: boolean, - pollingInterval?: PollingInterval + pollingInterval?: PollingInterval, ): GasFeeResult { const { data, error, loading } = useGasFeeQuery(tx, skip, pollingInterval) @@ -65,9 +65,7 @@ export function useUSDValue(chainId?: WalletChainId, ethValueInWei?: string): st * Construct cancelation transaction with increased gas (based on current network conditions), * then use it to compute new gas info. */ -export function useCancelationGasFeeInfo( - transaction: TransactionDetails -): CancelationGasFeeDetails | undefined { +export function useCancelationGasFeeInfo(transaction: TransactionDetails): CancelationGasFeeDetails | undefined { const cancelationRequest = useMemo(() => { return { chainId: transaction.chainId, @@ -93,7 +91,7 @@ export function useCancelationGasFeeInfo( adjustedFeeDetails = getAdjustedGasFeeDetails( transaction.options.request, baseTxGasFee.params, - TRANSACTION_CANCELLATION_GAS_FACTOR + TRANSACTION_CANCELLATION_GAS_FACTOR, ) } catch (error) { logger.error(error, { diff --git a/packages/wallet/src/features/gating/userPropertyHooks.ts b/packages/wallet/src/features/gating/userPropertyHooks.ts index 464666e1ea2..cf70155d25d 100644 --- a/packages/wallet/src/features/gating/userPropertyHooks.ts +++ b/packages/wallet/src/features/gating/userPropertyHooks.ts @@ -1,11 +1,11 @@ import { useEffect } from 'react' import { Statsig } from 'uniswap/src/features/gating/sdk/statsig' import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks' +import { getValidAddress } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' import { useENSName } from 'wallet/src/features/ens/api' import { AccountType } from 'wallet/src/features/wallet/accounts/types' import { useActiveAccount } from 'wallet/src/features/wallet/hooks' -import { getValidAddress } from 'wallet/src/utils/addresses' export function useGatingUserPropertyUsernames(): void { const activeAccount = useActiveAccount() @@ -24,12 +24,7 @@ export function useGatingUserPropertyUsernames(): void { ens: ens?.split('.')[0], }, }).catch((error) => { - logger.warn( - 'userPropertyHooks', - 'useGatingUserPropertyUsernames', - 'Failed to set usernames for gating', - error - ) + logger.warn('userPropertyHooks', 'useGatingUserPropertyUsernames', 'Failed to set usernames for gating', error) }) } }, [activeAccount, ens, unitag?.username]) diff --git a/packages/wallet/src/features/images/ImageUri.native.tsx b/packages/wallet/src/features/images/ImageUri.native.tsx index 2c78808b89d..53120f4833b 100644 --- a/packages/wallet/src/features/images/ImageUri.native.tsx +++ b/packages/wallet/src/features/images/ImageUri.native.tsx @@ -16,9 +16,7 @@ export function ImageUri({ imageDimensions, ...rest }: ImageUriProps): JSX.Element | null { - const inputImageAspectRatio = imageDimensions - ? imageDimensions?.width / imageDimensions?.height - : 1 + const inputImageAspectRatio = imageDimensions ? imageDimensions?.width / imageDimensions?.height : 1 const [isError, setIsError] = useState(false) const isLoaded = useSharedValue(false) @@ -64,11 +62,7 @@ export function ImageUri({ uri, cache: FastImage.cacheControl.immutable, }} - style={[ - styles.image, - imageStyle ?? [styles.fullWidth, { maxHeight: maxHeight ?? '100%' }], - { aspectRatio }, - ]} + style={[styles.image, imageStyle ?? [styles.fullWidth, { maxHeight: maxHeight ?? '100%' }], { aspectRatio }]} onError={(): void => setIsError(true)} onLoad={({ nativeEvent: { width, height } }: OnLoadEvent): void => { isLoaded.value = true diff --git a/packages/wallet/src/features/images/ImageUri.web.tsx b/packages/wallet/src/features/images/ImageUri.web.tsx index 00612d30943..03ed0ac91d6 100644 --- a/packages/wallet/src/features/images/ImageUri.web.tsx +++ b/packages/wallet/src/features/images/ImageUri.web.tsx @@ -4,12 +4,7 @@ import { Flex, Loader } from 'ui/src' import { ImageUriProps } from 'wallet/src/features/images/ImageUri' import { RemoteImage } from 'wallet/src/features/images/RemoteImage' -export function ImageUri({ - uri, - fallback, - loadingContainerStyle, - imageDimensions, -}: ImageUriProps): JSX.Element | null { +export function ImageUri({ uri, fallback, loadingContainerStyle, imageDimensions }: ImageUriProps): JSX.Element | null { const [height, setHeight] = useState(imageDimensions?.height ?? null) const [width, setWidth] = useState(imageDimensions?.width ?? null) const [isError, setIsError] = useState(false) @@ -28,7 +23,7 @@ export function ImageUri({ }, () => { setIsError(true) - } + }, ) }, [imageDimensions, uri]) @@ -48,13 +43,5 @@ export function ImageUri({ } // TODO: get sizing and other params accounted for - return ( - - ) + return } diff --git a/packages/wallet/src/features/images/NFTPreviewImage.tsx b/packages/wallet/src/features/images/NFTPreviewImage.tsx index ded76ecffad..609b0a2f33a 100644 --- a/packages/wallet/src/features/images/NFTPreviewImage.tsx +++ b/packages/wallet/src/features/images/NFTPreviewImage.tsx @@ -8,22 +8,14 @@ type NFTPreviewProps = { imageProps: ImageUriProps } -export function NFTPreviewImage({ - contractAddress, - tokenId, - imageProps, -}: NFTPreviewProps): JSX.Element | null { +export function NFTPreviewImage({ contractAddress, tokenId, imageProps }: NFTPreviewProps): JSX.Element | null { const { data, loading } = useNftPreviewUri(contractAddress, tokenId) const imageUrl = data?.previews?.image_medium_url if (imageUrl || loading) { return ( - + ) } diff --git a/packages/wallet/src/features/images/NFTViewer.tsx b/packages/wallet/src/features/images/NFTViewer.tsx index a85ccdb71cd..409f86c801d 100644 --- a/packages/wallet/src/features/images/NFTViewer.tsx +++ b/packages/wallet/src/features/images/NFTViewer.tsx @@ -49,18 +49,13 @@ export function NFTViewer(props: Props): JSX.Element { const fallback = useMemo( () => ( - + {placeholderContent || t('tokens.nfts.error.unavailable')} ), - [placeholderContent, maxHeight, t] + [placeholderContent, maxHeight, t], ) if (!imageHttpUri) { @@ -78,9 +73,7 @@ export function NFTViewer(props: Props): JSX.Element { const isGif = isGifUri(imageHttpUri) const formattedUri = - isGif && limitGIFSize - ? convertGIFUriToSmallImageFormat(imageHttpUri, limitGIFSize) - : imageHttpUri + isGif && limitGIFSize ? convertGIFUriToSmallImageFormat(imageHttpUri, limitGIFSize) : imageHttpUri const imageProps: ImageUriProps = { fallback, @@ -106,13 +99,7 @@ export function NFTViewer(props: Props): JSX.Element { } if (!props.showSvgPreview) { - return ( - - ) + return } // Display fallback if preview data is not provided @@ -120,13 +107,7 @@ export function NFTViewer(props: Props): JSX.Element { return fallback } - return ( - - ) + return } const style = StyleSheet.create({ diff --git a/packages/wallet/src/features/images/RemoteImage.tsx b/packages/wallet/src/features/images/RemoteImage.tsx index 8abf2c92325..8d16a21de94 100644 --- a/packages/wallet/src/features/images/RemoteImage.tsx +++ b/packages/wallet/src/features/images/RemoteImage.tsx @@ -45,7 +45,8 @@ export function RemoteImage({ height={height} overflow="hidden" testID={testID} - width={width}> + width={width} + > ) diff --git a/packages/wallet/src/features/images/RemoteSvg.tsx b/packages/wallet/src/features/images/RemoteSvg.tsx index d6a423b30eb..fc40ec9c6ed 100644 --- a/packages/wallet/src/features/images/RemoteSvg.tsx +++ b/packages/wallet/src/features/images/RemoteSvg.tsx @@ -41,7 +41,5 @@ export const RemoteSvg = ({ if (!svg) { return } - return ( - - ) + return } diff --git a/packages/wallet/src/features/images/WebSvgUri.web.tsx b/packages/wallet/src/features/images/WebSvgUri.web.tsx index a9d01ae4600..04e41dc2994 100644 --- a/packages/wallet/src/features/images/WebSvgUri.web.tsx +++ b/packages/wallet/src/features/images/WebSvgUri.web.tsx @@ -3,12 +3,5 @@ import { SvgUriProps } from 'wallet/src/features/images/WebSvgUri' export function WebSvgUri({ maxHeight, uri }: SvgUriProps): JSX.Element { // TODO: get sizing and other params accounted for - return ( - - ) + return } diff --git a/packages/wallet/src/features/images/hooks.ts b/packages/wallet/src/features/images/hooks.ts index b7fc15c4a24..2ea70e0db14 100644 --- a/packages/wallet/src/features/images/hooks.ts +++ b/packages/wallet/src/features/images/hooks.ts @@ -39,16 +39,13 @@ type PreviewsResponse = { } | null } -export function useNftPreviewUri( - contractAddress: string, - tokenId: string -): GqlResult { +export function useNftPreviewUri(contractAddress: string, tokenId: string): GqlResult { return useRestQuery( `/nfts/ethereum/${contractAddress}/${tokenId}`, { contractAddress, tokenId }, ['previews'], { ttlMs: 5 * ONE_MINUTE_MS }, 'GET', - apolloClient + apolloClient, ) } diff --git a/packages/wallet/src/features/images/utils.tsx b/packages/wallet/src/features/images/utils.tsx index e82cf8f4873..4207e58fddb 100644 --- a/packages/wallet/src/features/images/utils.tsx +++ b/packages/wallet/src/features/images/utils.tsx @@ -9,11 +9,7 @@ export type SvgData = { aspectRatio: number } -export async function fetchSVG( - uri: string, - autoplay: boolean, - signal?: AbortSignal -): Promise { +export async function fetchSVG(uri: string, autoplay: boolean, signal?: AbortSignal): Promise { const res = await fetch(uri, { signal }) const text = await res.text() @@ -30,8 +26,7 @@ export async function fetchSVG( let aspectRatio = FALLBACK_ASPECT_RATIO try { - aspectRatio = - viewboxHeight && viewboxWidth ? +viewboxWidth / +viewboxHeight : FALLBACK_ASPECT_RATIO + aspectRatio = viewboxHeight && viewboxWidth ? +viewboxWidth / +viewboxHeight : FALLBACK_ASPECT_RATIO } catch (e) { logger.debug('images/utils', 'fetchSVG', 'Could not calculate aspect ratio ' + e) } diff --git a/packages/wallet/src/features/language/LocalizationContext.tsx b/packages/wallet/src/features/language/LocalizationContext.tsx index b85e408237d..2bb2ad7fb75 100644 --- a/packages/wallet/src/features/language/LocalizationContext.tsx +++ b/packages/wallet/src/features/language/LocalizationContext.tsx @@ -16,8 +16,7 @@ export type LocalizationContextState = { export const LocalizationContext = createContext(undefined) export function LocalizationContextProvider({ children }: { children: ReactNode }): JSX.Element { - const { formatNumberOrString, formatCurrencyAmount, formatPercent, addFiatSymbolToNumber } = - useLocalizedFormatter() + const { formatNumberOrString, formatCurrencyAmount, formatPercent, addFiatSymbolToNumber } = useLocalizedFormatter() const { convertFiatAmount, convertFiatAmountFormatted } = useFiatConverter({ formatNumberOrString, @@ -39,7 +38,7 @@ export function LocalizationContextProvider({ children }: { children: ReactNode formatCurrencyAmount, formatNumberOrString, formatPercent, - ] + ], ) return {children} diff --git a/packages/wallet/src/features/language/formatter.ts b/packages/wallet/src/features/language/formatter.ts index 383cd50f54a..4110fbe5575 100644 --- a/packages/wallet/src/features/language/formatter.ts +++ b/packages/wallet/src/features/language/formatter.ts @@ -43,29 +43,24 @@ export function useLocalizedFormatter(): LocalizedFormatter { const locale = useCurrentLocale() const formatNumberOrStringInner = useCallback( - ({ - value, - type = NumberType.TokenNonTx, - currencyCode, - placeholder, - }: FormatNumberOrStringInput): string => + ({ value, type = NumberType.TokenNonTx, currencyCode, placeholder }: FormatNumberOrStringInput): string => formatNumberOrString({ price: value, locale, currencyCode, type, placeholder }), - [locale] + [locale], ) const formatCurrencyAmountInner = useCallback( ({ value, type, placeholder }: FormatCurrencyAmountInput): string => formatCurrencyAmount({ amount: value, locale, type, placeholder }), - [locale] + [locale], ) const formatPercentInner = useCallback( (value: Maybe): string => formatPercent(value, locale), - [locale] + [locale], ) const addFiatSymbolToNumberInner = useCallback( ({ value, currencyCode, currencySymbol }: AddFiatSymbolToNumberInput): string => addFiatSymbolToNumber({ value, currencyCode, currencySymbol, locale }), - [locale] + [locale], ) return useMemo( @@ -75,11 +70,6 @@ export function useLocalizedFormatter(): LocalizedFormatter { formatPercent: formatPercentInner, addFiatSymbolToNumber: addFiatSymbolToNumberInner, }), - [ - formatNumberOrStringInner, - formatCurrencyAmountInner, - formatPercentInner, - addFiatSymbolToNumberInner, - ] + [formatNumberOrStringInner, formatCurrencyAmountInner, formatPercentInner, addFiatSymbolToNumberInner], ) } diff --git a/packages/wallet/src/features/language/saga.ts b/packages/wallet/src/features/language/saga.ts index bdafe719feb..784bbdd1ac8 100644 --- a/packages/wallet/src/features/language/saga.ts +++ b/packages/wallet/src/features/language/saga.ts @@ -14,11 +14,7 @@ import { mapLocaleToLanguage, } from 'wallet/src/features/language/constants' import { getLocale } from 'wallet/src/features/language/hooks' -import { - selectCurrentLanguage, - setCurrentLanguage, - updateLanguage, -} from 'wallet/src/features/language/slice' +import { selectCurrentLanguage, setCurrentLanguage, updateLanguage } from 'wallet/src/features/language/slice' export function* appLanguageWatcherSaga() { yield* takeLatest(updateLanguage.type, appLanguageSaga) @@ -46,11 +42,7 @@ function* appLanguageSaga(action: ReturnType) { try { yield* call([i18n, i18n.changeLanguage], localeToSet) } catch (error) { - logger.warn( - 'language/saga', - 'appLanguageSaga', - 'Sync of language setting state and i18n instance failed' - ) + logger.warn('language/saga', 'appLanguageSaga', 'Sync of language setting state and i18n instance failed') } yield* call(restartAppIfRTL, localeToSet) @@ -82,11 +74,7 @@ function getDeviceLanguage(): Language { function restartAppIfRTL(currentLocale: Locale) { const isRtl = i18n.dir(currentLocale) === 'rtl' if (isRtl !== I18nManager.isRTL) { - logger.debug( - 'saga.ts', - 'restartAppIfRTL', - `Changing RTL to ${isRtl} for locale ${currentLocale}` - ) + logger.debug('saga.ts', 'restartAppIfRTL', `Changing RTL to ${isRtl} for locale ${currentLocale}`) I18nManager.forceRTL(isRtl) // Need to restart to apply RTL changes diff --git a/packages/wallet/src/features/language/slice.ts b/packages/wallet/src/features/language/slice.ts index ad994e6462c..2889ff3c5fe 100644 --- a/packages/wallet/src/features/language/slice.ts +++ b/packages/wallet/src/features/language/slice.ts @@ -24,10 +24,8 @@ const slice = createSlice({ export const { setCurrentLanguage, resetSettings } = slice.actions export const updateLanguage = createAction('language/updateLanguage') -export const syncAppWithDeviceLanguage = (): ReturnType => - updateLanguage(null) +export const syncAppWithDeviceLanguage = (): ReturnType => updateLanguage(null) -export const selectCurrentLanguage = (state: RootState): Language => - state.languageSettings.currentLanguage +export const selectCurrentLanguage = (state: RootState): Language => state.languageSettings.currentLanguage export const languageSettingsReducer = slice.reducer diff --git a/packages/wallet/src/features/nfts/hooks.ts b/packages/wallet/src/features/nfts/hooks.ts index c97d20ba19b..51b6c28bf76 100644 --- a/packages/wallet/src/features/nfts/hooks.ts +++ b/packages/wallet/src/features/nfts/hooks.ts @@ -1,10 +1,7 @@ import { useMemo } from 'react' -import { - NftsQuery, - useNftsQuery, -} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { PollingInterval } from 'uniswap/src/constants/misc' +import { NftsQuery, useNftsQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GqlResult } from 'uniswap/src/data/types' -import { PollingInterval } from 'wallet/src/constants/misc' import { selectNftsVisibility } from 'wallet/src/features/favorites/selectors' import { EMPTY_NFT_ITEM, @@ -19,11 +16,7 @@ export type GQLNftAsset = NonNullable< NonNullable[0]>['nftBalances']>[0] >['ownedAsset'] -export function useNFT( - owner: Address = '', - address?: Address, - tokenId?: string -): GqlResult { +export function useNFT(owner: Address = '', address?: Address, tokenId?: string): GqlResult { // TODO: [MOB-227] do a direct cache lookup in Apollo using id instead of re-querying const { data, loading, refetch } = useNftsQuery({ variables: { ownerAddress: owner }, @@ -34,11 +27,9 @@ export function useNFT( const nft = useMemo( () => data?.portfolios?.[0]?.nftBalances?.find( - (balance) => - balance?.ownedAsset?.nftContract?.address === address && - balance?.ownedAsset?.tokenId === tokenId + (balance) => balance?.ownedAsset?.nftContract?.address === address && balance?.ownedAsset?.tokenId === tokenId, )?.ownedAsset ?? undefined, - [data, address, tokenId] + [data, address, tokenId], ) return { data: nft, loading, refetch } @@ -47,7 +38,7 @@ export function useNFT( // Apply to NFTs fetched from API hidden filter, which is stored in Redux export function useGroupNftsByVisibility( nftDataItems: Array | undefined, - showHidden: boolean + showHidden: boolean, ): { nfts: Array numHidden: number @@ -73,7 +64,7 @@ export function useGroupNftsByVisibility( } return acc }, - { shown: [], hidden: [] } + { shown: [], hidden: [] }, ) return { nfts: [ diff --git a/packages/wallet/src/features/nfts/types.ts b/packages/wallet/src/features/nfts/types.ts index c7af4774ec3..7e17794e73e 100644 --- a/packages/wallet/src/features/nfts/types.ts +++ b/packages/wallet/src/features/nfts/types.ts @@ -1,7 +1,4 @@ -import { - Chain, - IAmount, -} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { Chain, IAmount } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' export type NFTItem = { name?: string diff --git a/packages/wallet/src/features/nfts/useNftContextMenu.tsx b/packages/wallet/src/features/nfts/useNftContextMenu.tsx index c98c24dcf7a..4120f5cb1aa 100644 --- a/packages/wallet/src/features/nfts/useNftContextMenu.tsx +++ b/packages/wallet/src/features/nfts/useNftContextMenu.tsx @@ -68,7 +68,7 @@ export function useNFTContextMenu({ visible: !hidden, hideDelay: 2 * ONE_SECOND_MS, assetName: 'NFT', - }) + }), ) } }, [nftKey, dispatch, hidden, showNotification, isSpam]) @@ -88,9 +88,7 @@ export function useNFTContextMenu({ : []), ...((isLocalAccount && [ { - title: hidden - ? t('tokens.nfts.hidden.action.unhide') - : t('tokens.nfts.hidden.action.hide'), + title: hidden ? t('tokens.nfts.hidden.action.unhide') : t('tokens.nfts.hidden.action.hide'), ...(isWeb ? { Icon: hidden ? Eye : EyeOff, @@ -105,14 +103,14 @@ export function useNFTContextMenu({ []), ] : [], - [nftKey, t, onPressShare, isLocalAccount, hidden, onPressHiddenStatus] + [nftKey, t, onPressShare, isLocalAccount, hidden, onPressHiddenStatus], ) const onContextMenuPress = useCallback( async (e: NativeSyntheticEvent): Promise => { await menuActions[e.nativeEvent.index]?.onPress?.() }, - [menuActions] + [menuActions], ) return { menuActions, onContextMenuPress, onlyShare: !!nftKey && !isLocalAccount } diff --git a/packages/wallet/src/features/notifications/buildReceiveNotification.ts b/packages/wallet/src/features/notifications/buildReceiveNotification.ts index 1a3568292ca..a8b035723b0 100644 --- a/packages/wallet/src/features/notifications/buildReceiveNotification.ts +++ b/packages/wallet/src/features/notifications/buildReceiveNotification.ts @@ -4,11 +4,7 @@ import { ReceiveCurrencyTxNotification, ReceiveNFTNotification, } from 'wallet/src/features/notifications/types' -import { - TransactionDetails, - TransactionStatus, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { TransactionDetails, TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' /** * Based on notification type info, returns an AppNotification object for either NFT or Currency receive. @@ -20,7 +16,7 @@ import { export function buildReceiveNotification( transactionDetails: TransactionDetails, - receivingAddress: Address // not included in transactionDetails + receivingAddress: Address, // not included in transactionDetails ): ReceiveNFTNotification | ReceiveCurrencyTxNotification | undefined { const { typeInfo, status, chainId, hash, id } = transactionDetails @@ -38,11 +34,7 @@ export function buildReceiveNotification( } // Currency receive txn. - if ( - typeInfo?.assetType === AssetType.Currency && - typeInfo?.currencyAmountRaw && - typeInfo?.sender - ) { + if (typeInfo?.assetType === AssetType.Currency && typeInfo?.currencyAmountRaw && typeInfo?.sender) { return { ...baseNotificationData, type: AppNotificationType.Transaction, @@ -55,10 +47,7 @@ export function buildReceiveNotification( } // NFT receive txn. - if ( - (typeInfo?.assetType === AssetType.ERC1155 || typeInfo?.assetType === AssetType.ERC721) && - typeInfo?.tokenId - ) { + if ((typeInfo?.assetType === AssetType.ERC1155 || typeInfo?.assetType === AssetType.ERC721) && typeInfo?.tokenId) { return { ...baseNotificationData, type: AppNotificationType.Transaction, diff --git a/packages/wallet/src/features/notifications/components/ApproveNotification.tsx b/packages/wallet/src/features/notifications/components/ApproveNotification.tsx index 2d1f7aedfb2..2fbea6fb812 100644 --- a/packages/wallet/src/features/notifications/components/ApproveNotification.tsx +++ b/packages/wallet/src/features/notifications/components/ApproveNotification.tsx @@ -1,3 +1,4 @@ +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { AssetType } from 'wallet/src/entities/assets' @@ -6,7 +7,6 @@ import { NOTIFICATION_ICON_SIZE } from 'wallet/src/features/notifications/consta import { ApproveTxNotification } from 'wallet/src/features/notifications/types' import { formApproveNotificationTitle } from 'wallet/src/features/notifications/utils' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' export function ApproveNotification({ notification: { address, chainId, tokenAddress, spender, txStatus, txType, hideDelay }, @@ -17,12 +17,7 @@ export function ApproveNotification({ const currencyInfo = useCurrencyInfo(buildCurrencyId(chainId, tokenAddress)) - const title = formApproveNotificationTitle( - txStatus, - currencyInfo?.currency, - tokenAddress, - spender - ) + const title = formApproveNotificationTitle(txStatus, currencyInfo?.currency, tokenAddress, spender) const icon = ( + ) : ( - + ) } title={ diff --git a/packages/wallet/src/features/notifications/components/NetworkChangedNotification.test.tsx b/packages/wallet/src/features/notifications/components/NetworkChangedNotification.test.tsx index 9bcc1aa27f3..db7df6f86f1 100644 --- a/packages/wallet/src/features/notifications/components/NetworkChangedNotification.test.tsx +++ b/packages/wallet/src/features/notifications/components/NetworkChangedNotification.test.tsx @@ -12,7 +12,7 @@ describe(NetworkChangedNotification, () => { chainId: UniverseChainId.Mainnet, flow: 'swap', }} - /> + />, ) const title = queryByText('Swapping on Ethereum') expect(title).toBeTruthy() @@ -26,7 +26,7 @@ describe(NetworkChangedNotification, () => { chainId: UniverseChainId.Mainnet, flow: 'send', }} - /> + />, ) const title = queryByText('Sending on Ethereum') expect(title).toBeTruthy() @@ -39,7 +39,7 @@ describe(NetworkChangedNotification, () => { type: AppNotificationType.NetworkChanged, chainId: UniverseChainId.Mainnet, }} - /> + />, ) const title = queryByText('Switched to Ethereum') expect(title).toBeTruthy() diff --git a/packages/wallet/src/features/notifications/components/NotSupportedNetworkNotification.test.tsx b/packages/wallet/src/features/notifications/components/NotSupportedNetworkNotification.test.tsx index 7ef2a1df222..6bcad82fc12 100644 --- a/packages/wallet/src/features/notifications/components/NotSupportedNetworkNotification.test.tsx +++ b/packages/wallet/src/features/notifications/components/NotSupportedNetworkNotification.test.tsx @@ -5,9 +5,7 @@ import { renderWithProviders } from 'wallet/src/test/render' describe(NotSupportedNetworkNotification, () => { it('renders without error', () => { const tree = renderWithProviders( - + , ) expect(tree).toMatchSnapshot() diff --git a/packages/wallet/src/features/notifications/components/NotificationToast.tsx b/packages/wallet/src/features/notifications/components/NotificationToast.tsx index ffcddb4a515..b7d444ef5db 100644 --- a/packages/wallet/src/features/notifications/components/NotificationToast.tsx +++ b/packages/wallet/src/features/notifications/components/NotificationToast.tsx @@ -3,12 +3,7 @@ // the tamagui optimizer disabling optimization for now on this file import { useCallback, useEffect } from 'react' -import { - Directions, - FlingGestureHandler, - FlingGestureHandlerGestureEvent, - State, -} from 'react-native-gesture-handler' +import { Directions, FlingGestureHandler, FlingGestureHandlerGestureEvent, State } from 'react-native-gesture-handler' import { useAnimatedStyle, useSharedValue, withDelay, withSpring } from 'react-native-reanimated' import { Flex, @@ -25,7 +20,7 @@ import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { borderRadii, spacing } from 'ui/src/theme' import { useTimeout } from 'utilities/src/time/timing' import { selectActiveAccountNotifications } from 'wallet/src/features/notifications/selectors' -import { popNotification } from 'wallet/src/features/notifications/slice' +import { popNotification, setNotificationViewed } from 'wallet/src/features/notifications/slice' import { useAppDispatch, useAppSelector } from 'wallet/src/state' const NOTIFICATION_HEIGHT = 64 @@ -63,8 +58,9 @@ const ToastEntryAnimation = styled(Flex, { top: '$spacing12', position: 'absolute', width: '100%', - zIndex: '$modal', + zIndex: '$overlay', opacity: 1, + pointerEvents: 'none', enterStyle: { y: HIDE_OFFSET_Y, @@ -72,6 +68,8 @@ const ToastEntryAnimation = styled(Flex, { }, }) +const SPRING_ANIMATION_DELAY = 100 + export function NotificationToast({ subtitle, title, @@ -92,17 +90,29 @@ export function NotificationToast({ const showOffset = useDeviceInsets().top + spacing.spacing4 + (isWeb ? spacing.spacing12 : 0) const bannerOffset = useSharedValue(HIDE_OFFSET_Y) + // Run this only once to ensure that if a new notification is created it doesn't show on the next screen + useEffect(() => { + if (currentNotification?.shown) { + dispatch(popNotification({ address })) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [address, dispatch]) + useEffect(() => { if (currentNotification) { - bannerOffset.value = withDelay(100, withSpring(showOffset, SPRING_ANIMATION)) + bannerOffset.value = withDelay(SPRING_ANIMATION_DELAY, withSpring(showOffset, SPRING_ANIMATION)) + // delay to ensure the notification is shown if theres a quick navigation event + setTimeout(() => { + dispatch(setNotificationViewed({ address })) + }, SPRING_ANIMATION_DELAY * 2) } - }, [showOffset, bannerOffset, currentNotification]) + }, [showOffset, bannerOffset, currentNotification, dispatch, address]) const animatedStyle = useAnimatedStyle( () => ({ transform: [{ translateY: bannerOffset.value }], }), - [bannerOffset] + [bannerOffset], ) const dismissLatest = useCallback(() => { @@ -146,19 +156,14 @@ export function NotificationToast({ borderColor="$surface3" borderRadius={smallToast ? SMALL_TOAST_RADIUS : LARGE_TOAST_RADIUS} borderWidth={TOAST_BORDER_WIDTH} - mx={smallToast ? 'auto' : '$spacing24'}> + mx={smallToast ? 'auto' : '$spacing12'} + pointerEvents="auto" + > {smallToast ? ( - + ) : ( ) - return ( + return isWeb ? ( + {notificationContent} + ) : ( - {isWeb ? ( - {notificationContent} - ) : ( - - {notificationContent} - - )} + + {notificationContent} + ) } @@ -206,9 +210,10 @@ function NotificationContent({ flex={1} flexDirection="row" minHeight={NOTIFICATION_HEIGHT} - p="$spacing16" + p="$spacing12" onPress={onPress} - onPressIn={onPressIn}> + onPressIn={onPressIn} + > + justifyContent="flex-start" + > {icon} @@ -243,12 +249,7 @@ function NotificationContent({ ) } -function NotificationContentSmall({ - title, - icon, - onPress, - onPressIn, -}: NotificationContentProps): JSX.Element { +function NotificationContentSmall({ title, icon, onPress, onPressIn }: NotificationContentProps): JSX.Element { return ( + onPressIn={onPressIn} + > {icon} diff --git a/packages/wallet/src/features/notifications/components/PasswordChangedNotification.tsx b/packages/wallet/src/features/notifications/components/PasswordChangedNotification.tsx index e1d0315bc0b..404fda96484 100644 --- a/packages/wallet/src/features/notifications/components/PasswordChangedNotification.tsx +++ b/packages/wallet/src/features/notifications/components/PasswordChangedNotification.tsx @@ -8,7 +8,5 @@ export function PasswordChangedNotification({ notification: PasswordChangedNotificationType }): JSX.Element { const { t } = useTranslation() - return ( - - ) + return } diff --git a/packages/wallet/src/features/notifications/components/PendingNotificationBadge.tsx b/packages/wallet/src/features/notifications/components/PendingNotificationBadge.tsx index 58bef86ab21..60f4354084e 100644 --- a/packages/wallet/src/features/notifications/components/PendingNotificationBadge.tsx +++ b/packages/wallet/src/features/notifications/components/PendingNotificationBadge.tsx @@ -17,9 +17,7 @@ interface Props { size?: number } -export function PendingNotificationBadge({ - size = LOADING_SPINNER_SIZE, -}: Props): JSX.Element | null { +export function PendingNotificationBadge({ size = LOADING_SPINNER_SIZE }: Props): JSX.Element | null { const colors = useSporeColors() const activeAccountAddress = useAppSelector(selectActiveAccountAddress) const notifications = useAppSelector(selectActiveAccountNotifications) @@ -40,12 +38,10 @@ export function PendingNotificationBadge({ /*************** Pending in-app txn **************/ - const swapPendingNotificationActive = - currentNotification?.type === AppNotificationType.SwapPending + const swapPendingNotificationActive = currentNotification?.type === AppNotificationType.SwapPending const pendingTransactionCount = (sortedPendingTransactions ?? []).length const txPendingLongerThanLimit = - sortedPendingTransactions?.[0] && - Date.now() - sortedPendingTransactions[0].addedTime > PENDING_TX_TIME_LIMIT + sortedPendingTransactions?.[0] && Date.now() - sortedPendingTransactions[0].addedTime > PENDING_TX_TIME_LIMIT // If a transaction has been pending for longer than 5 mins, then don't show the pending icon anymore // Dont show the loader if the swap pending toast is on screen @@ -65,12 +61,7 @@ export function PendingNotificationBadge({ if (hasNotifications) { return ( - + ) } diff --git a/packages/wallet/src/features/notifications/components/SharedNotificationToastRouter.tsx b/packages/wallet/src/features/notifications/components/SharedNotificationToastRouter.tsx index 1e2566a4c1a..624dde45078 100644 --- a/packages/wallet/src/features/notifications/components/SharedNotificationToastRouter.tsx +++ b/packages/wallet/src/features/notifications/components/SharedNotificationToastRouter.tsx @@ -19,11 +19,7 @@ import { WrapNotification } from 'wallet/src/features/notifications/components/W import { AppNotification, AppNotificationType } from 'wallet/src/features/notifications/types' import { TransactionType } from 'wallet/src/features/transactions/types' -export function SharedNotificationToastRouter({ - notification, -}: { - notification: AppNotification -}): JSX.Element | null { +export function SharedNotificationToastRouter({ notification }: { notification: AppNotification }): JSX.Element | null { switch (notification.type) { case AppNotificationType.Default: return diff --git a/packages/wallet/src/features/notifications/components/SwapNotification.tsx b/packages/wallet/src/features/notifications/components/SwapNotification.tsx index b39a473445b..6b8a1459717 100644 --- a/packages/wallet/src/features/notifications/components/SwapNotification.tsx +++ b/packages/wallet/src/features/notifications/components/SwapNotification.tsx @@ -39,7 +39,7 @@ export function SwapNotification({ outputCurrencyId, inputCurrencyAmountRaw, outputCurrencyAmountRaw, - tradeType + tradeType, ) const { t } = useTranslation() diff --git a/packages/wallet/src/features/notifications/components/SwapPendingNotification.tsx b/packages/wallet/src/features/notifications/components/SwapPendingNotification.tsx index de4dc70f531..5b93f2ca003 100644 --- a/packages/wallet/src/features/notifications/components/SwapPendingNotification.tsx +++ b/packages/wallet/src/features/notifications/components/SwapPendingNotification.tsx @@ -10,11 +10,7 @@ import { WrapType } from 'wallet/src/features/transactions/types' // and when a txn confirms it ll replace this toast. export const TRANSACTION_PENDING_NOTIFICATION_DELAY = 12 * ONE_SECOND_MS -export function SwapPendingNotification({ - notification, -}: { - notification: SwapPendingNotificationType -}): JSX.Element { +export function SwapPendingNotification({ notification }: { notification: SwapPendingNotificationType }): JSX.Element { const { t } = useTranslation() const notificationText = getNotificationText(notification.wrapType, t) diff --git a/packages/wallet/src/features/notifications/components/TransferCurrencyNotification.tsx b/packages/wallet/src/features/notifications/components/TransferCurrencyNotification.tsx index 7378ffa5166..bc004b141ae 100644 --- a/packages/wallet/src/features/notifications/components/TransferCurrencyNotification.tsx +++ b/packages/wallet/src/features/notifications/components/TransferCurrencyNotification.tsx @@ -1,3 +1,4 @@ +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { useENS } from 'wallet/src/features/ens/useENS' @@ -8,7 +9,6 @@ import { TransferCurrencyTxNotification } from 'wallet/src/features/notification import { formTransferCurrencyNotificationTitle } from 'wallet/src/features/notifications/utils' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { TransactionType } from 'wallet/src/features/transactions/types' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' export function TransferCurrencyNotification({ notification, @@ -16,18 +16,8 @@ export function TransferCurrencyNotification({ notification: TransferCurrencyTxNotification }): JSX.Element { const formatter = useLocalizationContext() - const { - address, - assetType, - chainId, - tokenAddress, - currencyAmountRaw, - txType, - txStatus, - hideDelay, - } = notification - const senderOrRecipient = - txType === TransactionType.Send ? notification.recipient : notification.sender + const { address, assetType, chainId, tokenAddress, currencyAmountRaw, txType, txStatus, hideDelay } = notification + const senderOrRecipient = txType === TransactionType.Send ? notification.recipient : notification.sender const { name: ensName } = useENS(chainId, senderOrRecipient) const currencyInfo = useCurrencyInfo(buildCurrencyId(chainId, tokenAddress)) @@ -38,7 +28,7 @@ export function TransferCurrencyNotification({ currencyInfo?.currency, tokenAddress, currencyAmountRaw, - ensName ?? senderOrRecipient + ensName ?? senderOrRecipient, ) const { navigateToAccountActivityList } = useWalletNavigation() diff --git a/packages/wallet/src/features/notifications/components/TransferNFTNotification.tsx b/packages/wallet/src/features/notifications/components/TransferNFTNotification.tsx index 1d90a0e935b..3e3cd9025d2 100644 --- a/packages/wallet/src/features/notifications/components/TransferNFTNotification.tsx +++ b/packages/wallet/src/features/notifications/components/TransferNFTNotification.tsx @@ -9,16 +9,10 @@ import { formTransferNFTNotificationTitle } from 'wallet/src/features/notificati import { TransactionType } from 'wallet/src/features/transactions/types' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' -export function TransferNFTNotification({ - notification, -}: { - notification: TransferNFTTxNotification -}): JSX.Element { - const { address, assetType, chainId, tokenAddress, tokenId, txType, txStatus, hideDelay } = - notification +export function TransferNFTNotification({ notification }: { notification: TransferNFTTxNotification }): JSX.Element { + const { address, assetType, chainId, tokenAddress, tokenId, txType, txStatus, hideDelay } = notification const userAddress = useActiveAccountAddressWithThrow() - const senderOrRecipient = - txType === TransactionType.Send ? notification.recipient : notification.sender + const senderOrRecipient = txType === TransactionType.Send ? notification.recipient : notification.sender const nftOwner = txType === TransactionType.Send ? notification.recipient : userAddress const { data: nft } = useNFT(nftOwner, tokenAddress, tokenId) const { name: ensName } = useENS(chainId, senderOrRecipient) @@ -29,7 +23,7 @@ export function TransferNFTNotification({ nft, tokenAddress, tokenId, - ensName ?? senderOrRecipient + ensName ?? senderOrRecipient, ) const { navigateToAccountActivityList } = useWalletNavigation() diff --git a/packages/wallet/src/features/notifications/components/UnknownNotification.tsx b/packages/wallet/src/features/notifications/components/UnknownNotification.tsx index f2d7e4e54c4..9e9c8d640d8 100644 --- a/packages/wallet/src/features/notifications/components/UnknownNotification.tsx +++ b/packages/wallet/src/features/notifications/components/UnknownNotification.tsx @@ -1,3 +1,5 @@ +import { AlertTriangle, CheckmarkCircle } from 'ui/src/components/icons' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { AssetType } from 'wallet/src/entities/assets' @@ -7,7 +9,7 @@ import { NOTIFICATION_ICON_SIZE } from 'wallet/src/features/notifications/consta import { TransactionNotificationBase } from 'wallet/src/features/notifications/types' import { formUnknownTxTitle } from 'wallet/src/features/notifications/utils' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' +import { TransactionStatus } from 'wallet/src/features/transactions/types' export function UnknownTxNotification({ notification: { address, chainId, tokenAddress, txStatus, txType, hideDelay }, @@ -15,11 +17,9 @@ export function UnknownTxNotification({ notification: TransactionNotificationBase }): JSX.Element { const { name: ensName } = useENS(chainId, tokenAddress) - const currencyInfo = useCurrencyInfo( - tokenAddress ? buildCurrencyId(chainId, tokenAddress) : undefined - ) + const currencyInfo = useCurrencyInfo(tokenAddress ? buildCurrencyId(chainId, tokenAddress) : undefined) const title = formUnknownTxTitle(txStatus, tokenAddress, ensName) - const icon = ( + const icon = currencyInfo ? ( + ) : txStatus === TransactionStatus.Success ? ( + + ) : ( + ) const { navigateToAccountActivityList } = useWalletNavigation() diff --git a/packages/wallet/src/features/notifications/components/WrapNotification.tsx b/packages/wallet/src/features/notifications/components/WrapNotification.tsx index bf35a7f5626..da37a804451 100644 --- a/packages/wallet/src/features/notifications/components/WrapNotification.tsx +++ b/packages/wallet/src/features/notifications/components/WrapNotification.tsx @@ -6,10 +6,7 @@ import { NotificationToast } from 'wallet/src/features/notifications/components/ import { NOTIFICATION_ICON_SIZE } from 'wallet/src/features/notifications/constants' import { WrapTxNotification } from 'wallet/src/features/notifications/types' import { formWrapNotificationTitle } from 'wallet/src/features/notifications/utils' -import { - useNativeCurrencyInfo, - useWrappedNativeCurrencyInfo, -} from 'wallet/src/features/tokens/useCurrencyInfo' +import { useNativeCurrencyInfo, useWrappedNativeCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { useCreateWrapFormState } from 'wallet/src/features/transactions/hooks' import { TransactionStatus } from 'wallet/src/features/transactions/types' @@ -32,7 +29,7 @@ export function WrapNotification({ inputCurrencyInfo?.currency, outputCurrencyInfo?.currency, currencyAmountRaw, - unwrapped + unwrapped, ) const wrapFormState = useCreateWrapFormState( @@ -40,7 +37,7 @@ export function WrapNotification({ chainId, txId, inputCurrencyInfo?.currency, - outputCurrencyInfo?.currency + outputCurrencyInfo?.currency, ) const { navigateToAccountActivityList, navigateToSwapFlow } = useWalletNavigation() diff --git a/packages/wallet/src/features/notifications/components/__snapshots__/NotSupportedNetworkNotification.test.tsx.snap b/packages/wallet/src/features/notifications/components/__snapshots__/NotSupportedNetworkNotification.test.tsx.snap index e89a292e048..8b1e2c635b0 100644 --- a/packages/wallet/src/features/notifications/components/__snapshots__/NotSupportedNetworkNotification.test.tsx.snap +++ b/packages/wallet/src/features/notifications/components/__snapshots__/NotSupportedNetworkNotification.test.tsx.snap @@ -41,6 +41,7 @@ exports[`NotSupportedNetworkNotification renders without error 1`] = ` } > { diff --git a/packages/wallet/src/features/notifications/selectors.ts b/packages/wallet/src/features/notifications/selectors.ts index 10df189787b..a4195a8130d 100644 --- a/packages/wallet/src/features/notifications/selectors.ts +++ b/packages/wallet/src/features/notifications/selectors.ts @@ -3,8 +3,7 @@ import { AppNotification } from 'wallet/src/features/notifications/types' import { selectActiveAccountAddress } from 'wallet/src/features/wallet/selectors' import { SharedState } from 'wallet/src/state/reducer' -const selectNotificationQueue = (state: SharedState): AppNotification[] => - state.notifications.notificationQueue +const selectNotificationQueue = (state: SharedState): AppNotification[] => state.notifications.notificationQueue export const selectActiveAccountNotifications = createSelector( selectNotificationQueue, @@ -15,20 +14,16 @@ export const selectActiveAccountNotifications = createSelector( } // If a notification doesn't have an address param assume it belongs to the active account return notificationQueue.filter((notif) => !notif.address || notif.address === address) - } + }, ) const selectNotificationStatus = ( - state: SharedState + state: SharedState, ): { [userAddress: string]: boolean | undefined } => state.notifications.notificationStatus -export const makeSelectHasNotifications = (): Selector< - SharedState, - boolean | undefined, - [Address | null] -> => +export const makeSelectHasNotifications = (): Selector => createSelector( selectNotificationStatus, (_: SharedState, address: Address | null) => address, @@ -37,11 +32,11 @@ export const makeSelectHasNotifications = (): Selector< return undefined } return notificationStatuses?.[address] - } + }, ) export const selectLastTxNotificationUpdate = ( - state: SharedState + state: SharedState, ): { [address: string]: number | undefined } => state.notifications.lastTxNotificationUpdate diff --git a/packages/wallet/src/features/notifications/slice.ts b/packages/wallet/src/features/notifications/slice.ts index 708ab18ad2b..2b9d22da093 100644 --- a/packages/wallet/src/features/notifications/slice.ts +++ b/packages/wallet/src/features/notifications/slice.ts @@ -27,29 +27,37 @@ const slice = createSlice({ if (!address) { state.notificationQueue.shift() } else { - const indexToRemove = state.notificationQueue.findIndex( - (notif) => notif.address === address - ) + const indexToRemove = state.notificationQueue.findIndex((notif) => notif.address === address) if (indexToRemove !== -1) { state.notificationQueue.splice(indexToRemove, 1) } } }, + setNotificationViewed: (state, action: PayloadAction<{ address: Maybe
}>) => { + const { address } = action.payload + if (!address) { + if (state.notificationQueue[0]) { + state.notificationQueue[0].shown = true + } + } else { + const indexToChange = state.notificationQueue.findIndex((notif) => notif.address === address) + if (indexToChange !== -1) { + const itemToChange = state.notificationQueue[indexToChange] + if (itemToChange) { + itemToChange.shown = true + } + } + } + }, clearNotificationQueue: (state) => { state.notificationQueue = [] }, resetNotifications: () => initialNotificationsState, - setNotificationStatus: ( - state, - action: PayloadAction<{ address: Address; hasNotifications: boolean }> - ) => { + setNotificationStatus: (state, action: PayloadAction<{ address: Address; hasNotifications: boolean }>) => { const { address, hasNotifications } = action.payload state.notificationStatus = { ...state.notificationStatus, [address]: hasNotifications } }, - setLastTxNotificationUpdate: ( - state, - { payload }: PayloadAction<{ address: Address; timestamp: number }> - ) => { + setLastTxNotificationUpdate: (state, { payload }: PayloadAction<{ address: Address; timestamp: number }>) => { const { address, timestamp } = payload state.lastTxNotificationUpdate[address] = timestamp }, @@ -59,6 +67,7 @@ const slice = createSlice({ export const { pushNotification, popNotification, + setNotificationViewed, clearNotificationQueue, resetNotifications, setNotificationStatus, diff --git a/packages/wallet/src/features/notifications/testingUtils.ts b/packages/wallet/src/features/notifications/testingUtils.ts index 8d461cf87a1..1d7c7e1ae5a 100644 --- a/packages/wallet/src/features/notifications/testingUtils.ts +++ b/packages/wallet/src/features/notifications/testingUtils.ts @@ -51,7 +51,7 @@ export const useMockNotification = (ms?: number): void => { ...exampleSwapSuccess, hideDelay: ms ?? exampleSwapSuccess.hideDelay, address: activeAddress, - }) + }), ) setSent(true) } diff --git a/packages/wallet/src/features/notifications/types.ts b/packages/wallet/src/features/notifications/types.ts index e8d4b2a8afe..7a149593efc 100644 --- a/packages/wallet/src/features/notifications/types.ts +++ b/packages/wallet/src/features/notifications/types.ts @@ -3,11 +3,7 @@ import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { WalletChainId } from 'uniswap/src/types/chains' import { WalletConnectEvent } from 'uniswap/src/types/walletConnect' import { AssetType } from 'wallet/src/entities/assets' -import { - FinalizedTransactionStatus, - TransactionType, - WrapType, -} from 'wallet/src/features/transactions/types' +import { FinalizedTransactionStatus, TransactionType, WrapType } from 'wallet/src/features/transactions/types' export enum AppNotificationType { Default, @@ -35,6 +31,7 @@ export interface AppNotificationBase { type: AppNotificationType address?: Address hideDelay?: number + shown?: boolean } export interface AppNotificationDefault extends AppNotificationBase { @@ -123,9 +120,7 @@ export interface UnknownTxNotification extends TransactionNotificationBase { txType: TransactionType.Unknown } -export type TransferCurrencyTxNotification = - | SendCurrencyTxNotification - | ReceiveCurrencyTxNotification +export type TransferCurrencyTxNotification = SendCurrencyTxNotification | ReceiveCurrencyTxNotification export type TransferNFTTxNotification = SendNFTNotification | ReceiveNFTNotification diff --git a/packages/wallet/src/features/notifications/utils.ts b/packages/wallet/src/features/notifications/utils.ts index edb305999c2..4f3d54680c7 100644 --- a/packages/wallet/src/features/notifications/utils.ts +++ b/packages/wallet/src/features/notifications/utils.ts @@ -1,16 +1,16 @@ import { Currency, TradeType } from '@uniswap/sdk-core' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import i18n from 'uniswap/src/i18n/i18n' import { WalletConnectEvent } from 'uniswap/src/types/walletConnect' +import { getValidAddress, shortenAddress } from 'uniswap/src/utils/addresses' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' +import { currencyIdToAddress } from 'uniswap/src/utils/currencyId' import { LocalizationContextState } from 'wallet/src/features/language/LocalizationContext' import { GQLNftAsset } from 'wallet/src/features/nfts/hooks' import { WalletConnectNotification } from 'wallet/src/features/notifications/types' import { TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' -import { getValidAddress, shortenAddress } from 'wallet/src/utils/addresses' import { getCurrencyDisplayText, getFormattedCurrencyAmount } from 'wallet/src/utils/currency' -import { currencyIdToAddress } from 'wallet/src/utils/currencyId' export const formWCNotificationTitle = (appNotification: WalletConnectNotification): string => { const { event, dappName, chainId } = appNotification @@ -41,7 +41,7 @@ export const formApproveNotificationTitle = ( txStatus: TransactionStatus, currency: Maybe, tokenAddress: Address, - spender: Address + spender: Address, ): string => { const currencyDisplayText = getCurrencyDisplayText(currency, tokenAddress) const address = shortenAddress(spender) @@ -51,13 +51,13 @@ export const formApproveNotificationTitle = ( address, }) : txStatus === TransactionStatus.Canceled - ? i18n.t('notification.transaction.approve.canceled', { - currencySymbol: currencyDisplayText, - }) - : i18n.t('notification.transaction.approve.fail', { - currencySymbol: currencyDisplayText, - address, - }) + ? i18n.t('notification.transaction.approve.canceled', { + currencySymbol: currencyDisplayText, + }) + : i18n.t('notification.transaction.approve.fail', { + currencySymbol: currencyDisplayText, + address, + }) } export const formSwapNotificationTitle = ( @@ -69,28 +69,22 @@ export const formSwapNotificationTitle = ( outputCurrencyId: string, inputCurrencyAmountRaw: string, outputCurrencyAmountRaw: string, - tradeType?: TradeType + tradeType?: TradeType, ): string => { - const inputCurrencySymbol = getCurrencyDisplayText( - inputCurrency, - currencyIdToAddress(inputCurrencyId) - ) - const outputCurrencySymbol = getCurrencyDisplayText( - outputCurrency, - currencyIdToAddress(outputCurrencyId) - ) + const inputCurrencySymbol = getCurrencyDisplayText(inputCurrency, currencyIdToAddress(inputCurrencyId)) + const outputCurrencySymbol = getCurrencyDisplayText(outputCurrency, currencyIdToAddress(outputCurrencyId)) const inputAmount = getFormattedCurrencyAmount( inputCurrency, inputCurrencyAmountRaw, formatter, - tradeType === TradeType.EXACT_OUTPUT + tradeType === TradeType.EXACT_OUTPUT, ) const outputAmount = getFormattedCurrencyAmount( outputCurrency, outputCurrencyAmountRaw, formatter, - tradeType === TradeType.EXACT_INPUT + tradeType === TradeType.EXACT_INPUT, ) const inputCurrencyAmountWithSymbol = `${inputAmount}${inputCurrencySymbol}` @@ -102,14 +96,14 @@ export const formSwapNotificationTitle = ( outputCurrencyAmountWithSymbol, }) : txStatus === TransactionStatus.Canceled - ? i18n.t('notification.transaction.swap.canceled', { - inputCurrencySymbol, - outputCurrencySymbol, - }) - : i18n.t('notification.transaction.swap.fail', { - inputCurrencyAmountWithSymbol, - outputCurrencyAmountWithSymbol, - }) + ? i18n.t('notification.transaction.swap.canceled', { + inputCurrencySymbol, + outputCurrencySymbol, + }) + : i18n.t('notification.transaction.swap.fail', { + inputCurrencyAmountWithSymbol, + outputCurrencyAmountWithSymbol, + }) } export const formWrapNotificationTitle = ( @@ -118,7 +112,7 @@ export const formWrapNotificationTitle = ( inputCurrency: Maybe, outputCurrency: Maybe, currencyAmountRaw: string, - unwrapped: boolean + unwrapped: boolean, ): string => { const inputCurrencySymbol = getSymbolDisplayText(inputCurrency?.symbol) const outputCurrencySymbol = getSymbolDisplayText(outputCurrency?.symbol) @@ -136,12 +130,12 @@ export const formWrapNotificationTitle = ( outputCurrencyAmountWithSymbol, }) : txStatus === TransactionStatus.Canceled - ? i18n.t('notification.transaction.unwrap.canceled', { - inputCurrencySymbol, - }) - : i18n.t('notification.transaction.unwrap.fail', { - inputCurrencyAmountWithSymbol, - }) + ? i18n.t('notification.transaction.unwrap.canceled', { + inputCurrencySymbol, + }) + : i18n.t('notification.transaction.unwrap.fail', { + inputCurrencyAmountWithSymbol, + }) } return txStatus === TransactionStatus.Success ? i18n.t('notification.transaction.wrap.success', { @@ -149,12 +143,12 @@ export const formWrapNotificationTitle = ( outputCurrencyAmountWithSymbol, }) : txStatus === TransactionStatus.Canceled - ? i18n.t('notification.transaction.wrap.canceled', { - inputCurrencySymbol, - }) - : i18n.t('notification.transaction.wrap.fail', { - inputCurrencyAmountWithSymbol, - }) + ? i18n.t('notification.transaction.wrap.canceled', { + inputCurrencySymbol, + }) + : i18n.t('notification.transaction.wrap.fail', { + inputCurrencyAmountWithSymbol, + }) } export const formTransferCurrencyNotificationTitle = ( @@ -164,7 +158,7 @@ export const formTransferCurrencyNotificationTitle = ( currency: Maybe, tokenAddress: string, currencyAmountRaw: string, - senderOrRecipient: string + senderOrRecipient: string, ): string => { const currencySymbol = getCurrencyDisplayText(currency, tokenAddress) const amount = getFormattedCurrencyAmount(currency, currencyAmountRaw, formatter) @@ -178,7 +172,7 @@ export const formTransferNFTNotificationTitle = ( nft: GQLNftAsset | undefined, tokenAddress: Address, tokenId: string, - senderOrRecipient: string + senderOrRecipient: string, ): string => { const nftName = nft?.name ?? `NFT ${shortenAddress(tokenAddress)} #${tokenId}` const shortenedAddressOrENS = getShortenedAddressOrEns(senderOrRecipient) @@ -188,7 +182,7 @@ export const formTransferNFTNotificationTitle = ( export const formUnknownTxTitle = ( txStatus: TransactionStatus, tokenAddress: Address | undefined, - ensName: string | null + ensName: string | null, ): string => { const address = tokenAddress && shortenAddress(tokenAddress) const target = ensName ?? address @@ -210,7 +204,7 @@ const formTransferTxTitle = ( txType: TransactionType, txStatus: TransactionStatus, tokenNameOrAddress: string, - walletNameOrAddress: string + walletNameOrAddress: string, ): string => { if (txType === TransactionType.Send) { return txStatus === TransactionStatus.Success @@ -219,13 +213,13 @@ const formTransferTxTitle = ( walletNameOrAddress, }) : txStatus === TransactionStatus.Canceled - ? i18n.t('notification.transaction.transfer.canceled', { - tokenNameOrAddress, - }) - : i18n.t('notification.transaction.transfer.fail', { - tokenNameOrAddress, - walletNameOrAddress, - }) + ? i18n.t('notification.transaction.transfer.canceled', { + tokenNameOrAddress, + }) + : i18n.t('notification.transaction.transfer.fail', { + tokenNameOrAddress, + walletNameOrAddress, + }) } return i18n.t('notification.transaction.transfer.received', { diff --git a/packages/wallet/src/features/onboarding/OnboardingContext.tsx b/packages/wallet/src/features/onboarding/OnboardingContext.tsx index ccbe519fad6..7972ba5d219 100644 --- a/packages/wallet/src/features/onboarding/OnboardingContext.tsx +++ b/packages/wallet/src/features/onboarding/OnboardingContext.tsx @@ -4,6 +4,7 @@ import { MobileEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { UnitagClaim } from 'uniswap/src/features/unitags/types' import { ImportType } from 'uniswap/src/types/onboarding' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' import { isExtension } from 'utilities/src/platform' import { setHasSkippedUnitagPrompt } from 'wallet/src/features/behaviorHistory/slice' @@ -13,32 +14,22 @@ import { createImportedAccounts } from 'wallet/src/features/onboarding/createImp import { createOnboardingAccount } from 'wallet/src/features/onboarding/createOnboardingAccount' import { useClaimUnitag } from 'wallet/src/features/unitags/hooks' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { - EditAccountAction, - editAccountActions, -} from 'wallet/src/features/wallet/accounts/editAccountSaga' -import { - Account, - BackupType, - SignerMnemonicAccount, -} from 'wallet/src/features/wallet/accounts/types' +import { EditAccountAction, editAccountActions } from 'wallet/src/features/wallet/accounts/editAccountSaga' +import { Account, BackupType, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' import { createAccountsActions } from 'wallet/src/features/wallet/create/createAccountsSaga' import { selectSortedSignerMnemonicAccounts } from 'wallet/src/features/wallet/selectors' import { useAppDispatch, useAppSelector } from 'wallet/src/state' -import { areAddressesEqual } from 'wallet/src/utils/addresses' export interface OnboardingContext { generateOnboardingAccount: (password?: string) => Promise - generateImportedAccounts: ( - mnemonicId: string, - backupType: BackupType.Cloud | BackupType.Manual - ) => Promise + generateImportedAccounts: (mnemonicId: string, backupType: BackupType.Cloud | BackupType.Manual) => Promise generateImportedAccountsByMnemonic: ( validMnemonic: string, password?: string, - backupType?: BackupType.Cloud | BackupType.Manual + backupType?: BackupType.Cloud | BackupType.Manual, ) => Promise addBackupMethod: (backupMethod: BackupType) => void + hasBackup: (address: string, backupType?: BackupType) => boolean | undefined enableNotifications: () => void selectImportedAccounts: (accountAddresses: string[]) => Promise finishOnboarding: (importType: ImportType, accounts?: SignerMnemonicAccount[]) => Promise @@ -46,6 +37,7 @@ export interface OnboardingContext { getOnboardingAccount: () => SignerMnemonicAccount | undefined getOnboardingAccountAddress: () => string | undefined getImportedAccounts: () => SignerMnemonicAccount[] | undefined + setRecoveredImportedAccounts: (accounts: SignerMnemonicAccount[]) => void getImportedAccountsAddresses: () => string[] | undefined getUnitagClaim: () => UnitagClaim | undefined addUnitagClaim: (unitag: UnitagClaim) => void @@ -62,14 +54,15 @@ const initialOnboardingContext: OnboardingContext = { generateImportedAccounts: async () => undefined, generateImportedAccountsByMnemonic: async () => undefined, addBackupMethod: () => undefined, + hasBackup: () => undefined, enableNotifications: () => undefined, selectImportedAccounts: async () => [], - finishOnboarding: async (_importType: ImportType, _accounts?: SignerMnemonicAccount[]) => - undefined, + finishOnboarding: async (_importType: ImportType, _accounts?: SignerMnemonicAccount[]) => undefined, getAllOnboardingAccounts: () => [], getOnboardingAccount: () => undefined, getOnboardingAccountAddress: () => undefined, getImportedAccounts: () => undefined, + setRecoveredImportedAccounts: (_accounts: SignerMnemonicAccount[]) => undefined, getImportedAccountsAddresses: () => undefined, getUnitagClaim: () => undefined, addUnitagClaim: () => undefined, @@ -103,13 +96,9 @@ export function OnboardingContextProvider({ children }: PropsWithChildren importedAccounts - ?.sort( - (a, b) => - (a as SignerMnemonicAccount).derivationIndex - - (b as SignerMnemonicAccount).derivationIndex - ) + ?.sort((a, b) => (a as SignerMnemonicAccount).derivationIndex - (b as SignerMnemonicAccount).derivationIndex) .map((account: SignerMnemonicAccount) => account.address), - [importedAccounts] + [importedAccounts], ) /** @@ -118,6 +107,12 @@ export function OnboardingContextProvider({ children }: PropsWithChildren => { + if (isExtension) { + // Clear any stale data from Keyring + // Only used on web during onboarding + // Mobile has different legacy conditions + await Keyring.removeAllMnemonicsAndPrivateKeys() + } resetOnboardingContextData() setOnboardingAccount(await createOnboardingAccount(sortedMnemonicAccounts, password)) } @@ -155,7 +150,7 @@ export function OnboardingContextProvider({ children }: PropsWithChildren => { setImportedAccounts(undefined) setOnboardingAccount(undefined) @@ -165,8 +160,14 @@ export function OnboardingContextProvider({ children }: PropsWithChildren => { + if (isExtension) { + // Clear any stale data from Keyring + // Only used on web during onboarding + // Mobile has different legacy conditions + await Keyring.removeAllMnemonicsAndPrivateKeys() + } const mnemonicId = await Keyring.importMnemonic(validMnemonic, password, true) await generateImportedAccounts(mnemonicId, backupType) } @@ -187,14 +188,12 @@ export function OnboardingContextProvider({ children }: PropsWithChildren => { + const selectImportedAccounts = async (accountAddresses: string[]): Promise => { if (!importedAccounts) { throw new Error('No imported accounts available for toggling selecting imported accounts') } const filteredImportedAccounts = importedAccounts.filter((importedAccount) => - accountAddresses.includes(importedAccount.address) + accountAddresses.includes(importedAccount.address), ) const namedImportedAccounts = filteredImportedAccounts.map((acc, index) => ({ ...acc, @@ -234,6 +233,18 @@ export function OnboardingContextProvider({ children }: PropsWithChildren { + return getAllOnboardingAccounts() + .find((account) => account.address === address) + ?.backups?.some((backup) => + backupType ? backup === backupType : backup === BackupType.Cloud || backup === BackupType.Manual, + ) + } + /** * Enables push notifications for all pending accounts */ @@ -268,10 +279,7 @@ export function OnboardingContextProvider({ children }: PropsWithChildren => { + const finishOnboarding = async (importType: ImportType, accounts?: SignerMnemonicAccount[]): Promise => { const isWatchFlow = importType === ImportType.Watch const onboardingAccounts = isWatchFlow ? [] : accounts ?? getAllOnboardingAccounts() const onboardingAddresses = onboardingAccounts.map((a) => a.address) @@ -281,15 +289,12 @@ export function OnboardingContextProvider({ children }: PropsWithChildren - acc.backups?.includes(BackupType.Cloud) + acc.backups?.includes(BackupType.Cloud), ), }) @@ -383,9 +388,7 @@ export function OnboardingContextProvider({ children }: PropsWithChildren { throwIfNotExtension() if (!mnemonic || (mnemonic.length !== 12 && mnemonic.length !== 24)) { - throw new Error( - 'Incorrect value of mnemonic parameted passed to addOnboardingAccountMnemonic function' - ) + throw new Error('Incorrect value of mnemonic parameted passed to addOnboardingAccountMnemonic function') } setOnboardingAccountMnemonic(mnemonic) } @@ -402,7 +405,9 @@ export function OnboardingContextProvider({ children }: PropsWithChildren + }} + > {children} ) diff --git a/packages/wallet/src/features/onboarding/createImportedAccounts.ts b/packages/wallet/src/features/onboarding/createImportedAccounts.ts index c702a58fcc1..2209adc67ea 100644 --- a/packages/wallet/src/features/onboarding/createImportedAccounts.ts +++ b/packages/wallet/src/features/onboarding/createImportedAccounts.ts @@ -1,21 +1,17 @@ import dayjs from 'dayjs' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { - AccountType, - BackupType, - SignerMnemonicAccount, -} from 'wallet/src/features/wallet/accounts/types' +import { AccountType, BackupType, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' export const NUMBER_OF_WALLETS_TO_IMPORT = 10 export const createImportedAccounts = async ( mnemonicId: string, - backupType?: BackupType.Cloud | BackupType.Manual + backupType?: BackupType.Cloud | BackupType.Manual, ): Promise => { const addresses = await Promise.all( Array(NUMBER_OF_WALLETS_TO_IMPORT) .fill(null) - .map(async (_, index) => await Keyring.generateAndStorePrivateKey(mnemonicId, index)) + .map(async (_, index) => await Keyring.generateAndStorePrivateKey(mnemonicId, index)), ) const importedAccounts: SignerMnemonicAccount[] = addresses.map((address, index) => ({ type: AccountType.SignerMnemonic, diff --git a/packages/wallet/src/features/onboarding/createOnboardingAccount.ts b/packages/wallet/src/features/onboarding/createOnboardingAccount.ts index 82eb6ea4ee3..622b5acf453 100644 --- a/packages/wallet/src/features/onboarding/createOnboardingAccount.ts +++ b/packages/wallet/src/features/onboarding/createOnboardingAccount.ts @@ -1,21 +1,17 @@ import dayjs from 'dayjs' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { - AccountType, - BackupType, - SignerMnemonicAccount, -} from 'wallet/src/features/wallet/accounts/types' +import { AccountType, BackupType, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' /** * Takes a list of existing mnemonic accounts to use as reference for pulling the next derivation index */ export const createOnboardingAccount = async ( sortedMnemonicAccounts: SignerMnemonicAccount[], - password?: string + password?: string, ): Promise => { const { nextDerivationIndex, mnemonicId, existingBackups } = await getNewAccountParams( sortedMnemonicAccounts, - password + password, ) const address = await Keyring.generateAndStorePrivateKey(mnemonicId, nextDerivationIndex) return { @@ -31,7 +27,7 @@ export const createOnboardingAccount = async ( export async function getNewAccountParams( sortedAccounts: SignerMnemonicAccount[], - password?: string + password?: string, ): Promise<{ nextDerivationIndex: number mnemonicId: string diff --git a/packages/wallet/src/features/onboarding/createViewOnlyAccount.ts b/packages/wallet/src/features/onboarding/createViewOnlyAccount.ts index bbb00d732ac..44742339589 100644 --- a/packages/wallet/src/features/onboarding/createViewOnlyAccount.ts +++ b/packages/wallet/src/features/onboarding/createViewOnlyAccount.ts @@ -1,6 +1,6 @@ import dayjs from 'dayjs' +import { getValidAddress } from 'uniswap/src/utils/addresses' import { AccountType, ReadOnlyAccount } from 'wallet/src/features/wallet/accounts/types' -import { getValidAddress } from 'wallet/src/utils/addresses' export const createViewOnlyAccount = (address: string): ReadOnlyAccount => { const formattedAddress = getValidAddress(address, true) diff --git a/packages/wallet/src/features/onboarding/hooks/useSelectAccounts.tsx b/packages/wallet/src/features/onboarding/hooks/useSelectAccounts.tsx index 7c2f4ce09ed..e86046ee2fe 100644 --- a/packages/wallet/src/features/onboarding/hooks/useSelectAccounts.tsx +++ b/packages/wallet/src/features/onboarding/hooks/useSelectAccounts.tsx @@ -10,10 +10,7 @@ export function useSelectAccounts(accounts: ImportableAccount[] = []): { selectedAddresses: string[] toggleAddressSelection: (address: string) => void } { - const initialSelectedAddresses = useMemo( - () => accounts.map((account) => account.ownerAddress), - [accounts] - ) + const initialSelectedAddresses = useMemo(() => accounts.map((account) => account.ownerAddress), [accounts]) const [selectedAddresses, setSelectedAddresses] = useState(initialSelectedAddresses) const toggleAddressSelection = (address: string): void => { @@ -22,9 +19,7 @@ export function useSelectAccounts(accounts: ImportableAccount[] = []): { return } if (selectedAddresses.includes(address)) { - setSelectedAddresses( - selectedAddresses.filter((selectedAddress) => selectedAddress !== address) - ) + setSelectedAddresses(selectedAddresses.filter((selectedAddress) => selectedAddress !== address)) } else { setSelectedAddresses([...selectedAddresses, address]) } diff --git a/packages/wallet/src/features/portfolio/AnimatedNumber.tsx b/packages/wallet/src/features/portfolio/AnimatedNumber.tsx index 42554ac35e9..7d5b8d6c2d0 100644 --- a/packages/wallet/src/features/portfolio/AnimatedNumber.tsx +++ b/packages/wallet/src/features/portfolio/AnimatedNumber.tsx @@ -26,7 +26,7 @@ export const ADDITIONAL_WIDTH_FOR_ANIMATIONS = 8 // TODO: remove need to manually define width of each character const NUMBER_WIDTH_ARRAY_SCALED = NUMBER_WIDTH_ARRAY.map( - (width) => width * (fonts.heading2.fontSize / fonts.heading1.fontSize) + (width) => width * (fonts.heading2.fontSize / fonts.heading1.fontSize), ) const isRTL = I18nManager.isRTL @@ -54,18 +54,16 @@ const RollNumber = ({ }): JSX.Element => { const colors = useSporeColors() const fontColor = useSharedValue( - nextColor || - (shouldFadeDecimals && index > chars.length - 4 ? colors.neutral3.val : colors.neutral1.val) + nextColor || (shouldFadeDecimals && index > chars.length - 4 ? colors.neutral3.val : colors.neutral1.val), ) const yOffset = useSharedValue(digit && Number(digit) >= 0 ? DIGIT_HEIGHT * -digit : 0) useEffect(() => { - const finishColor = - shouldFadeDecimals && index > chars.length - 4 ? colors.neutral3.val : colors.neutral1.val + const finishColor = shouldFadeDecimals && index > chars.length - 4 ? colors.neutral3.val : colors.neutral1.val if (nextColor && index > commonPrefixLength - 1) { fontColor.value = withSequence( withTiming(nextColor, { duration: 250 }), - withDelay(50, withTiming(finishColor, { duration: 310 })) + withDelay(50, withTiming(finishColor, { duration: 310 })), ) } else { fontColor.value = finishColor @@ -93,7 +91,8 @@ const RollNumber = ({ + style={[animatedFontStyle, AnimatedFontStyles.fontStyle, { height: DIGIT_HEIGHT }]} + > {char} ) @@ -117,11 +116,11 @@ const RollNumber = ({ style={[ animatedWrapperStyle, { - width: - (NUMBER_WIDTH_ARRAY_SCALED[Number(digit)] || 0) + ADDITIONAL_WIDTH_FOR_ANIMATIONS, + width: (NUMBER_WIDTH_ARRAY_SCALED[Number(digit)] || 0) + ADDITIONAL_WIDTH_FOR_ANIMATIONS, ...margin, }, - ]}> + ]} + > {numbers} ) @@ -129,7 +128,8 @@ const RollNumber = ({ return ( + style={[animatedFontStyle, AnimatedFontStyles.fontStyle, { height: DIGIT_HEIGHT }]} + > {digit} ) @@ -154,7 +154,8 @@ const Char = ({ entering={nextColor ? FadeIn : undefined} exiting={FadeOut} layout={Layout} - style={[{ height: DIGIT_HEIGHT }, AnimatedCharStyles.wrapperStyle]}> + style={[{ height: DIGIT_HEIGHT }, AnimatedCharStyles.wrapperStyle]} + > { { color: colors.neutral1.val, }, - ]}> + ]} + > {amountOfCurrency[0]} + }} + > {currency.decimalSeparator} {amountOfCurrency[1]} @@ -262,11 +265,7 @@ const ReanimatedNumber = ({ const scaleWraper = useAnimatedStyle(() => { return { - transform: [ - { translateX: -SCREEN_WIDTH / 2 }, - { scale: scale.value }, - { translateX: SCREEN_WIDTH / 2 }, - ], + transform: [{ translateX: -SCREEN_WIDTH / 2 }, { scale: scale.value }, { translateX: SCREEN_WIDTH / 2 }], } }) @@ -309,11 +308,7 @@ const ReanimatedNumber = ({ {placeholderChars.map((_, index) => ( - + {chars?.map((_, index) => ( + onLayout={fitBalanceOnLayout} + > {value} diff --git a/packages/wallet/src/features/portfolio/HiddenTokensRow.tsx b/packages/wallet/src/features/portfolio/HiddenTokensRow.tsx index ff18d3e588a..48f726d2179 100644 --- a/packages/wallet/src/features/portfolio/HiddenTokensRow.tsx +++ b/packages/wallet/src/features/portfolio/HiddenTokensRow.tsx @@ -17,17 +17,8 @@ export function HiddenTokensRow({ const { t } = useTranslation() return ( - - + + {t('tokens.hidden.label', { numHidden })} @@ -42,13 +33,15 @@ export function HiddenTokensRow({ justifyContent="center" pl="$spacing12" pr="$spacing8" - py="$spacing8"> + py="$spacing8" + > + variant="buttonLabel3" + > {isExpanded ? t('common.button.hide') : t('common.button.show')} @@ -83,7 +80,8 @@ const WebBalanceWithFadedDecimals = ({ value }: { value: string }): JSX.Element style={{ fontWeight: WEB_BALANCE_FONT_WEIGHT, }} - variant="heading2"> + variant="heading2" + > {amountOfCurrency[0]} {amountOfCurrency.length > 1 && ( diff --git a/packages/wallet/src/features/portfolio/PortfolioEmptyState.tsx b/packages/wallet/src/features/portfolio/PortfolioEmptyState.tsx index 17f1da3ef6d..ed06d8147c4 100644 --- a/packages/wallet/src/features/portfolio/PortfolioEmptyState.tsx +++ b/packages/wallet/src/features/portfolio/PortfolioEmptyState.tsx @@ -1,36 +1,19 @@ -import React, { useMemo } from 'react' +import React, { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { - ImageBackground, - ImageSourcePropType, - StyleProp, - StyleSheet, - ViewStyle, - VirtualizedList, -} from 'react-native' -import { Flex, Text, TouchableArea, useIsDarkMode } from 'ui/src' +import { ImageBackground, ImageSourcePropType, StyleProp, StyleSheet, ViewStyle, VirtualizedList } from 'react-native' +import { Flex, useIsDarkMode } from 'ui/src' import { CRYPTO_PURCHASE_BACKGROUND_DARK, CRYPTO_PURCHASE_BACKGROUND_LIGHT } from 'ui/src/assets' import { ArrowDownCircle, Buy as BuyIcon, PaperStack } from 'ui/src/components/icons' import { borderRadii } from 'ui/src/theme' +import { ActionCard, ActionCardItem } from 'uniswap/src/components/misc/ActionCard' import { FeatureFlags } from 'uniswap/src/features/gating/flags' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' -import Trace from 'uniswap/src/features/telemetry/Trace' -import { ElementName, ElementNameType } from 'uniswap/src/features/telemetry/constants' +import { ElementName } from 'uniswap/src/features/telemetry/constants' import { useCexTransferProviders } from 'wallet/src/features/fiatOnRamp/api' import { ImageUri } from 'wallet/src/features/images/ImageUri' import { AccountType } from 'wallet/src/features/wallet/accounts/types' import { useActiveAccount } from 'wallet/src/features/wallet/hooks' -interface ActionCardItem { - title: string - blurb: string - icon: JSX.Element - elementName: ElementNameType - backgroundImage?: ImageSourcePropType - badgeText?: string - onPress?: () => void -} - enum ActionOption { Buy = 'Buy', Import = 'Import', @@ -47,11 +30,7 @@ type WalletEmptyStateProps = { onPressBuy?: () => void } -export function PortfolioEmptyState({ - onPressReceive, - onPressImport, - onPressBuy, -}: WalletEmptyStateProps): JSX.Element { +export function PortfolioEmptyState({ onPressReceive, onPressImport, onPressBuy }: WalletEmptyStateProps): JSX.Element { const { t } = useTranslation() const isDarkMode = useIsDarkMode() @@ -60,6 +39,17 @@ export function PortfolioEmptyState({ const cexTransferEnabled = useFeatureFlag(FeatureFlags.CexTransfers) const cexTransferProviders = useCexTransferProviders(cexTransferEnabled) + const BackgroundImageWrapperCallback = useCallback( + ({ children }: { children: React.ReactNode }) => { + return ( + + {children} + + ) + }, + [isDarkMode], + ) + const options: { [key in ActionOption]: ActionCardItem } = useMemo( () => ({ [ActionOption.Buy]: { @@ -67,10 +57,8 @@ export function PortfolioEmptyState({ blurb: t('home.tokens.empty.action.buy.description'), elementName: ElementName.EmptyStateBuy, icon: , - backgroundImage: isDarkMode - ? CRYPTO_PURCHASE_BACKGROUND_DARK - : CRYPTO_PURCHASE_BACKGROUND_LIGHT, onPress: onPressBuy, + BackgroundImageWrapperCallback, }, [ActionOption.Receive]: { title: t('home.tokens.empty.action.receive.title'), @@ -78,9 +66,7 @@ export function PortfolioEmptyState({ elementName: ElementName.EmptyStateReceive, icon: cexTransferProviders.length > 0 ? ( - provider.logos.lightLogo)} - /> + provider.logos.lightLogo)} /> ) : ( ), @@ -94,14 +80,12 @@ export function PortfolioEmptyState({ onPress: onPressImport, }, }), - [t, isDarkMode, onPressBuy, cexTransferProviders, onPressReceive, onPressImport] + [t, onPressBuy, BackgroundImageWrapperCallback, cexTransferProviders, onPressReceive, onPressImport], ) // Order options based on view only status, and wether we have a valid buy handler const sortedOptions = - isViewOnly && onPressImport - ? [options.Import] - : [...(onPressBuy ? [options.Buy] : []), options.Receive] + isViewOnly && onPressImport ? [options.Import] : [...(onPressBuy ? [options.Buy] : []), options.Receive] return ( @@ -112,51 +96,17 @@ export function PortfolioEmptyState({ ) } -const ActionCard = ({ - title, - blurb, - onPress, - icon, - elementName, - backgroundImage, -}: ActionCardItem): JSX.Element => ( - - - - - {icon} - - - {title} - - - {blurb} - - - - - - -) - -const BackgroundWrapper = ({ +const BackgroundImage = ({ children, - backgroundImage, + image, }: { children: React.ReactNode - backgroundImage?: ImageSourcePropType + image: ImageSourcePropType }): JSX.Element => { - return backgroundImage !== undefined ? ( - + return ( + {children} - ) : ( - {children} ) } @@ -170,7 +120,8 @@ function ReceiveCryptoIcon(): JSX.Element { style={{ ...styles.iconContainer, borderRadius: borderRadii.roundedFull, - }}> + }} + > + style={styles.iconContainer} + > + onPress={onPress} + > void } -export const TokenBalanceListContext = createContext( - undefined -) +export const TokenBalanceListContext = createContext(undefined) export function TokenBalanceListContextProvider({ owner, @@ -113,21 +108,17 @@ export function TokenBalanceListContextProvider({ onPressToken, refetch, rows, - ] + ], ) - return ( - {children} - ) + return {children} } export const useTokenBalanceListContext = (): TokenBalanceListContextState => { const context = useContext(TokenBalanceListContext) if (context === undefined) { - throw new Error( - '`useTokenBalanceListContext` must be used inside of `TokenBalanceListContextProvider`' - ) + throw new Error('`useTokenBalanceListContext` must be used inside of `TokenBalanceListContextProvider`') } return context diff --git a/packages/wallet/src/features/portfolio/api.ts b/packages/wallet/src/features/portfolio/api.ts index 86d98fa40b7..fe51ae7f4a9 100644 --- a/packages/wallet/src/features/portfolio/api.ts +++ b/packages/wallet/src/features/portfolio/api.ts @@ -2,12 +2,12 @@ import { Currency, CurrencyAmount, NativeCurrency as NativeCurrencyClass } from import { useMemo } from 'react' import ERC20_ABI from 'uniswap/src/abis/erc20.json' import { useRestQuery } from 'uniswap/src/data/rest' +import { getPollingIntervalByBlocktime } from 'uniswap/src/features/chains/utils' import { WalletChainId } from 'uniswap/src/types/chains' -import { getPollingIntervalByBlocktime } from 'wallet/src/features/chains/utils' +import { currencyAddress as getCurrencyAddress } from 'uniswap/src/utils/currencyId' import { createEthersProvider } from 'wallet/src/features/providers/createEthersProvider' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { walletContextValue } from 'wallet/src/features/wallet/context' -import { currencyAddress as getCurrencyAddress } from 'wallet/src/utils/currencyId' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' // stub endpoint to conform to REST endpoint styles @@ -40,19 +40,14 @@ export const getOnChainBalancesFetch = async (params: BalanceLookupParams): Prom } // erc20 lookup - const erc20Contract = walletContextValue.contracts.getOrCreateContract( - chainId, - currencyAddress, - provider, - ERC20_ABI - ) + const erc20Contract = walletContextValue.contracts.getOrCreateContract(chainId, currencyAddress, provider, ERC20_ABI) const balance = await erc20Contract.callStatic.balanceOf?.(accountAddress) return new Response(JSON.stringify({ balance: balance.toString() })) } export function useOnChainCurrencyBalance( currency?: Currency | null, - accountAddress?: Address + accountAddress?: Address, ): { balance: CurrencyAmount | undefined; isLoading: boolean; error: unknown } { const { data, error } = useRestQuery<{ balance?: string }, BalanceLookupParams>( STUB_ONCHAIN_BALANCES_ENDPOINT, @@ -67,24 +62,22 @@ export function useOnChainCurrencyBalance( pollInterval: getPollingIntervalByBlocktime(currency?.chainId), ttlMs: getPollingIntervalByBlocktime(currency?.chainId), skip: !currency, - } + }, ) return useMemo( () => ({ - balance: - getCurrencyAmount({ value: data?.balance, valueType: ValueType.Raw, currency }) ?? - undefined, + balance: getCurrencyAmount({ value: data?.balance, valueType: ValueType.Raw, currency }) ?? undefined, isLoading: !data?.balance, error, }), - [data, currency, error] + [data, currency, error], ) } export function useOnChainNativeCurrencyBalance( chain: WalletChainId, - accountAddress?: Address + accountAddress?: Address, ): { balance: CurrencyAmount | undefined; isLoading: boolean } { const currency = NativeCurrency.onChain(chain) const { balance, isLoading } = useOnChainCurrencyBalance(currency, accountAddress) diff --git a/packages/wallet/src/features/portfolio/useTokenContextMenu.tsx b/packages/wallet/src/features/portfolio/useTokenContextMenu.tsx index 9ce9cf30dec..46c7b1bc031 100644 --- a/packages/wallet/src/features/portfolio/useTokenContextMenu.tsx +++ b/packages/wallet/src/features/portfolio/useTokenContextMenu.tsx @@ -1,15 +1,13 @@ import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { NativeSyntheticEvent } from 'react-native' -import type { - ContextMenuAction, - ContextMenuOnPressNativeEvent, -} from 'react-native-context-menu-view' +import type { ContextMenuAction, ContextMenuOnPressNativeEvent } from 'react-native-context-menu-view' import { GeneratedIcon, isWeb } from 'ui/src' import { CoinConvert, Eye, EyeOff, ReceiveAlt, SendAction } from 'ui/src/components/icons' import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { UniverseChainId } from 'uniswap/src/types/chains' import { CurrencyId } from 'uniswap/src/types/currency' +import { areCurrencyIdsEqual, currencyIdToAddress, currencyIdToChain } from 'uniswap/src/utils/currencyId' import { ONE_SECOND_MS } from 'utilities/src/time/time' import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { usePortfolioCacheUpdater } from 'wallet/src/features/dataApi/balances' @@ -19,11 +17,6 @@ import { AppNotificationType } from 'wallet/src/features/notifications/types' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' import { useAppDispatch } from 'wallet/src/state' -import { - areCurrencyIdsEqual, - currencyIdToAddress, - currencyIdToChain, -} from 'wallet/src/utils/currencyId' interface TokenMenuParams { currencyId: CurrencyId @@ -33,11 +26,7 @@ interface TokenMenuParams { type MenuAction = ContextMenuAction & { onPress: () => void; Icon?: GeneratedIcon } -export function useTokenContextMenu({ - currencyId, - tokenSymbolForNotification, - portfolioBalance, -}: TokenMenuParams): { +export function useTokenContextMenu({ currencyId, tokenSymbolForNotification, portfolioBalance }: TokenMenuParams): { menuActions: Array onContextMenuPress: (e: NativeSyntheticEvent) => void } { @@ -45,8 +34,7 @@ export function useTokenContextMenu({ const dispatch = useAppDispatch() const activeAccountAddress = useActiveAccountAddressWithThrow() - const { navigateToSwapFlow, navigateToReceive, navigateToSend, handleShareToken } = - useWalletNavigation() + const { navigateToSwapFlow, navigateToReceive, navigateToSend, handleShareToken } = useWalletNavigation() const activeAccountHoldsToken = portfolioBalance && areCurrencyIdsEqual(currencyId, portfolioBalance?.currencyInfo.currencyId) @@ -65,7 +53,7 @@ export function useTokenContextMenu({ // Do not show warning modal speed-bump if user is trying to swap tokens they own navigateToSwapFlow({ currencyField, currencyAddress, currencyChainId }) }, - [currencyAddress, currencyChainId, navigateToSwapFlow] + [currencyAddress, currencyChainId, navigateToSwapFlow], ) const onPressShare = useCallback(async () => { @@ -92,7 +80,7 @@ export function useTokenContextMenu({ visible: !isHidden, hideDelay: 2 * ONE_SECOND_MS, assetName: tokenSymbolForNotification, - }) + }), ) } }, [currencyId, dispatch, isHidden, tokenSymbolForNotification, updateCache, portfolioBalance]) @@ -161,14 +149,14 @@ export function useTokenContextMenu({ isHidden, onPressHiddenStatus, onPressSwap, - ] + ], ) const onContextMenuPress = useCallback( (e: NativeSyntheticEvent): void => { menuActions[e.nativeEvent.index]?.onPress?.() }, - [menuActions] + [menuActions], ) return { menuActions, onContextMenuPress } diff --git a/packages/wallet/src/features/providers/ProviderManager.ts b/packages/wallet/src/features/providers/ProviderManager.ts index a1e82d599f2..2144a9bceb0 100644 --- a/packages/wallet/src/features/providers/ProviderManager.ts +++ b/packages/wallet/src/features/providers/ProviderManager.ts @@ -74,11 +74,7 @@ export class ProviderManager { removeProviders(chainId: WalletChainId): void { const providersInfo = this._providers[chainId] if (!providersInfo) { - logger.warn( - 'ProviderManager', - 'removeProviders', - `Attempting to remove non-existent provider: ${chainId}` - ) + logger.warn('ProviderManager', 'removeProviders', `Attempting to remove non-existent provider: ${chainId}`) return } diff --git a/packages/wallet/src/features/providers/createEthersProvider.e2e.js b/packages/wallet/src/features/providers/createEthersProvider.e2e.js index c9cc2e15fe7..599175d5981 100644 --- a/packages/wallet/src/features/providers/createEthersProvider.e2e.js +++ b/packages/wallet/src/features/providers/createEthersProvider.e2e.js @@ -3,7 +3,6 @@ * Replaces `createEthersProvider.ts` when RN_SRC_EXT=e2e.js at runtime */ import { JsonRpcProvider } from '@ethersproject/providers' -import { WalletChainId } from 'uniswap/src/types/chains' export function createEthersProvider(chainId) { if (chainId === ChainId.Mainnet) { @@ -24,9 +23,7 @@ class TestProvider extends JsonRpcProvider { return block } catch (e) { if (e.reason === 'missing response') { - throw new Error( - 'Hardhat node is not running. Start it with `yarn hardhat`. [original error: ' + e - ) + throw new Error('Hardhat node is not running. Start it with `yarn hardhat`. [original error: ' + e) } throw e } diff --git a/packages/wallet/src/features/providers/createEthersProvider.ts b/packages/wallet/src/features/providers/createEthersProvider.ts index cc0f303d2f8..815691b0479 100644 --- a/packages/wallet/src/features/providers/createEthersProvider.ts +++ b/packages/wallet/src/features/providers/createEthersProvider.ts @@ -8,7 +8,7 @@ import { getInfuraChainName } from 'wallet/src/features/providers/utils' // Should use ProviderManager for provider access unless being accessed outside of ProviderManagerContext (e.g., Apollo initialization) export function createEthersProvider( chainId: WalletChainId, - rpcType: RPCType = RPCType.Public + rpcType: RPCType = RPCType.Public, ): ethersProviders.JsonRpcProvider | null { try { if (rpcType === RPCType.Private) { diff --git a/packages/wallet/src/features/scantastic/types.ts b/packages/wallet/src/features/scantastic/types.ts index 38db7081a41..e41e4c7e163 100644 --- a/packages/wallet/src/features/scantastic/types.ts +++ b/packages/wallet/src/features/scantastic/types.ts @@ -18,7 +18,7 @@ export const ScantasticParamsSchema = z.object({ invalid_type_error: 'Invalid public exponent', }), }, - { required_error: 'Public key is required' } + { required_error: 'Public key is required' }, ), vendor: z.string().nullish(), model: z.string().nullish(), diff --git a/packages/wallet/src/features/search/SearchResult.ts b/packages/wallet/src/features/search/SearchResult.ts index 32f8bfd915e..9774b6ff216 100644 --- a/packages/wallet/src/features/search/SearchResult.ts +++ b/packages/wallet/src/features/search/SearchResult.ts @@ -1,11 +1,7 @@ import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { WalletChainId } from 'uniswap/src/types/chains' -export type SearchResult = - | TokenSearchResult - | WalletSearchResult - | EtherscanSearchResult - | NFTCollectionSearchResult +export type SearchResult = TokenSearchResult | WalletSearchResult | EtherscanSearchResult | NFTCollectionSearchResult // Retain original ordering as these are saved to storage and loaded back out export enum SearchResultType { @@ -31,10 +27,7 @@ export interface SearchResultBase { searchId?: string } -export type WalletSearchResult = - | ENSAddressSearchResult - | UnitagSearchResult - | WalletByAddressSearchResult +export type WalletSearchResult = ENSAddressSearchResult | UnitagSearchResult | WalletByAddressSearchResult export interface WalletByAddressSearchResult extends SearchResultBase { type: SearchResultType.WalletByAddress diff --git a/packages/wallet/src/features/search/SearchTextInput.tsx b/packages/wallet/src/features/search/SearchTextInput.tsx index 3f3f03805fc..4f0d2fa917e 100644 --- a/packages/wallet/src/features/search/SearchTextInput.tsx +++ b/packages/wallet/src/features/search/SearchTextInput.tsx @@ -82,9 +82,7 @@ export const SearchTextInput = forwardRef const showCloseButton = !!onClose const [isFocus, setIsFocus] = useState(false) const [cancelButtonWidth, setCancelButtonWidth] = useState(showCancelButton ? 40 : 0) - const [showClearButton, setShowClearButton] = useState( - value && value.length > 0 && !disableClearable - ) + const [showClearButton, setShowClearButton] = useState(value && value.length > 0 && !disableClearable) const onPressCancel = (): void => { inputRef.current?.clear() @@ -120,7 +118,7 @@ export const SearchTextInput = forwardRef onChangeText?.(text) setShowClearButton(text.length > 0 && !disableClearable) }, - [disableClearable, onChangeText] + [disableClearable, onChangeText], ) return ( @@ -148,7 +146,8 @@ export const SearchTextInput = forwardRef '$theme-dark': { shadowColor: '$sporeBlack', }, - })}> + })} + > {!hideIcon && ( @@ -216,7 +215,8 @@ export const SearchTextInput = forwardRef + scale={isFocus && showClearButton ? 0 : 1} + > {endAdornment} ) : null} @@ -253,7 +253,8 @@ export const SearchTextInput = forwardRef right={0} scale={isFocus ? 1 : 0} x={isFocus ? 0 : dimensions.fullWidth} - onLayout={onCancelButtonLayout}> + onLayout={onCancelButtonLayout} + > {t('common.button.cancel')} @@ -261,5 +262,5 @@ export const SearchTextInput = forwardRef )} ) - } + }, ) diff --git a/packages/wallet/src/features/search/searchHistorySlice.ts b/packages/wallet/src/features/search/searchHistorySlice.ts index d4ff5de746c..5fab06498a0 100644 --- a/packages/wallet/src/features/search/searchHistorySlice.ts +++ b/packages/wallet/src/features/search/searchHistorySlice.ts @@ -40,10 +40,7 @@ const slice = createSlice({ state.results.unshift({ ...searchResult, searchId }) // Filter out to only uniques & keep size under SEARCH_HISTORY_LENGTH state.results = state.results - .filter( - (result, index, self) => - index === self.findIndex((value) => value.searchId === result.searchId) - ) + .filter((result, index, self) => index === self.findIndex((value) => value.searchId === result.searchId)) .slice(0, SEARCH_HISTORY_LENGTH) }, clearSearchHistory: (state) => { diff --git a/packages/wallet/src/features/telemetry/selectors.ts b/packages/wallet/src/features/telemetry/selectors.ts new file mode 100644 index 00000000000..99e689d1d96 --- /dev/null +++ b/packages/wallet/src/features/telemetry/selectors.ts @@ -0,0 +1,12 @@ +import { SharedState } from 'wallet/src/state/reducer' + +export const selectLastBalancesReport = (state: SharedState): number => state.telemetry.lastBalancesReport + +export const selectLastBalancesReportValue = (state: SharedState): number | undefined => + state.telemetry.lastBalancesReportValue + +export const selectLastHeartbeat = (state: SharedState): number => state.telemetry.lastHeartbeat + +export const selectWalletIsFunded = (state: SharedState): boolean => state.telemetry.walletIsFunded + +export const selectAllowAnalytics = (state: SharedState): boolean => state.telemetry.allowAnalytics diff --git a/apps/mobile/src/features/telemetry/slice.ts b/packages/wallet/src/features/telemetry/slice.ts similarity index 93% rename from apps/mobile/src/features/telemetry/slice.ts rename to packages/wallet/src/features/telemetry/slice.ts index cf6ad0f686e..2c83df3549a 100644 --- a/apps/mobile/src/features/telemetry/slice.ts +++ b/packages/wallet/src/features/telemetry/slice.ts @@ -35,10 +35,7 @@ export const slice = createSlice({ sendAnalyticsEvent(SharedEventName.HEARTBEAT) state.lastHeartbeat = Date.now() }, - recordBalancesReport: ( - state, - { payload: { totalBalance } }: PayloadAction<{ totalBalance: number }> - ) => { + recordBalancesReport: (state, { payload: { totalBalance } }: PayloadAction<{ totalBalance: number }>) => { state.lastBalancesReport = Date.now() state.lastBalancesReportValue = totalBalance }, @@ -76,7 +73,7 @@ export function shouldReportBalances( lastBalancesReport: number | undefined, lastBalancesReportValue: number | undefined, signerAccountAddresses: string[], - signerAccountValues: number[] + signerAccountValues: number[], ): boolean { const currentBalance = signerAccountValues.reduce((a, b) => a + b, 0) @@ -87,6 +84,5 @@ export function shouldReportBalances( return validAccountInfo && (didWalletGetFunded || balanceReportDue) } -export const { recordHeartbeat, recordBalancesReport, recordWalletFunded, setAllowAnalytics } = - slice.actions +export const { recordHeartbeat, recordBalancesReport, recordWalletFunded, setAllowAnalytics } = slice.actions export const { reducer: telemetryReducer } = slice diff --git a/packages/wallet/src/features/timing/selectors.ts b/packages/wallet/src/features/timing/selectors.ts index 10d17614520..02256288b53 100644 --- a/packages/wallet/src/features/timing/selectors.ts +++ b/packages/wallet/src/features/timing/selectors.ts @@ -1,4 +1,3 @@ import { SharedState } from 'wallet/src/state/reducer' -export const selectSwapStartTimestamp = (state: SharedState): number | undefined => - state.timing.swap.startTimestamp +export const selectSwapStartTimestamp = (state: SharedState): number | undefined => state.timing.swap.startTimestamp diff --git a/packages/wallet/src/features/timing/slice.ts b/packages/wallet/src/features/timing/slice.ts index 12a2ba210d9..c1947527e84 100644 --- a/packages/wallet/src/features/timing/slice.ts +++ b/packages/wallet/src/features/timing/slice.ts @@ -20,10 +20,7 @@ export const slice = createSlice({ name: 'timing', initialState: initialTimingState, reducers: { - updateSwapStartTimestamp: ( - state, - { payload: { timestamp } }: PayloadAction<{ timestamp?: number }> - ) => { + updateSwapStartTimestamp: (state, { payload: { timestamp } }: PayloadAction<{ timestamp?: number }>) => { state.swap.startTimestamp = timestamp }, }, diff --git a/packages/wallet/src/features/tokens/NativeCurrency.ts b/packages/wallet/src/features/tokens/NativeCurrency.ts index 19ff510abde..4c867b4ca51 100644 --- a/packages/wallet/src/features/tokens/NativeCurrency.ts +++ b/packages/wallet/src/features/tokens/NativeCurrency.ts @@ -1,10 +1,10 @@ // adapted from https://github.com/Uniswap/interface/src/constants/tokens.ts import { Currency, NativeCurrency as NativeCurrencyClass, Token } from '@uniswap/sdk-core' +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { WalletChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' import { wrappedNativeCurrency } from 'wallet/src/constants/tokens' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' export class NativeCurrency implements NativeCurrencyClass { constructor(chainId: number) { @@ -46,9 +46,6 @@ export class NativeCurrency implements NativeCurrencyClass { private static _cachedNativeCurrency: { [chainId: number]: NativeCurrency } = {} public static onChain(chainId: number): NativeCurrency { - return ( - this._cachedNativeCurrency[chainId] ?? - (this._cachedNativeCurrency[chainId] = new NativeCurrency(chainId)) - ) + return this._cachedNativeCurrency[chainId] ?? (this._cachedNativeCurrency[chainId] = new NativeCurrency(chainId)) } } diff --git a/packages/wallet/src/features/tokens/TokenWarningModal.tsx b/packages/wallet/src/features/tokens/TokenWarningModal.tsx index 14dd0f34adf..944bc21f2c7 100644 --- a/packages/wallet/src/features/tokens/TokenWarningModal.tsx +++ b/packages/wallet/src/features/tokens/TokenWarningModal.tsx @@ -3,13 +3,13 @@ import { Button, Flex, Text, isWeb, useSporeColors } from 'ui/src' import { AppTFunction } from 'ui/src/i18n/types' import { ThemeNames, imageSizes, opacify } from 'ui/src/theme' import { TokenLogo } from 'uniswap/src/components/CurrencyLogo/TokenLogo' +import WarningIcon from 'uniswap/src/components/icons/WarningIcon' import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal' import { uniswapUrls } from 'uniswap/src/constants/urls' import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' -import WarningIcon from 'wallet/src/components/icons/WarningIcon' +import { useTokenSafetyLevelColors } from 'uniswap/src/features/tokens/safetyHooks' import { LearnMoreLink } from 'wallet/src/components/text/LearnMoreLink' -import { useTokenSafetyLevelColors } from 'wallet/src/features/tokens/safetyHooks' import { getTokenSafetyHeaderText } from 'wallet/src/features/tokens/utils' function getTokenSafetyBodyText(safetyLevel: Maybe, t: AppTFunction): string { @@ -55,8 +55,7 @@ export default function TokenWarningModal({ const closeButtonText = hideAcceptButton ? t('common.button.close') : t('common.button.back') - const showWarningIcon = - safetyLevel === SafetyLevel.StrongWarning || safetyLevel === SafetyLevel.Blocked + const showWarningIcon = safetyLevel === SafetyLevel.StrongWarning || safetyLevel === SafetyLevel.Blocked if (!isVisible) { return null @@ -69,7 +68,8 @@ export default function TokenWarningModal({ gap="$spacing16" pb={isWeb ? '$none' : '$spacing12'} pt="$spacing12" - px={isWeb ? '$none' : '$spacing24'}> + px={isWeb ? '$none' : '$spacing24'} + > {showWarningIcon ? ( + }} + > {getTokenSafetyHeaderText(safetyLevel, t)} @@ -93,15 +94,17 @@ export default function TokenWarningModal({ - {!hideAcceptButton && ( )} diff --git a/packages/wallet/src/features/tokens/dismissedWarningTokensSelector.ts b/packages/wallet/src/features/tokens/dismissedWarningTokensSelector.ts index 72d0ad2368c..783cc7e26cb 100644 --- a/packages/wallet/src/features/tokens/dismissedWarningTokensSelector.ts +++ b/packages/wallet/src/features/tokens/dismissedWarningTokensSelector.ts @@ -3,7 +3,7 @@ import type { SharedState } from 'wallet/src/state/reducer' // selectors export const dismissedWarningTokensSelector = ( - state: SharedState + state: SharedState, ): { [currencyId: string]: boolean } => state.tokens.dismissedWarningTokens diff --git a/packages/wallet/src/features/tokens/hooks.ts b/packages/wallet/src/features/tokens/hooks.ts index 56b939198bb..2b62b40116d 100644 --- a/packages/wallet/src/features/tokens/hooks.ts +++ b/packages/wallet/src/features/tokens/hooks.ts @@ -1,12 +1,12 @@ import { useMemo } from 'react' +import { getWrappedNativeAddress } from 'uniswap/src/constants/addresses' import { Chain, SearchPopularTokensQuery, useSearchPopularTokensQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getWrappedNativeAddress } from 'wallet/src/constants/addresses' -import { areAddressesEqual } from 'wallet/src/utils/addresses' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' export type TopToken = NonNullable[0]> @@ -35,8 +35,7 @@ export function usePopularTokens(): { return } - const isWeth = - areAddressesEqual(token.address, wethAddress) && token?.chain === Chain.Ethereum + const isWeth = areAddressesEqual(token.address, wethAddress) && token?.chain === Chain.Ethereum // manually replace weth with eth given backend only returns eth data as a proxy for eth if (isWeth && eth) { diff --git a/packages/wallet/src/features/tokens/safetyHooks.ts b/packages/wallet/src/features/tokens/safetyHooks.ts index 46759ede7d5..c8c1e600891 100644 --- a/packages/wallet/src/features/tokens/safetyHooks.ts +++ b/packages/wallet/src/features/tokens/safetyHooks.ts @@ -1,6 +1,4 @@ import { useCallback } from 'react' -import { ThemeKeys } from 'ui/src' -import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { CurrencyId } from 'uniswap/src/types/currency' import { dismissedWarningTokensSelector } from 'wallet/src/features/tokens/dismissedWarningTokensSelector' import { addDismissedWarningToken } from 'wallet/src/features/tokens/tokensSlice' @@ -13,9 +11,7 @@ export function useTokenWarningDismissed(currencyId: Maybe): { const dispatch = useAppDispatch() const dismissedTokens = useAppSelector(dismissedWarningTokensSelector) - const tokenWarningDismissed = Boolean( - currencyId && dismissedTokens && dismissedTokens[currencyId] - ) + const tokenWarningDismissed = Boolean(currencyId && dismissedTokens && dismissedTokens[currencyId]) const dismissWarningCallback = useCallback(() => { if (currencyId) { @@ -28,15 +24,3 @@ export function useTokenWarningDismissed(currencyId: Maybe): { dismissWarningCallback, } } - -export function useTokenSafetyLevelColors(safetyLevel: Maybe): ThemeKeys { - switch (safetyLevel) { - case SafetyLevel.MediumWarning: - return 'DEP_accentWarning' - case SafetyLevel.StrongWarning: - return 'statusCritical' - case SafetyLevel.Blocked: - default: - return 'neutral2' - } -} diff --git a/packages/wallet/src/features/tokens/useCurrencyInfo.ts b/packages/wallet/src/features/tokens/useCurrencyInfo.ts index d5cb8a00af3..5e438691426 100644 --- a/packages/wallet/src/features/tokens/useCurrencyInfo.ts +++ b/packages/wallet/src/features/tokens/useCurrencyInfo.ts @@ -2,10 +2,7 @@ import { useMemo } from 'react' import { useTokenQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { WalletChainId } from 'uniswap/src/types/chains' -import { - currencyIdToContractInput, - gqlTokenToCurrencyInfo, -} from 'wallet/src/features/dataApi/utils' +import { currencyIdToContractInput, gqlTokenToCurrencyInfo } from 'wallet/src/features/dataApi/utils' import { buildNativeCurrencyId, buildWrappedNativeCurrencyId } from 'wallet/src/utils/currencyId' export function useCurrencyInfo(_currencyId?: string): Maybe { diff --git a/packages/wallet/src/features/tokens/utils.ts b/packages/wallet/src/features/tokens/utils.ts index 33e08d66719..aa692cf5f19 100644 --- a/packages/wallet/src/features/tokens/utils.ts +++ b/packages/wallet/src/features/tokens/utils.ts @@ -1,10 +1,7 @@ import { AppTFunction } from 'ui/src/i18n/types' import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -export function getTokenSafetyHeaderText( - safetyLevel: Maybe, - t: AppTFunction -): string | undefined { +export function getTokenSafetyHeaderText(safetyLevel: Maybe, t: AppTFunction): string | undefined { switch (safetyLevel) { case SafetyLevel.MediumWarning: return t('token.safetyLevel.medium.header') diff --git a/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenBaseComponent.tsx b/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenBaseComponent.tsx index 48c5ce2dd1e..7511aa08186 100644 --- a/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenBaseComponent.tsx +++ b/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenBaseComponent.tsx @@ -2,28 +2,22 @@ import { Trans } from 'react-i18next' import { Flex, Text, isWeb } from 'ui/src' import { AlertTriangle, InfoCircle } from 'ui/src/components/icons' import { UniverseChainId } from 'uniswap/src/types/chains' -import { insufficientNativeTokenTextVariant } from 'wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenWarning' +import { INSUFFICIENT_NATIVE_TOKEN_TEXT_VARIANT } from 'wallet/src/features/transactions/InsufficientNativeTokenWarning/constants' import { useInsufficientNativeTokenWarning } from 'wallet/src/features/transactions/InsufficientNativeTokenWarning/useInsufficientNativeTokenWarning' export function InsufficientNativeTokenBaseComponent({ parsedInsufficentNativeTokenWarning, }: { - parsedInsufficentNativeTokenWarning: NonNullable< - ReturnType - > + parsedInsufficentNativeTokenWarning: NonNullable> }): JSX.Element | null { const { nativeCurrency, networkColors, networkName, flow } = parsedInsufficentNativeTokenWarning const currencySymbol = nativeCurrency.symbol - const shouldShowNetworkName = - nativeCurrency.symbol === 'ETH' && nativeCurrency.chainId !== UniverseChainId.Mainnet + const shouldShowNetworkName = nativeCurrency.symbol === 'ETH' && nativeCurrency.chainId !== UniverseChainId.Mainnet const textComponentWithNetworkColor = ( - + ) return ( @@ -33,7 +27,8 @@ export function InsufficientNativeTokenBaseComponent({ backgroundColor={isWeb ? '$surface2' : undefined} borderRadius="$rounded12" gap="$spacing8" - p={isWeb ? '$spacing16' : '$none'}> + p={isWeb ? '$spacing16' : '$none'} + > {isWeb && ( @@ -41,7 +36,7 @@ export function InsufficientNativeTokenBaseComponent({ )} - + {shouldShowNetworkName ? ( flow === 'swap' ? ( @@ -58,7 +57,8 @@ export function InsufficientNativeTokenWarning({ tokenSymbol: nativeCurrency.symbol, }) } - onClose={(): void => setShowModal(false)}> + onClose={(): void => setShowModal(false)} + > {modalOrTooltipMainMessage} diff --git a/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenWarning.tsx b/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenWarning.tsx index 0dcf4809b36..cab6c756606 100644 --- a/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenWarning.tsx +++ b/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenWarning.tsx @@ -1,18 +1,13 @@ -import { isWeb } from 'ui/src' import { NotImplementedError } from 'utilities/src/errors' import { GasFeeResult } from 'wallet/src/features/gas/types' import { Warning } from 'wallet/src/features/transactions/WarningModal/types' -export const insufficientNativeTokenTextVariant = isWeb ? 'body4' : 'body3' - export type InsufficientNativeTokenWarningProps = { warnings: Warning[] flow: 'send' | 'swap' gasFee: GasFeeResult } -export function InsufficientNativeTokenWarning( - _: InsufficientNativeTokenWarningProps -): JSX.Element | null { +export function InsufficientNativeTokenWarning(_: InsufficientNativeTokenWarningProps): JSX.Element | null { throw new NotImplementedError('InsufficientNativeTokenWarning') } diff --git a/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/constants.ts b/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/constants.ts new file mode 100644 index 00000000000..45d0dcc9f06 --- /dev/null +++ b/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/constants.ts @@ -0,0 +1,3 @@ +import { isWeb } from 'ui/src' + +export const INSUFFICIENT_NATIVE_TOKEN_TEXT_VARIANT = isWeb ? 'body4' : 'body3' diff --git a/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/useInsufficientNativeTokenWarning.tsx b/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/useInsufficientNativeTokenWarning.tsx index 88d62d4cdff..b4c2b55b99d 100644 --- a/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/useInsufficientNativeTokenWarning.tsx +++ b/packages/wallet/src/features/transactions/InsufficientNativeTokenWarning/useInsufficientNativeTokenWarning.tsx @@ -3,27 +3,21 @@ import { useMemo } from 'react' import { Trans } from 'react-i18next' import { Text } from 'ui/src' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { UniverseChainId } from 'uniswap/src/types/chains' import { NumberType } from 'utilities/src/format/types' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { useNativeCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { - insufficientNativeTokenTextVariant, - type InsufficientNativeTokenWarningProps, -} from 'wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenWarning' +import { type InsufficientNativeTokenWarningProps } from 'wallet/src/features/transactions/InsufficientNativeTokenWarning/InsufficientNativeTokenWarning' +import { INSUFFICIENT_NATIVE_TOKEN_TEXT_VARIANT } from 'wallet/src/features/transactions/InsufficientNativeTokenWarning/constants' import { Warning, WarningLabel } from 'wallet/src/features/transactions/WarningModal/types' import { useUSDCValue } from 'wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice' import { useNetworkColors } from 'wallet/src/utils/colors' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' -export function useInsufficientNativeTokenWarning({ - flow, - gasFee, - warnings, -}: InsufficientNativeTokenWarningProps): { +export function useInsufficientNativeTokenWarning({ flow, gasFee, warnings }: InsufficientNativeTokenWarningProps): { gasAmount: CurrencyAmount | null | undefined gasAmountFiatFormatted: string nativeCurrency: Currency @@ -48,19 +42,14 @@ export function useInsufficientNativeTokenWarning({ getCurrencyAmount({ value: gasFee.value, valueType: ValueType.Raw, - currency: nativeCurrency?.chainId - ? NativeCurrency.onChain(nativeCurrency.chainId) - : undefined, + currency: nativeCurrency?.chainId ? NativeCurrency.onChain(nativeCurrency.chainId) : undefined, }), - [gasFee.value, nativeCurrency?.chainId] + [gasFee.value, nativeCurrency?.chainId], ) const gasAmountUsd = useUSDCValue(gasAmount) - const gasAmountFiatFormatted = convertFiatAmountFormatted( - gasAmountUsd?.toExact(), - NumberType.FiatGasPrice - ) + const gasAmountFiatFormatted = convertFiatAmountFormatted(gasAmountUsd?.toExact(), NumberType.FiatGasPrice) if (!warning || !nativeCurrency || !nativeCurrencyInfo) { return null @@ -81,7 +70,7 @@ export function useInsufficientNativeTokenWarning({ // We need to pass this as a `component` instead of a `value` because there seems to be a bug in i18next // which causes the value `<$0.01` to be incorrectly escaped. fiatTokenAmount: ( - + {gasAmountFiatFormatted} ), diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/ApproveTransactionDetails.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/ApproveTransactionDetails.tsx new file mode 100644 index 00000000000..a93e6266d1c --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/ApproveTransactionDetails.tsx @@ -0,0 +1,62 @@ +import { useTranslation } from 'react-i18next' +import { Flex, Text, TouchableArea, isWeb } from 'ui/src' +import { iconSizes } from 'ui/src/theme' +import { CurrencyLogo } from 'uniswap/src/components/CurrencyLogo/CurrencyLogo' +import { getSymbolDisplayText } from 'uniswap/src/utils/currency' +import { NumberType } from 'utilities/src/format/types' +import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' +import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' +import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' +import { ApproveTransactionInfo, TransactionDetails } from 'wallet/src/features/transactions/types' +import { buildCurrencyId } from 'wallet/src/utils/currencyId' + +const INFINITE_AMOUNT = 'INF' +const ZERO_AMOUNT = '0.0' +export function ApproveTransactionDetails({ + transactionDetails, + typeInfo, + onClose, +}: { + transactionDetails: TransactionDetails + typeInfo: ApproveTransactionInfo + onClose: () => void +}): JSX.Element { + const { t } = useTranslation() + const { formatNumberOrString } = useLocalizationContext() + const { navigateToTokenDetails } = useWalletNavigation() + const currencyInfo = useCurrencyInfo(buildCurrencyId(transactionDetails.chainId, typeInfo.tokenAddress)) + + const { approvalAmount } = typeInfo + + const amount = + approvalAmount === INFINITE_AMOUNT + ? t('transaction.amount.unlimited') + : approvalAmount && approvalAmount !== ZERO_AMOUNT + ? formatNumberOrString({ value: approvalAmount, type: NumberType.TokenNonTx }) + : '' + + const symbol = getSymbolDisplayText(currencyInfo?.currency.symbol) + + const onPressToken = (): void => { + if (currencyInfo) { + navigateToTokenDetails(currencyInfo.currencyId) + if (!isWeb) { + onClose() + } + } + } + + return ( + + + {amount} + + + + {symbol} + + + + + ) +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/HeaderLogo.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/HeaderLogo.tsx index 371ee5ab4d0..f5c6b563b40 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/HeaderLogo.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/HeaderLogo.tsx @@ -2,13 +2,11 @@ import { Flex, useIsDarkMode, useSporeColors } from 'ui/src' import { ContractInteraction } from 'ui/src/components/icons' import { iconSizes } from 'ui/src/theme' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' -import { - DappLogoWithWCBadge, - LogoWithTxStatus, -} from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' +import { getOptionalServiceProviderLogo } from 'uniswap/src/features/fiatOnRamp/utils' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' +import { DappLogoWithWCBadge, LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { SplitLogo } from 'wallet/src/components/CurrencyLogo/SplitLogo' import { AssetType } from 'wallet/src/entities/assets' -import { getOptionalServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils' import { useCurrencyInfo, useNativeCurrencyInfo, @@ -21,6 +19,8 @@ import { isNFTApproveTransactionInfo, isNFTMintTransactionInfo, isNFTTradeTransactionInfo, + isOnRampPurchaseTransactionInfo, + isOnRampTransferTransactionInfo, isReceiveTokenTransactionInfo, isSendTokenTransactionInfo, isSwapTransactionInfo, @@ -33,13 +33,15 @@ import { NFTApproveTransactionInfo, NFTMintTransactionInfo, NFTTradeTransactionInfo, + OnRampPurchaseInfo, + OnRampTransferInfo, ReceiveTokenTransactionInfo, SendTokenTransactionInfo, TransactionDetails, + UnknownTransactionInfo, WCConfirmInfo, WrapTransactionInfo, } from 'wallet/src/features/transactions/types' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' const TXN_DETAILS_ICON_SIZE = iconSizes.icon40 @@ -75,7 +77,7 @@ const getLogoWithTxStatus = ({ /> ) -export const HeaderLogo = ({ transactionDetails }: HeaderLogoProps): JSX.Element => { +export function HeaderLogo({ transactionDetails }: HeaderLogoProps): JSX.Element { const { typeInfo } = transactionDetails const getHeaderLogoComponent = (): JSX.Element => { @@ -99,8 +101,10 @@ export const HeaderLogo = ({ transactionDetails }: HeaderLogoProps): JSX.Element return } else if (isWrapTransactionInfo(typeInfo)) { return + } else if (isOnRampPurchaseTransactionInfo(typeInfo) || isOnRampTransferTransactionInfo(typeInfo)) { + return } else { - return + return } } @@ -111,10 +115,10 @@ interface SpecificHeaderLogoProps extends HeaderLogoProps { typeInfo: T } -const SwapHeaderLogo = ({ +function SwapHeaderLogo({ transactionDetails, typeInfo, -}: SpecificHeaderLogoProps): JSX.Element => { +}: SpecificHeaderLogoProps): JSX.Element { const inputCurrency = useCurrencyInfo(typeInfo.inputCurrencyId) const outputCurrency = useCurrencyInfo(typeInfo.outputCurrencyId) @@ -128,13 +132,11 @@ const SwapHeaderLogo = ({ ) } -const ApproveHeaderLogo = ({ +function ApproveHeaderLogo({ transactionDetails, typeInfo, -}: SpecificHeaderLogoProps): JSX.Element => { - const currencyInfo = useCurrencyInfo( - buildCurrencyId(transactionDetails.chainId, typeInfo.tokenAddress) - ) +}: SpecificHeaderLogoProps): JSX.Element { + const currencyInfo = useCurrencyInfo(buildCurrencyId(transactionDetails.chainId, typeInfo.tokenAddress)) return getLogoWithTxStatus({ assetType: AssetType.Currency, currencyInfo, @@ -142,22 +144,16 @@ const ApproveHeaderLogo = ({ }) } -const FiatPurchaseHeaderLogo = ({ +function FiatPurchaseHeaderLogo({ transactionDetails, typeInfo, -}: SpecificHeaderLogoProps): JSX.Element => { +}: SpecificHeaderLogoProps): JSX.Element { const outputCurrencyInfo = useCurrencyInfo( typeInfo.outputCurrency?.metadata.contractAddress - ? buildCurrencyId( - transactionDetails.chainId, - typeInfo.outputCurrency?.metadata.contractAddress - ) - : undefined - ) - const serviceProviderLogoUrl = getOptionalServiceProviderLogo( - typeInfo.serviceProviderLogo, - useIsDarkMode() + ? buildCurrencyId(transactionDetails.chainId, typeInfo.outputCurrency?.metadata.contractAddress) + : undefined, ) + const serviceProviderLogoUrl = getOptionalServiceProviderLogo(typeInfo.serviceProviderLogo, useIsDarkMode()) return getLogoWithTxStatus({ assetType: AssetType.Currency, transactionDetails, @@ -167,54 +163,58 @@ const FiatPurchaseHeaderLogo = ({ }) } -const TokenTransferHeaderLogo = ({ +function TokenTransferHeaderLogo({ transactionDetails, typeInfo, -}: SpecificHeaderLogoProps< - ReceiveTokenTransactionInfo | SendTokenTransactionInfo ->): JSX.Element => { +}: SpecificHeaderLogoProps): JSX.Element { const currencyInfo = useCurrencyInfo( typeInfo.assetType === AssetType.Currency ? buildCurrencyId(transactionDetails.chainId, typeInfo.tokenAddress) - : undefined + : undefined, ) return getLogoWithTxStatus({ assetType: typeInfo.assetType, currencyInfo, transactionDetails, - nftImageUrl: - typeInfo.assetType !== AssetType.Currency ? typeInfo.nftSummaryInfo?.imageURL : undefined, + nftImageUrl: typeInfo.assetType !== AssetType.Currency ? typeInfo.nftSummaryInfo?.imageURL : undefined, }) } -const NFTHeaderLogo = ({ +function OnRampHeaderLogo({ transactionDetails, typeInfo, -}: SpecificHeaderLogoProps< - NFTApproveTransactionInfo | NFTMintTransactionInfo | NFTTradeTransactionInfo ->): JSX.Element => - getLogoWithTxStatus({ - assetType: AssetType.ERC721, +}: SpecificHeaderLogoProps): JSX.Element { + const currencyInfo = useCurrencyInfo(buildCurrencyId(transactionDetails.chainId, typeInfo.destinationTokenAddress)) + return getLogoWithTxStatus({ + assetType: AssetType.Currency, + currencyInfo, transactionDetails, - nftImageUrl: typeInfo.nftSummaryInfo.imageURL, }) +} -const WCConfirmHeaderLogo = ({ +function NFTHeaderLogo({ transactionDetails, typeInfo, -}: SpecificHeaderLogoProps): JSX.Element => ( - -) +}: SpecificHeaderLogoProps): JSX.Element { + return getLogoWithTxStatus({ + assetType: AssetType.ERC721, + transactionDetails, + nftImageUrl: typeInfo.nftSummaryInfo.imageURL, + }) +} -const WrapHeaderLogo = ({ - transactionDetails, - typeInfo, -}: SpecificHeaderLogoProps): JSX.Element => { +function WCConfirmHeaderLogo({ transactionDetails, typeInfo }: SpecificHeaderLogoProps): JSX.Element { + return ( + + ) +} + +function WrapHeaderLogo({ transactionDetails, typeInfo }: SpecificHeaderLogoProps): JSX.Element { const unwrapped = typeInfo.unwrapped const nativeCurrencyInfo = useNativeCurrencyInfo(transactionDetails.chainId) const wrappedCurrencyInfo = useWrappedNativeCurrencyInfo(transactionDetails.chainId) @@ -229,7 +229,21 @@ const WrapHeaderLogo = ({ ) } -const UnknownHeaderLogo = (): JSX.Element => { +function UnknownHeaderLogo({ + transactionDetails, + typeInfo, +}: SpecificHeaderLogoProps): JSX.Element { const colors = useSporeColors() - return + return typeInfo.dappInfo?.icon ? ( + + ) : ( + + ) } diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/NftTransactionDetails.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/NftTransactionDetails.tsx new file mode 100644 index 00000000000..629c8b9df4f --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/NftTransactionDetails.tsx @@ -0,0 +1,109 @@ +import { Flex, Text, TouchableArea, isWeb } from 'ui/src' +import { RotatableChevron } from 'ui/src/components/icons' +import { iconSizes } from 'ui/src/theme' +import { UniverseChainId } from 'uniswap/src/types/chains' +import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' +import { NFTViewer } from 'wallet/src/features/images/NFTViewer' +import { + NFTApproveTransactionInfo, + NFTMintTransactionInfo, + NFTSummaryInfo, + NFTTradeTransactionInfo, + ReceiveTokenTransactionInfo, + SendTokenTransactionInfo, + TransactionDetails, +} from 'wallet/src/features/transactions/types' + +const MAX_NFT_IMAGE_HEIGHT = 375 + +export function NftTransactionDetails({ + transactionDetails, + typeInfo, + onClose, +}: { + transactionDetails: TransactionDetails + typeInfo: + | ReceiveTokenTransactionInfo + | SendTokenTransactionInfo + | NFTTradeTransactionInfo + | NFTMintTransactionInfo + | NFTApproveTransactionInfo + onClose: () => void +}): JSX.Element { + if (!typeInfo.nftSummaryInfo) { + return <> + } + + return ( + + ) +} + +export function NftTransactionContent({ + chainId, + nftSummaryInfo, + onClose, +}: { + chainId: UniverseChainId + nftSummaryInfo: NFTSummaryInfo + onClose: () => void +}): JSX.Element { + const { navigateToNftCollection, navigateToNftDetails } = useWalletNavigation() + + const onPressNft = (): void => { + navigateToNftDetails({ + address: nftSummaryInfo.address, + tokenId: nftSummaryInfo.tokenId, + }) + onClose() + } + + const onPressCollection = (): void => { + // Collection should not be clickable on L2s + if (chainId === UniverseChainId.Mainnet) { + navigateToNftCollection({ collectionAddress: nftSummaryInfo.address }) + onClose() + } + } + + const disableOnPressNftItem = isWeb + const disableOnPressNftCollection = isWeb || chainId !== UniverseChainId.Mainnet + + return ( + + + + + + {nftSummaryInfo.name} + + + + {nftSummaryInfo.collectionName} + + {!disableOnPressNftCollection && ( + + )} + + + + + ) +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/OnRampTransactionDetails.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/OnRampTransactionDetails.tsx new file mode 100644 index 00000000000..f5f7029a80a --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/OnRampTransactionDetails.tsx @@ -0,0 +1,47 @@ +import { getSymbolDisplayText } from 'uniswap/src/utils/currency' +import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' +import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' +import { CurrencyTransferContent } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/TransferTransactionDetails' +import { useFormattedCurrencyAmountAndUSDValue } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/utils' +import { + OnRampPurchaseInfo, + OnRampTransferInfo, + TransactionDetails, + TransactionType, +} from 'wallet/src/features/transactions/types' +import { buildCurrencyId } from 'wallet/src/utils/currencyId' +import { ValueType } from 'wallet/src/utils/getCurrencyAmount' + +export function OnRampTransactionDetails({ + transactionDetails, + typeInfo, + onClose, +}: { + transactionDetails: TransactionDetails + typeInfo: OnRampTransferInfo | OnRampPurchaseInfo + onClose: () => void +}): JSX.Element { + const formatter = useLocalizationContext() + const currencyInfo = useCurrencyInfo(buildCurrencyId(transactionDetails.chainId, typeInfo.destinationTokenAddress)) + + const { amount, value } = useFormattedCurrencyAmountAndUSDValue({ + currency: currencyInfo?.currency, + currencyAmountRaw: typeInfo.destinationTokenAmount.toString(), + formatter, + isApproximateAmount: false, + valueType: ValueType.Exact, + }) + const symbol = getSymbolDisplayText(currencyInfo?.currency.symbol) + + const tokenAmountWithSymbol = symbol ? amount + ' ' + symbol : amount // Prevents 'undefined' from being displayed + + return ( + + ) +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/SwapTransactionDetails.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/SwapTransactionDetails.tsx index 106819a1041..9b0cddb19ec 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/SwapTransactionDetails.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/SwapTransactionDetails.tsx @@ -1,108 +1,139 @@ -import { Currency, TradeType } from '@uniswap/sdk-core' -import { Flex, Text, useSporeColors } from 'ui/src' +import { TradeType } from '@uniswap/sdk-core' +import { Flex, Text, TouchableArea, isWeb, useSporeColors } from 'ui/src' import { iconSizes } from 'ui/src/theme' import { CurrencyLogo } from 'uniswap/src/components/CurrencyLogo/CurrencyLogo' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' -import { NumberType } from 'utilities/src/format/types' import { Arrow } from 'wallet/src/components/icons/Arrow' -import { - LocalizationContextState, - useLocalizationContext, -} from 'wallet/src/features/language/LocalizationContext' +import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' +import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { SwapTypeTransactionInfo } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/types' +import { useFormattedCurrencyAmountAndUSDValue } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/utils' import { getAmountsFromTrade } from 'wallet/src/features/transactions/getAmountsFromTrade' -import { useUSDCValue } from 'wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice' import { isConfirmedSwapTypeInfo } from 'wallet/src/features/transactions/types' -import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' -export const SwapTransactionDetails = ({ +export function SwapTransactionDetails({ typeInfo, + onClose, }: { typeInfo: SwapTypeTransactionInfo -}): JSX.Element => { - const colors = useSporeColors() - const formatter = useLocalizationContext() + onClose: () => void +}): JSX.Element { const inputCurrency = useCurrencyInfo(typeInfo.inputCurrencyId) const outputCurrency = useCurrencyInfo(typeInfo.outputCurrencyId) const isConfirmed = isConfirmedSwapTypeInfo(typeInfo) const { inputCurrencyAmountRaw, outputCurrencyAmountRaw } = getAmountsFromTrade(typeInfo) + + return ( + + ) +} + +export function SwapTransactionContent({ + inputCurrency, + outputCurrency, + isConfirmed, + inputCurrencyAmountRaw, + outputCurrencyAmountRaw, + tradeType, + onClose, +}: { + inputCurrency: Maybe + outputCurrency: Maybe + isConfirmed: boolean + inputCurrencyAmountRaw: string + outputCurrencyAmountRaw: string + tradeType?: TradeType + onClose: () => void +}): JSX.Element { + const colors = useSporeColors() + const formatter = useLocalizationContext() + const { navigateToTokenDetails } = useWalletNavigation() + const { tilde: inputTilde, amount: inputAmount, value: inputValue, - } = useFormattedCurrencyAmountAndUSDValue( - inputCurrency?.currency, - inputCurrencyAmountRaw, + } = useFormattedCurrencyAmountAndUSDValue({ + currency: inputCurrency?.currency, + currencyAmountRaw: inputCurrencyAmountRaw, formatter, - isConfirmed ? false : typeInfo.tradeType === TradeType.EXACT_OUTPUT - ) + isApproximateAmount: isConfirmed ? false : tradeType === TradeType.EXACT_OUTPUT, + }) const { tilde: outputTilde, amount: outputAmount, value: outputValue, - } = useFormattedCurrencyAmountAndUSDValue( - outputCurrency?.currency, - outputCurrencyAmountRaw, + } = useFormattedCurrencyAmountAndUSDValue({ + currency: outputCurrency?.currency, + currencyAmountRaw: outputCurrencyAmountRaw, formatter, - isConfirmed ? false : typeInfo.tradeType === TradeType.EXACT_INPUT - ) - + isApproximateAmount: isConfirmed ? false : tradeType === TradeType.EXACT_INPUT, + }) const inputSymbol = getSymbolDisplayText(inputCurrency?.currency.symbol) const outputSymbol = getSymbolDisplayText(outputCurrency?.currency.symbol) + const onPressInputToken = (): void => { + if (inputCurrency) { + navigateToTokenDetails(inputCurrency.currencyId) + if (!isWeb) { + onClose() + } + } + } + + const onPressOutputToken = (): void => { + if (outputCurrency) { + navigateToTokenDetails(outputCurrency.currencyId) + if (!isWeb) { + onClose() + } + } + } + return ( - - - - - {inputTilde} - {inputAmount} {inputSymbol} - - - {inputValue} - + + + + + + {inputTilde} + {inputAmount} {inputSymbol} + + + {inputValue} + + + - - + - - - - {outputTilde} - {outputAmount} {outputSymbol} - - - {outputValue} - + + + + + {outputTilde} + {outputAmount} {outputSymbol} + + + {outputValue} + + + - - + ) } - -function useFormattedCurrencyAmountAndUSDValue( - currency: Maybe, - currencyAmountRaw: string, - formatter: LocalizationContextState, - isApproximateAmount = false, - valueType = ValueType.Raw -): { amount: string; value: string; tilde: string } { - const currencyAmount = getCurrencyAmount({ - value: currencyAmountRaw, - valueType, - currency, - }) - - const value = useUSDCValue(currencyAmount) - const formattedAmount = formatter.formatCurrencyAmount({ value: currencyAmount }) - return { - tilde: isApproximateAmount ? '~' : '', - amount: `${formattedAmount}`, - value: formatter.formatCurrencyAmount({ value, type: NumberType.FiatTokenPrice }), - } -} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsInfoRows.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsInfoRows.tsx new file mode 100644 index 00000000000..4701ff0a495 --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsInfoRows.tsx @@ -0,0 +1,216 @@ +import { PropsWithChildren } from 'react' +import { useTranslation } from 'react-i18next' +import { Flex, Text, TouchableArea, UniversalImage, UniversalImageResizeMode, useIsDarkMode } from 'ui/src' +import { CopyAlt, ExternalLink, Unitag } from 'ui/src/components/icons' +import { borderRadii, iconSizes } from 'ui/src/theme' +import { NetworkLogo } from 'uniswap/src/components/CurrencyLogo/NetworkLogo' +import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks' +import { UniverseChainId } from 'uniswap/src/types/chains' +import { shortenAddress } from 'utilities/src/addresses' +import { useENS } from 'wallet/src/features/ens/useENS' +import { pushNotification } from 'wallet/src/features/notifications/slice' +import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types' +import { useNetworkFee } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/hooks' +import { shortenHash } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/utils' +import { ContentRow } from 'wallet/src/features/transactions/TransactionRequest/ContentRow' +import { TransactionDetails, TransactionType } from 'wallet/src/features/transactions/types' +import { useAppDispatch } from 'wallet/src/state' +import { setClipboard } from 'wallet/src/utils/clipboard' +import { ExplorerDataType, getExplorerLink, openUri } from 'wallet/src/utils/linking' + +export function TransactionDetailsInfoRows({ + transactionDetails, +}: { + transactionDetails: TransactionDetails +}): JSX.Element { + const rows = useTransactionDetailsInfoRows(transactionDetails) + + return ( + + {rows} + + ) +} + +export function useTransactionDetailsInfoRows(transactionDetails: TransactionDetails): JSX.Element[] { + const { t } = useTranslation() + const isDarkMode = useIsDarkMode() + + const { typeInfo } = transactionDetails + + const defaultRows = [ + , + , + ] + const specificRows: JSX.Element[] = [] + + switch (typeInfo.type) { + case TransactionType.Approve: + case TransactionType.NFTApprove: + case TransactionType.NFTMint: + if (typeInfo.dappInfo && typeInfo.dappInfo.name) { + specificRows.push() + } + break + case TransactionType.WCConfirm: + specificRows.push() + break + case TransactionType.Receive: + specificRows.push() + break + case TransactionType.Send: + specificRows.push() + break + case TransactionType.OnRampPurchase: + case TransactionType.OnRampTransfer: + specificRows.push( + + + {typeInfo.serviceProvider.name} + , + ) + break + case TransactionType.Swap: + case TransactionType.Wrap: + case TransactionType.FiatPurchase: + case TransactionType.NFTTrade: + // For now, these cases don't add any specific rows + break + + case TransactionType.Unknown: + if (typeInfo.dappInfo) { + if (typeInfo.dappInfo.name) { + specificRows.push( + , + ) + } + specificRows.push( + + {shortenAddress(typeInfo.dappInfo.address)} + => { + if (typeInfo.dappInfo?.address) { + await openUri( + getExplorerLink(transactionDetails.chainId, typeInfo.dappInfo.address, ExplorerDataType.ADDRESS), + ) + } + }} + > + + + , + ) + } + break + default: + break + } + + // Combine specific rows and default rows + // In the future, you can modify this logic to omit or change default rows for specific types + return [...specificRows, ...defaultRows] +} + +function NetworkFeeRow({ transactionDetails }: { transactionDetails: TransactionDetails }): JSX.Element { + const { t } = useTranslation() + const { value: networkFeeValue } = useNetworkFee(transactionDetails) + + return ( + + + {networkFeeValue} + + ) +} + +function TransactionHashRow({ hash }: { hash?: string }): JSX.Element { + const { t } = useTranslation() + const dispatch = useAppDispatch() + + const onPressCopy = async (): Promise => { + if (!hash) { + return + } + + await setClipboard(hash) + dispatch( + pushNotification({ + type: AppNotificationType.Copied, + copyType: CopyNotificationType.TransactionId, + }), + ) + } + + return ( + + {shortenHash(hash)} + + + + + ) +} + +function InfoRow({ + label, + children, +}: PropsWithChildren & { + label: string +}): JSX.Element { + return ( + + + {children} + + + ) +} + +function DappInfoRow({ name, iconUrl }: { name: string; iconUrl?: string | null }): JSX.Element { + const { t } = useTranslation() + return ( + + {iconUrl && ( + + )} + {name} + + ) +} + +function TransactionParticipantRow({ address, isSend = false }: { address: string; isSend?: boolean }): JSX.Element { + const { t } = useTranslation() + const { name: ensName } = useENS(UniverseChainId.Mainnet, address, true) + const { unitag } = useUnitagByAddress(address) + const personDisplayName = unitag?.username ?? ensName ?? shortenAddress(address) + return ( + + {personDisplayName} + {unitag?.username && } + + ) +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal.test.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal.test.tsx new file mode 100644 index 00000000000..41ef5c5b1fd --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal.test.tsx @@ -0,0 +1,98 @@ +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' +import { TransactionDetailsInfoRows } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsInfoRows' +import { + TransactionDetailsContent, + TransactionDetailsHeader, +} from 'wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal' +import { TransactionStatus } from 'wallet/src/features/transactions/types' +import { + ACCOUNT, + ARBITRUM_DAI_CURRENCY_INFO, + BASE_CURRENCY, + ETH_CURRENCY_INFO, + OPTIMISM_CURRENCY, + POLYGON_CURRENCY, + currencyInfo, + finalizedTransactionDetails, + preloadedSharedState, +} from 'wallet/src/test/fixtures' +import { renderWithProviders } from 'wallet/src/test/render' + +const preloadedState = preloadedSharedState({ account: ACCOUNT }) + +// Helper function to get currency info based on chain ID +const getCurrencyInfoForChain = (chainId: number): CurrencyInfo => { + switch (chainId) { + case 1: // Mainnet + return ETH_CURRENCY_INFO + case 42161: // Arbitrum One + return ARBITRUM_DAI_CURRENCY_INFO + case 8453: // Base + return currencyInfo({ nativeCurrency: BASE_CURRENCY }) + case 10: // Optimism + return currencyInfo({ nativeCurrency: OPTIMISM_CURRENCY }) + case 137: // Polygon + return currencyInfo({ nativeCurrency: POLYGON_CURRENCY }) + default: + return ETH_CURRENCY_INFO // fallback to ETH + } +} + +// Mock useCurrencyInfo +jest.mock('wallet/src/features/tokens/useCurrencyInfo', () => ({ + useCurrencyInfo: (currencyId: string | undefined): Maybe => { + if (!currencyId) { + return null + } + const [, chainIdStr] = currencyId.split(':') + if (!chainIdStr) { + return null + } + const chainId = parseInt(chainIdStr, 10) + return getCurrencyInfoForChain(chainId) + }, +})) + +jest.mock('wallet/src/features/language/localizedDayjs', () => ({ + useFormattedDateTime: jest.fn(() => 'January 1, 2023 12:00 AM'), + FORMAT_DATE_TIME_MEDIUM: 'MMMM D, YYYY h:mm A', +})) + +describe('TransactionDetails Components', () => { + const mockTransaction = finalizedTransactionDetails({ + status: TransactionStatus.Success, + }) + console.log(mockTransaction) + + it('renders TransactionDetailsHeader without error', () => { + const onClose = jest.fn() + + const { toJSON } = renderWithProviders( + , + { preloadedState }, + ) + + expect(toJSON()).toMatchSnapshot() + }) + + it('renders TransactionDetailsContent without error', () => { + const onClose = jest.fn() + + const { toJSON } = renderWithProviders( + , + { + preloadedState, + }, + ) + + expect(toJSON()).toMatchSnapshot() + }) + + it('renders TransactionDetailsInfoRows without error', () => { + const { toJSON } = renderWithProviders(, { + preloadedState, + }) + + expect(toJSON()).toMatchSnapshot() + }) +}) diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal.tsx index 65a3b03783a..7513651f0af 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal.tsx @@ -1,174 +1,246 @@ import dayjs from 'dayjs' +import { useMemo } from 'react' import { useTranslation } from 'react-i18next' -import { Button, Flex, Separator, Text, TouchableArea } from 'ui/src' -import { CopyAlt, TripleDots } from 'ui/src/components/icons' +import { + Button, + ContextMenu, + Flex, + MenuContentItem, + Separator, + Text, + TouchableArea, + isWeb, + useIsDarkMode, +} from 'ui/src' +import { CopySheets, HelpCenter, TripleDots, UniswapX } from 'ui/src/components/icons' +import { UNIVERSE_CHAIN_LOGO } from 'uniswap/src/assets/chainLogos' import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal' +import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { ModalName } from 'uniswap/src/features/telemetry/constants' -import { - FORMAT_DATE_TIME_MEDIUM, - useFormattedDateTime, -} from 'wallet/src/features/language/localizedDayjs' +import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' +import { AssetType } from 'wallet/src/entities/assets' +import { AuthTrigger } from 'wallet/src/features/auth/types' +import { FORMAT_DATE_TIME_MEDIUM, useFormattedDateTime } from 'wallet/src/features/language/localizedDayjs' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types' +import { ApproveTransactionDetails } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/ApproveTransactionDetails' import { HeaderLogo } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/HeaderLogo' +import { NftTransactionDetails } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/NftTransactionDetails' +import { OnRampTransactionDetails } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/OnRampTransactionDetails' import { SwapTransactionDetails } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/SwapTransactionDetails' +import { TransactionDetailsInfoRows } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsInfoRows' +import { TransferTransactionDetails } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/TransferTransactionDetails' +import { WrapTransactionDetails } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/WrapTransactionDetails' import { isApproveTransactionInfo, isFiatPurchaseTransactionInfo, isNFTApproveTransactionInfo, isNFTMintTransactionInfo, isNFTTradeTransactionInfo, + isOnRampPurchaseTransactionInfo, + isOnRampTransferTransactionInfo, isReceiveTokenTransactionInfo, isSendTokenTransactionInfo, isSwapTransactionInfo, + isUnknownTransactionInfo, isWCConfirmTransactionInfo, isWrapTransactionInfo, } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/types' +import { useTransactionActionsCancelModals } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/useTransactionActionsCancelModals' +import { + getTransactionId, + openSupportLink, +} from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal' import { getTransactionSummaryTitle } from 'wallet/src/features/transactions/SummaryCards/utils' -import { TransactionDetails } from 'wallet/src/features/transactions/types' +import { TransactionDetails, TransactionTypeInfo } from 'wallet/src/features/transactions/types' import { useAppDispatch } from 'wallet/src/state' import { setClipboard } from 'wallet/src/utils/clipboard' +import { openTransactionLink } from 'wallet/src/utils/linking' type TransactionDetailsModalProps = { + authTrigger?: AuthTrigger onClose: () => void transactionDetails: TransactionDetails } -const TransactionDetailsHeader = ({ +export function TransactionDetailsHeader({ + authTrigger, + onClose, transactionDetails, -}: { - transactionDetails: TransactionDetails -}): JSX.Element => { +}: TransactionDetailsModalProps): JSX.Element { const { t } = useTranslation() - const dateString = useFormattedDateTime( - dayjs(transactionDetails.addedTime), - FORMAT_DATE_TIME_MEDIUM - ) + const dispatch = useAppDispatch() + const isDarkMode = useIsDarkMode() + + const { openActionsModal, renderModals } = useTransactionActionsCancelModals({ + authTrigger, + onNavigateAway: onClose, + transaction: transactionDetails, + }) + + const dateString = useFormattedDateTime(dayjs(transactionDetails.addedTime), FORMAT_DATE_TIME_MEDIUM) const title = getTransactionSummaryTitle(transactionDetails, t) + const chainInfo = UNIVERSE_CHAIN_INFO[transactionDetails.chainId] + const chainLogo = UNIVERSE_CHAIN_LOGO[transactionDetails.chainId].explorer + + const menuContent = useMemo(() => { + const items: MenuContentItem[] = [] + + if (transactionDetails.hash) { + items.push({ + label: t('transaction.action.viewEtherscan', { blockExplorerName: chainInfo.explorer.name }), + textProps: { variant: 'body2' }, + onPress: () => openTransactionLink(transactionDetails.hash, transactionDetails.chainId), + Icon: isDarkMode ? chainLogo.logoDark : chainLogo.logoLight, + }) + } + + const transactionId = getTransactionId(transactionDetails) + if (transactionId) { + items.push({ + label: t('transaction.action.copy'), + textProps: { variant: 'body2' }, + onPress: async () => { + await setClipboard(transactionId) + dispatch( + pushNotification({ + type: AppNotificationType.Copied, + copyType: CopyNotificationType.TransactionId, + }), + ) + }, + Icon: CopySheets, + }) + + items.push({ + label: t('settings.action.help'), + textProps: { variant: 'body2' }, + onPress: async (): Promise => { + await openSupportLink(transactionDetails) + }, + Icon: HelpCenter, + }) + } + + return items + }, [dispatch, t, transactionDetails, chainInfo, chainLogo, isDarkMode]) + return ( - - - - - {title} - {dateString} + <> + + + + + + {(transactionDetails.routing === Routing.DUTCH_V2 || + transactionDetails.routing === Routing.DUTCH_LIMIT) && } + {title} + + + {dateString} + + + {isWeb ? ( + + + + + + ) : ( + + + + )} - - + {renderModals()} + ) } -const TransactionDetailsContent = ({ +export function TransactionDetailsContent({ transactionDetails, + onClose, }: { transactionDetails: TransactionDetails -}): JSX.Element => { + onClose: () => void +}): JSX.Element | null { const { typeInfo } = transactionDetails - const getContentComponent = (): JSX.Element => { + const getContentComponent = (): JSX.Element | null => { if (isApproveTransactionInfo(typeInfo)) { - return <> + return } else if (isFiatPurchaseTransactionInfo(typeInfo)) { return <> } else if (isNFTApproveTransactionInfo(typeInfo)) { - return <> + return } else if (isNFTMintTransactionInfo(typeInfo)) { - return <> + return } else if (isNFTTradeTransactionInfo(typeInfo)) { - return <> - } else if (isReceiveTokenTransactionInfo(typeInfo)) { - return <> - } else if (isSendTokenTransactionInfo(typeInfo)) { - return <> + return + } else if (isReceiveTokenTransactionInfo(typeInfo) || isSendTokenTransactionInfo(typeInfo)) { + return ( + + ) } else if (isSwapTransactionInfo(typeInfo)) { - return + return } else if (isWCConfirmTransactionInfo(typeInfo)) { return <> } else if (isWrapTransactionInfo(typeInfo)) { - return <> + return + } else if (isOnRampPurchaseTransactionInfo(typeInfo) || isOnRampTransferTransactionInfo(typeInfo)) { + return } else { - return <> + return null } } - return ( - - {getContentComponent()} - - ) -} - -const TransactionDetailsInfoRows = ({ - transactionDetails, -}: { - transactionDetails: TransactionDetails -}): JSX.Element => { - const { t } = useTranslation() - const dispatch = useAppDispatch() - - const onPressCopy = async (): Promise => { - if (!transactionDetails.hash) { - return - } - - await setClipboard(transactionDetails.hash) - dispatch( - pushNotification({ - type: AppNotificationType.Copied, - copyType: CopyNotificationType.TransactionId, - }) - ) + const contentComponent = getContentComponent() + if (contentComponent === null) { + return null } + return {contentComponent} +} - return ( - - - {t('transaction.details.networkFee')} - $99.22 - - - {t('transaction.details.transactionId')} - - {shortenHash(transactionDetails.hash)} - - - - - - - ) +const isNFTActivity = (typeInfo: TransactionTypeInfo): boolean => { + const isTransferNft = + (isReceiveTokenTransactionInfo(typeInfo) || isSendTokenTransactionInfo(typeInfo)) && + typeInfo.assetType !== AssetType.Currency + const isNft = + isTransferNft || + isNFTApproveTransactionInfo(typeInfo) || + isNFTMintTransactionInfo(typeInfo) || + isNFTTradeTransactionInfo(typeInfo) + return isNft } -export const TransactionDetailsModal = ({ +export function TransactionDetailsModal({ + authTrigger, onClose, transactionDetails, -}: TransactionDetailsModalProps): JSX.Element => { +}: TransactionDetailsModalProps): JSX.Element { const { t } = useTranslation() + const { typeInfo } = transactionDetails + + // Hide both separators if it's an Nft transaction. Hide top separator if it's an unknown type transaction. + const isNftTransaction = isNFTActivity(typeInfo) + const hideTopSeparator = isNftTransaction || isUnknownTransactionInfo(typeInfo) + const hideBottomSeparator = isNftTransaction + return ( - - - - - - + + + + {!hideTopSeparator && } + + {!hideBottomSeparator && } - + {isWeb && ( + + )} ) } - -function shortenHash(hash: string | undefined, chars: NumberRange<1, 20> = 4): string { - if (!hash) { - return '' - } - return `${hash.substring(0, chars + 2)}...${hash.substring(hash.length - chars)}` -} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransferTransactionDetails.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransferTransactionDetails.tsx new file mode 100644 index 00000000000..afb2bb94eb8 --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/TransferTransactionDetails.tsx @@ -0,0 +1,93 @@ +import { Flex, Text, TouchableArea, isWeb } from 'ui/src' +import { iconSizes } from 'ui/src/theme' +import { CurrencyLogo } from 'uniswap/src/components/CurrencyLogo/CurrencyLogo' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' +import { getSymbolDisplayText } from 'uniswap/src/utils/currency' +import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' +import { AssetType } from 'wallet/src/entities/assets' +import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' +import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' +import { NftTransactionDetails } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/NftTransactionDetails' +import { useFormattedCurrencyAmountAndUSDValue } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/utils' +import { + ReceiveTokenTransactionInfo, + SendTokenTransactionInfo, + TransactionDetails, +} from 'wallet/src/features/transactions/types' +import { buildCurrencyId } from 'wallet/src/utils/currencyId' + +export function TransferTransactionDetails({ + transactionDetails, + typeInfo, + onClose, +}: { + transactionDetails: TransactionDetails + typeInfo: ReceiveTokenTransactionInfo | SendTokenTransactionInfo + onClose: () => void +}): JSX.Element { + const formatter = useLocalizationContext() + const isCurrency = typeInfo.assetType === AssetType.Currency + const currencyInfo = useCurrencyInfo( + isCurrency ? buildCurrencyId(transactionDetails.chainId, typeInfo.tokenAddress) : undefined, + ) + + const { amount, value } = useFormattedCurrencyAmountAndUSDValue({ + currency: currencyInfo?.currency, + currencyAmountRaw: typeInfo.currencyAmountRaw, + formatter, + isApproximateAmount: false, + }) + const symbol = getSymbolDisplayText(currencyInfo?.currency.symbol) + + const tokenAmountWithSymbol = symbol ? amount + ' ' + symbol : amount // Prevents 'undefined' from being displayed + + return isCurrency ? ( + + ) : ( + + ) +} + +export function CurrencyTransferContent({ + tokenAmountWithSymbol, + currencyInfo, + value, + onClose, + showValueAsHeading = false, +}: { + tokenAmountWithSymbol: string | undefined + currencyInfo: Maybe + value: string + onClose: () => void + showValueAsHeading?: boolean +}): JSX.Element { + const { navigateToTokenDetails } = useWalletNavigation() + + const onPressToken = (): void => { + if (currencyInfo) { + navigateToTokenDetails(currencyInfo.currencyId) + if (!isWeb) { + onClose() + } + } + } + + return ( + + + {showValueAsHeading ? value : tokenAmountWithSymbol} + + + + {showValueAsHeading ? tokenAmountWithSymbol : value} + + + + + ) +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/WrapTransactionDetails.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/WrapTransactionDetails.tsx new file mode 100644 index 00000000000..459a8a78270 --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/WrapTransactionDetails.tsx @@ -0,0 +1,31 @@ +import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' +import { SwapTransactionContent } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/SwapTransactionDetails' +import { TransactionDetails, WrapTransactionInfo } from 'wallet/src/features/transactions/types' +import { buildNativeCurrencyId, buildWrappedNativeCurrencyId } from 'wallet/src/utils/currencyId' + +export function WrapTransactionDetails({ + transactionDetails, + typeInfo, + onClose, +}: { + transactionDetails: TransactionDetails + typeInfo: WrapTransactionInfo + onClose: () => void +}): JSX.Element { + const nativeCurrency = useCurrencyInfo(buildNativeCurrencyId(transactionDetails.chainId)) + const wrappedNativeCurrency = useCurrencyInfo(buildWrappedNativeCurrencyId(transactionDetails.chainId)) + + const inputCurrency = typeInfo.unwrapped ? wrappedNativeCurrency : nativeCurrency + const outputCurrency = typeInfo.unwrapped ? nativeCurrency : wrappedNativeCurrency + + return ( + + ) +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/__snapshots__/TransactionDetailsModal.test.tsx.snap b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/__snapshots__/TransactionDetailsModal.test.tsx.snap new file mode 100644 index 00000000000..6aa0acee7f8 --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/__snapshots__/TransactionDetailsModal.test.tsx.snap @@ -0,0 +1,636 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TransactionDetails Components renders TransactionDetailsContent without error 1`] = ` + + + + + + + + + + +`; + +exports[`TransactionDetails Components renders TransactionDetailsHeader without error 1`] = ` + + + + + + + +
+
+
+ + + + + + + + + Approved + + + + January 1, 2023 12:00 AM + + + + + + + + + + + + + +`; + +exports[`TransactionDetails Components renders TransactionDetailsInfoRows without error 1`] = ` + + + + Network cost + + + +
+
+
+ + + - + + + + + + Transaction ID + + + + b568a9...0600 + + + + + + + + + + + +`; diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/hooks.ts b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/hooks.ts new file mode 100644 index 00000000000..7b96ab758c2 --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/hooks.ts @@ -0,0 +1,26 @@ +import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' +import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' +import { useFormattedCurrencyAmountAndUSDValue } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/utils' +import { TransactionDetails } from 'wallet/src/features/transactions/types' +import { buildCurrencyId } from 'wallet/src/utils/currencyId' +import { ValueType } from 'wallet/src/utils/getCurrencyAmount' + +export function useNetworkFee(transactionDetails: TransactionDetails): { + value: string + amount: string +} { + const formatter = useLocalizationContext() + + const currencyId = transactionDetails.networkFee + ? buildCurrencyId(transactionDetails.chainId, transactionDetails.networkFee.tokenAddress) + : undefined + const currencyInfo = useCurrencyInfo(currencyId) + + return useFormattedCurrencyAmountAndUSDValue({ + currency: currencyInfo?.currency, + currencyAmountRaw: transactionDetails.networkFee?.quantity, + valueType: ValueType.Exact, + formatter, + isApproximateAmount: false, + }) +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/types.ts b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/types.ts index 5d433669a35..d978fba40a8 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/types.ts +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/types.ts @@ -2,6 +2,10 @@ import { ConfirmedSwapTransactionInfo, ExactInputSwapTransactionInfo, ExactOutputSwapTransactionInfo, + OnRampPurchaseInfo, + OnRampTransferInfo, + UnknownTransactionInfo, + WrapTransactionInfo, } from 'wallet/src/features/transactions/types' export type SwapTypeTransactionInfo = @@ -22,60 +26,54 @@ import { WCConfirmInfo, } from 'wallet/src/features/transactions/types' -export function isApproveTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is ApproveTransactionInfo { +export function isApproveTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is ApproveTransactionInfo { return typeInfo.type === TransactionType.Approve } -export function isFiatPurchaseTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is FiatPurchaseTransactionInfo { +export function isFiatPurchaseTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is FiatPurchaseTransactionInfo { return typeInfo.type === TransactionType.FiatPurchase } -export function isNFTApproveTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is NFTApproveTransactionInfo { +export function isOnRampPurchaseTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is OnRampPurchaseInfo { + return typeInfo.type === TransactionType.OnRampPurchase +} + +export function isOnRampTransferTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is OnRampTransferInfo { + return typeInfo.type === TransactionType.OnRampTransfer +} + +export function isNFTApproveTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is NFTApproveTransactionInfo { return typeInfo.type === TransactionType.NFTApprove } -export function isNFTMintTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is NFTMintTransactionInfo { +export function isNFTMintTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is NFTMintTransactionInfo { return typeInfo.type === TransactionType.NFTMint } -export function isNFTTradeTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is NFTTradeTransactionInfo { +export function isNFTTradeTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is NFTTradeTransactionInfo { return typeInfo.type === TransactionType.NFTTrade } -export function isReceiveTokenTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is ReceiveTokenTransactionInfo { +export function isReceiveTokenTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is ReceiveTokenTransactionInfo { return typeInfo.type === TransactionType.Receive } -export function isSendTokenTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is SendTokenTransactionInfo { +export function isSendTokenTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is SendTokenTransactionInfo { return typeInfo.type === TransactionType.Send } -export function isSwapTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is SwapTypeTransactionInfo { +export function isSwapTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is SwapTypeTransactionInfo { return typeInfo.type === TransactionType.Swap } -export function isWCConfirmTransactionInfo( - typeInfo: TransactionTypeInfo -): typeInfo is WCConfirmInfo { +export function isWCConfirmTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is WCConfirmInfo { return typeInfo.type === TransactionType.WCConfirm } -export function isWrapTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is WCConfirmInfo { +export function isWrapTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is WrapTransactionInfo { return typeInfo.type === TransactionType.Wrap } + +export function isUnknownTransactionInfo(typeInfo: TransactionTypeInfo): typeInfo is UnknownTransactionInfo { + return typeInfo.type === TransactionType.Unknown +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/useTransactionActionsCancelModals.tsx b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/useTransactionActionsCancelModals.tsx new file mode 100644 index 00000000000..7fdf7660fb8 --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/useTransactionActionsCancelModals.tsx @@ -0,0 +1,141 @@ +import { providers } from 'ethers' +import { useEffect, useState } from 'react' +import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal' +import { ModalName } from 'uniswap/src/features/telemetry/constants' +import { CurrencyId } from 'uniswap/src/types/currency' +import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' +import { AuthTrigger } from 'wallet/src/features/auth/types' +import { CancelConfirmationView } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/CancelConfirmationView' +import TransactionActionsModal from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal' +import { cancelTransaction } from 'wallet/src/features/transactions/slice' +import { TransactionDetails, TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' +import { getIsCancelable } from 'wallet/src/features/transactions/utils' +import { AccountType } from 'wallet/src/features/wallet/accounts/types' +import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' +import { useAppDispatch } from 'wallet/src/state' +import { openMoonpayTransactionLink, openTransactionLink } from 'wallet/src/utils/linking' + +export const useTransactionActionsCancelModals = ({ + authTrigger, + onNavigateAway, + transaction, +}: { + authTrigger?: AuthTrigger + onNavigateAway?: () => void + transaction: TransactionDetails +}): { + renderModals: () => JSX.Element + openActionsModal: () => void +} => { + const { navigateToTokenDetails } = useWalletNavigation() + + const { type } = useActiveAccountWithThrow() + const readonly = type === AccountType.Readonly + + const [showActionsModal, setShowActionsModal] = useState(false) + const [showCancelModal, setShowCancelModal] = useState(false) + const dispatch = useAppDispatch() + + const { status, addedTime, hash, chainId, typeInfo } = transaction + + const isCancelable = !readonly && getIsCancelable(transaction) + + const handleCancel = (txRequest: providers.TransactionRequest): void => { + if (!transaction) { + return + } + dispatch( + cancelTransaction({ + chainId: transaction.chainId, + id: transaction.id, + address: transaction.from, + cancelRequest: txRequest, + }), + ) + setShowCancelModal(false) + } + + const handleCancelModalClose = (): void => { + setShowCancelModal(false) + } + + const handleActionsModalClose = (): void => { + setShowActionsModal(false) + } + + const handleExplore = (): Promise => { + setShowActionsModal(false) + return openTransactionLink(hash, chainId) + } + + const handleViewMoonpay = (): Promise | undefined => { + if (transaction.typeInfo.type === TransactionType.FiatPurchase) { + setShowActionsModal(false) + return openMoonpayTransactionLink(transaction.typeInfo) + } + return undefined + } + + const handleViewTokenDetails = (currencyId: CurrencyId): void | undefined => { + if (transaction.typeInfo.type === TransactionType.Swap) { + setShowActionsModal(false) + navigateToTokenDetails(currencyId) + onNavigateAway?.() + } + return undefined + } + + const handleCancelConfirmationBack = (): void => { + setShowActionsModal(true) + setShowCancelModal(false) + } + + useEffect(() => { + if (status !== TransactionStatus.Pending) { + setShowCancelModal(false) + } + }, [status]) + + const openActionsModal = (): void => { + setShowActionsModal(true) + } + + const renderModals = (): JSX.Element => ( + <> + {showActionsModal && ( + { + setShowActionsModal(false) + setShowCancelModal(true) + }} + onClose={handleActionsModalClose} + onExplore={handleExplore} + onViewMoonpay={ + typeInfo.type === TransactionType.FiatPurchase && typeInfo.explorerUrl ? handleViewMoonpay : undefined + } + onViewTokenDetails={typeInfo.type === TransactionType.Swap ? handleViewTokenDetails : undefined} + /> + )} + {showCancelModal && ( + + {transaction && ( + + )} + + )} + + ) + + return { + openActionsModal, + renderModals, + } +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/utils.ts b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/utils.ts new file mode 100644 index 00000000000..294af3ea076 --- /dev/null +++ b/packages/wallet/src/features/transactions/SummaryCards/DetailsModal/utils.ts @@ -0,0 +1,40 @@ +import { Currency } from '@uniswap/sdk-core' +import { NumberType } from 'utilities/src/format/types' +import { LocalizationContextState } from 'wallet/src/features/language/LocalizationContext' +import { useUSDCValue } from 'wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice' +import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' + +export function useFormattedCurrencyAmountAndUSDValue({ + currency, + currencyAmountRaw, + formatter, + isApproximateAmount = false, + valueType = ValueType.Raw, +}: { + currency: Maybe + currencyAmountRaw: string | undefined + formatter: LocalizationContextState + isApproximateAmount?: boolean + valueType?: ValueType +}): { amount: string; value: string; tilde: string } { + const currencyAmount = getCurrencyAmount({ + value: currencyAmountRaw, + valueType, + currency, + }) + + const value = useUSDCValue(currencyAmount) + const formattedAmount = formatter.formatCurrencyAmount({ value: currencyAmount }) + return { + tilde: isApproximateAmount ? '~' : '', + amount: `${formattedAmount}`, + value: formatter.formatCurrencyAmount({ value, type: NumberType.FiatTokenPrice }), + } +} + +export function shortenHash(hash: string | undefined, chars: NumberRange<1, 20> = 4): string { + if (!hash) { + return '' + } + return `${hash.substring(0, chars + 2)}...${hash.substring(hash.length - chars)}` +} diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/ApproveSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/ApproveSummaryItem.tsx index 84fe7159fce..91800b39633 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/ApproveSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/ApproveSummaryItem.tsx @@ -1,22 +1,15 @@ import { createElement } from 'react' import { useTranslation } from 'react-i18next' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { NumberType } from 'utilities/src/format/types' import { LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { AssetType } from 'wallet/src/entities/assets' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TXN_HISTORY_ICON_SIZE } from 'wallet/src/features/transactions/SummaryCards/utils' -import { - ApproveTransactionInfo, - TransactionDetails, - TransactionType, -} from 'wallet/src/features/transactions/types' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' +import { ApproveTransactionInfo, TransactionDetails, TransactionType } from 'wallet/src/features/transactions/types' const INFINITE_AMOUNT = 'INF' const ZERO_AMOUNT = '0.0' @@ -29,9 +22,7 @@ export function ApproveSummaryItem({ }): JSX.Element { const { t } = useTranslation() const { formatNumberOrString } = useLocalizationContext() - const currencyInfo = useCurrencyInfo( - buildCurrencyId(transaction.chainId, transaction.typeInfo.tokenAddress) - ) + const currencyInfo = useCurrencyInfo(buildCurrencyId(transaction.chainId, transaction.typeInfo.tokenAddress)) const { approvalAmount } = transaction.typeInfo @@ -39,12 +30,10 @@ export function ApproveSummaryItem({ approvalAmount === INFINITE_AMOUNT ? t('transaction.amount.unlimited') : approvalAmount && approvalAmount !== ZERO_AMOUNT - ? formatNumberOrString({ value: approvalAmount, type: NumberType.TokenNonTx }) - : '' + ? formatNumberOrString({ value: approvalAmount, type: NumberType.TokenNonTx }) + : '' - const caption = `${amount ? amount + ' ' : ''}${ - getSymbolDisplayText(currencyInfo?.currency.symbol) ?? '' - }` + const caption = `${amount ? amount + ' ' : ''}${getSymbolDisplayText(currencyInfo?.currency.symbol) ?? ''}` return createElement(layoutElement as React.FunctionComponent, { caption, diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/CancelConfirmationView.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/CancelConfirmationView.tsx index a39866bb2f4..579b57b93a5 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/CancelConfirmationView.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/CancelConfirmationView.tsx @@ -5,6 +5,7 @@ import { ActivityIndicator } from 'react-native' import { Button, Flex, HapticFeedback, Text, useSporeColors } from 'ui/src' import SlashCircleIcon from 'ui/src/assets/icons/slash-circle.svg' import { ElementName } from 'uniswap/src/features/telemetry/constants' +import { shortenAddress } from 'uniswap/src/utils/addresses' import { NumberType } from 'utilities/src/format/types' import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' import { AuthTrigger } from 'wallet/src/features/auth/types' @@ -12,7 +13,6 @@ import { useCancelationGasFeeInfo, useUSDValue } from 'wallet/src/features/gas/h import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { TransactionDetails, TransactionStatus } from 'wallet/src/features/transactions/types' import { useActiveAccount } from 'wallet/src/features/wallet/hooks' -import { shortenAddress } from 'wallet/src/utils/addresses' export function CancelConfirmationView({ authTrigger, @@ -31,10 +31,7 @@ export function CancelConfirmationView({ const accountAddress = useActiveAccount()?.address const cancelationGasFeeInfo = useCancelationGasFeeInfo(transactionDetails) - const gasFeeUSD = useUSDValue( - transactionDetails.chainId, - cancelationGasFeeInfo?.cancelationGasFee - ) + const gasFeeUSD = useUSDValue(transactionDetails.chainId, cancelationGasFeeInfo?.cancelationGasFee) const gasFee = convertFiatAmountFormatted(gasFeeUSD, NumberType.FiatGasPrice) const onCancelConfirm = useCallback(() => { @@ -58,13 +55,7 @@ export function CancelConfirmationView({ !cancelationGasFeeInfo?.cancelRequest || transactionDetails.status !== TransactionStatus.Pending return ( - + @@ -103,7 +94,8 @@ export function CancelConfirmationView({ testID={ElementName.Cancel} theme="detrimental" width="50%" - onPress={onPressCancel}> + onPress={onPressCancel} + > {t('common.button.confirm')} diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem.tsx index 28a5ca98dec..90d88b4e73e 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/FiatPurchaseSummaryItem.tsx @@ -1,23 +1,17 @@ import React, { createElement } from 'react' import { useTranslation } from 'react-i18next' import { useIsDarkMode } from 'ui/src' +import { getOptionalServiceProviderLogo } from 'uniswap/src/features/fiatOnRamp/utils' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { NumberType } from 'utilities/src/format/types' import { LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { AssetType } from 'wallet/src/entities/assets' -import { getOptionalServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TXN_HISTORY_ICON_SIZE } from 'wallet/src/features/transactions/SummaryCards/utils' -import { - FiatPurchaseTransactionInfo, - TransactionDetails, -} from 'wallet/src/features/transactions/types' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' +import { FiatPurchaseTransactionInfo, TransactionDetails } from 'wallet/src/features/transactions/types' export function FiatPurchaseSummaryItem({ transaction, @@ -43,16 +37,13 @@ export function FiatPurchaseSummaryItem({ const outputCurrencyInfo = useCurrencyInfo( outputCurrency?.metadata.contractAddress ? buildCurrencyId(chainId, outputCurrency?.metadata.contractAddress) - : undefined + : undefined, ) const cryptoSymbol = - outputSymbol ?? - getSymbolDisplayText(outputCurrencyInfo?.currency.symbol) ?? - t('transaction.currency.unknown') + outputSymbol ?? getSymbolDisplayText(outputCurrencyInfo?.currency.symbol) ?? t('transaction.currency.unknown') - const cryptoPurchaseAmount = - formatNumberOrString({ value: outputCurrencyAmount }) + ' ' + cryptoSymbol + const cryptoPurchaseAmount = formatNumberOrString({ value: outputCurrencyAmount }) + ' ' + cryptoSymbol const isDarkMode = useIsDarkMode() const serviceProviderLogoUrl = getOptionalServiceProviderLogo(serviceProviderLogo, isDarkMode) diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTApproveSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTApproveSummaryItem.tsx index 3bc4bac0538..c922722e430 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTApproveSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTApproveSummaryItem.tsx @@ -1,10 +1,6 @@ import { NFTSummaryItem } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/NFTSummaryItem' import { SummaryItemProps } from 'wallet/src/features/transactions/SummaryCards/types' -import { - NFTApproveTransactionInfo, - TransactionDetails, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { NFTApproveTransactionInfo, TransactionDetails, TransactionType } from 'wallet/src/features/transactions/types' export function NFTApproveSummaryItem({ transaction, diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTMintSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTMintSummaryItem.tsx index f9b22df0df7..94b43a674e3 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTMintSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTMintSummaryItem.tsx @@ -1,10 +1,6 @@ import { NFTSummaryItem } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/NFTSummaryItem' import { SummaryItemProps } from 'wallet/src/features/transactions/SummaryCards/types' -import { - NFTMintTransactionInfo, - TransactionDetails, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { NFTMintTransactionInfo, TransactionDetails, TransactionType } from 'wallet/src/features/transactions/types' export function NFTMintSummaryItem({ transaction, @@ -13,10 +9,6 @@ export function NFTMintSummaryItem({ transaction: TransactionDetails & { typeInfo: NFTMintTransactionInfo } }): JSX.Element { return ( - + ) } diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTSummaryItem.tsx index b1bd568cef6..301cf1a31a9 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/NFTSummaryItem.tsx @@ -1,10 +1,7 @@ import { createElement } from 'react' import { LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { AssetType } from 'wallet/src/entities/assets' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TXN_HISTORY_ICON_SIZE } from 'wallet/src/features/transactions/SummaryCards/utils' import { NFTApproveTransactionInfo, diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/OnRampTransferSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/OnRampTransferSummaryItem.tsx index 1897409aaa8..84096fc77eb 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/OnRampTransferSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/OnRampTransferSummaryItem.tsx @@ -1,14 +1,12 @@ import React, { createElement } from 'react' import { useTranslation } from 'react-i18next' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { NumberType } from 'utilities/src/format/types' import { LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { AssetType } from 'wallet/src/entities/assets' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TXN_HISTORY_ICON_SIZE } from 'wallet/src/features/transactions/SummaryCards/utils' import { OnRampPurchaseInfo, @@ -16,7 +14,6 @@ import { TransactionDetails, TransactionType, } from 'wallet/src/features/transactions/types' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' export function OnRampTransferSummaryItem({ transaction, @@ -32,8 +29,7 @@ export function OnRampTransferSummaryItem({ const outputCurrencyInfo = useCurrencyInfo(buildCurrencyId(chainId, destinationTokenAddress)) - const cryptoPurchaseAmount = - formatNumberOrString({ value: destinationTokenAmount }) + ' ' + destinationTokenSymbol + const cryptoPurchaseAmount = formatNumberOrString({ value: destinationTokenAmount }) + ' ' + destinationTokenSymbol const formatFiatTokenPrice = (purchaseInfo: OnRampPurchaseInfo): string => { return formatNumberOrString({ diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/SendSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/SendSummaryItem.tsx index a20581f676a..eeb666da176 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/SendSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/SendSummaryItem.tsx @@ -1,10 +1,6 @@ import { TransferTokenSummaryItem } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransferTokenSummaryItem' import { SummaryItemProps } from 'wallet/src/features/transactions/SummaryCards/types' -import { - SendTokenTransactionInfo, - TransactionDetails, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { SendTokenTransactionInfo, TransactionDetails, TransactionType } from 'wallet/src/features/transactions/types' export function SendSummaryItem({ transaction, diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem.tsx index 9526107f0d8..8d621ae838d 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/SwapSummaryItem.tsx @@ -6,10 +6,7 @@ import { SplitLogo } from 'wallet/src/components/CurrencyLogo/SplitLogo' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { getAmountsFromTrade } from 'wallet/src/features/transactions/getAmountsFromTrade' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TXN_HISTORY_ICON_SIZE } from 'wallet/src/features/transactions/SummaryCards/utils' import { ExactInputSwapTransactionInfo, @@ -48,17 +45,17 @@ export function SwapSummaryItem({ inputCurrencyAmountRaw, formatter, //** isApproximateAmount - input value and confirmed amount are both exact so this should be false **// - isConfirmedSwapTypeInfo(typeInfo) ? false : typeInfo.tradeType === TradeType.EXACT_OUTPUT + isConfirmedSwapTypeInfo(typeInfo) ? false : typeInfo.tradeType === TradeType.EXACT_OUTPUT, ) const otherCurrencyAmount = getFormattedCurrencyAmount( outputCurrency, outputCurrencyAmountRaw, formatter, //** isApproximateAmount - input value and confirmed amount are both exact so this should be false **// - isConfirmedSwapTypeInfo(typeInfo) ? false : typeInfo.tradeType === TradeType.EXACT_INPUT + isConfirmedSwapTypeInfo(typeInfo) ? false : typeInfo.tradeType === TradeType.EXACT_INPUT, ) return `${currencyAmount}${getSymbolDisplayText( - inputCurrency.symbol + inputCurrency.symbol, )} → ${otherCurrencyAmount}${getSymbolDisplayText(outputCurrency.symbol)}` }, [inputCurrencyInfo, outputCurrencyInfo, formatter, typeInfo]) @@ -66,15 +63,14 @@ export function SwapSummaryItem({ const swapFormState = swapCallbacks?.useSwapFormTransactionState( transaction.from, transaction.chainId, - transaction.id + transaction.id, ) const latestSwapTx = swapCallbacks?.useLatestSwapTransaction(transaction.from) const isTheLatestSwap = latestSwapTx && latestSwapTx.id === transaction.id // if this is the latest tx or it was added within the last 15 minutes, show the retry button const shouldShowRetry = - isTheLatestSwap || - (Date.now() - transaction.addedTime < MAX_SHOW_RETRY_TIME && swapCallbacks?.onRetryGenerator) + isTheLatestSwap || (Date.now() - transaction.addedTime < MAX_SHOW_RETRY_TIME && swapCallbacks?.onRetryGenerator) const onRetry = swapCallbacks?.onRetryGenerator?.(swapFormState) diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal.tsx index fbcb574f8b1..336fe8a186b 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal.tsx @@ -2,10 +2,7 @@ import dayjs from 'dayjs' import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { ColorTokens, Flex, Separator, Text, isWeb } from 'ui/src' -import { - ActionSheetModalContent, - MenuItemProp, -} from 'uniswap/src/components/modals/ActionSheetModal' +import { ActionSheetModalContent, MenuItemProp } from 'uniswap/src/components/modals/ActionSheetModal' import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { ElementName, ModalName } from 'uniswap/src/features/telemetry/constants' @@ -14,11 +11,7 @@ import { FORMAT_DATE_LONG, useFormattedDate } from 'wallet/src/features/language import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { - BaseSwapTransactionInfo, - TransactionDetails, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { BaseSwapTransactionInfo, TransactionDetails, TransactionType } from 'wallet/src/features/transactions/types' import { useAppDispatch } from 'wallet/src/state' import { setClipboard } from 'wallet/src/utils/clipboard' import { @@ -32,11 +25,7 @@ function renderOptionItem(label: string, textColorOverride?: ColorTokens): () => return ( <> - + {label} @@ -75,13 +64,9 @@ export default function TransactionActionsModal({ onClose() }, [onClose]) - const inputCurrencyInfo = useCurrencyInfo( - (transactionDetails.typeInfo as BaseSwapTransactionInfo).inputCurrencyId - ) + const inputCurrencyInfo = useCurrencyInfo((transactionDetails.typeInfo as BaseSwapTransactionInfo).inputCurrencyId) - const outputCurrencyInfo = useCurrencyInfo( - (transactionDetails.typeInfo as BaseSwapTransactionInfo).outputCurrencyId - ) + const outputCurrencyInfo = useCurrencyInfo((transactionDetails.typeInfo as BaseSwapTransactionInfo).outputCurrencyId) const options = useMemo(() => { const isSwapTransaction = transactionDetails.typeInfo.type === TransactionType.Swap @@ -95,7 +80,7 @@ export default function TransactionActionsModal({ render: renderOptionItem( t('transaction.action.view', { tokenSymbol: inputCurrencyInfo?.currency.symbol, - }) + }), ), }, { @@ -104,7 +89,7 @@ export default function TransactionActionsModal({ render: renderOptionItem( t('transaction.action.view', { tokenSymbol: outputCurrencyInfo?.currency.symbol, - }) + }), ), }, ] @@ -130,7 +115,7 @@ export default function TransactionActionsModal({ render: renderOptionItem( t('transaction.action.viewEtherscan', { blockExplorerName: chainInfo.explorer.name, - }) + }), ), }, ] @@ -148,7 +133,7 @@ export default function TransactionActionsModal({ pushNotification({ type: AppNotificationType.Copied, copyType: CopyNotificationType.TransactionId, - }) + }), ) handleClose() }, @@ -201,7 +186,8 @@ export default function TransactionActionsModal({ backgroundColor="$transparent" name={ModalName.TransactionActions} onClose={handleClose} - {...(isWeb && { alignment: 'top' })}> + {...(isWeb && { alignment: 'top' })} + > { +export async function openSupportLink(transactionDetails: TransactionDetails): Promise { switch (transactionDetails.typeInfo.type) { case TransactionType.FiatPurchase: - return openLegacyFiatOnRampServiceProviderLink( - transactionDetails.typeInfo.serviceProvider ?? 'MOONPAY' - ) + return openLegacyFiatOnRampServiceProviderLink(transactionDetails.typeInfo.serviceProvider ?? 'MOONPAY') case TransactionType.OnRampPurchase: case TransactionType.OnRampTransfer: return openOnRampSupportLink(transactionDetails.typeInfo.serviceProvider) @@ -231,7 +215,7 @@ async function openSupportLink(transactionDetails: TransactionDetails): Promise< } } -function getTransactionId(transactionDetails: TransactionDetails): string | undefined { +export function getTransactionId(transactionDetails: TransactionDetails): string | undefined { switch (transactionDetails.typeInfo.type) { case TransactionType.FiatPurchase: case TransactionType.OnRampPurchase: diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout.tsx index b3067d53649..be301b7dc5d 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout.tsx @@ -1,20 +1,14 @@ -/* eslint-disable complexity */ -import { providers } from 'ethers' -import { memo, useEffect, useState } from 'react' +import { memo, useState } from 'react' import { useTranslation } from 'react-i18next' import { Flex, SpinningLoader, Text, TouchableArea, isWeb, useSporeColors } from 'ui/src' -import AlertTriangle from 'ui/src/assets/icons/alert-triangle.svg' import SlashCircleIcon from 'ui/src/assets/icons/slash-circle.svg' -import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal' +import { AlertTriangle, UniswapX } from 'ui/src/components/icons' import { FeatureFlags } from 'uniswap/src/features/gating/flags' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' -import { ModalName } from 'uniswap/src/features/telemetry/constants' -import { CurrencyId } from 'uniswap/src/types/currency' import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText' -import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' +import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' import { TransactionDetailsModal } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/TransactionDetailsModal' -import { CancelConfirmationView } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/CancelConfirmationView' -import TransactionActionsModal from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal' +import { useTransactionActionsCancelModals } from 'wallet/src/features/transactions/SummaryCards/DetailsModal/useTransactionActionsCancelModals' import { TransactionSummaryTitle } from 'wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryTitle' import { TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { @@ -24,13 +18,10 @@ import { useFormattedTime, } from 'wallet/src/features/transactions/SummaryCards/utils' import { useIsQueuedTransaction } from 'wallet/src/features/transactions/hooks' -import { cancelTransaction } from 'wallet/src/features/transactions/slice' -import { TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' -import { getIsCancelable } from 'wallet/src/features/transactions/utils' +import { TransactionStatus } from 'wallet/src/features/transactions/types' import { AccountType } from 'wallet/src/features/wallet/accounts/types' import { useActiveAccountWithThrow, useDisplayName } from 'wallet/src/features/wallet/hooks' -import { useAppDispatch } from 'wallet/src/state' -import { openMoonpayTransactionLink, openTransactionLink } from 'wallet/src/utils/linking' +import { openTransactionLink } from 'wallet/src/utils/linking' const LOADING_SPINNER_SIZE = 20 @@ -47,52 +38,27 @@ function TransactionSummaryLayout({ const colors = useSporeColors() const isTransactionDetailsModalEnabled = useFeatureFlag(FeatureFlags.TransactionDetailsSheet) - const { navigateToTokenDetails } = useWalletNavigation() - const { type } = useActiveAccountWithThrow() const readonly = type === AccountType.Readonly - const [showActionsModal, setShowActionsModal] = useState(false) - const [showCancelModal, setShowCancelModal] = useState(false) const [showDetailsModal, setShowDetailsModal] = useState(false) - const dispatch = useAppDispatch() - const { status, addedTime, hash, chainId, typeInfo } = transaction + const { status, hash, chainId } = transaction const walletDisplayName = useDisplayName(transaction.ownerAddress) title = title ?? getTransactionSummaryTitle(transaction, t) ?? '' const inProgress = status === TransactionStatus.Cancelling || status === TransactionStatus.Pending - const inCancelling = - status === TransactionStatus.Canceled || status === TransactionStatus.Cancelling + const isCancel = status === TransactionStatus.Canceled || status === TransactionStatus.Cancelling // Monitor latest nonce to identify queued transactions. const queued = useIsQueuedTransaction(transaction) - const isCancelable = !readonly && getIsCancelable(transaction) - - function handleCancel(txRequest: providers.TransactionRequest): void { - if (!transaction) { - return - } - dispatch( - cancelTransaction({ - chainId: transaction.chainId, - id: transaction.id, - address: transaction.from, - cancelRequest: txRequest, - }) - ) - setShowCancelModal(false) - } - - // Hide cancelation modal if transaction is no longer pending. - useEffect(() => { - if (status !== TransactionStatus.Pending) { - setShowCancelModal(false) - } - }, [status]) + const { openActionsModal, renderModals } = useTransactionActionsCancelModals({ + authTrigger, + transaction, + }) const onPress = async (): Promise => { if (readonly) { @@ -101,7 +67,7 @@ function TransactionSummaryLayout({ if (isTransactionDetailsModalEnabled) { setShowDetailsModal(true) } else { - setShowActionsModal(true) + openActionsModal() } } } @@ -110,7 +76,7 @@ function TransactionSummaryLayout({ const statusIconFill = colors.surface1.get() - const rightBlock = inCancelling ? ( + const rightBlock = isCancel ? ( ) : ( @@ -144,7 +109,8 @@ function TransactionSummaryLayout({ gap="$spacing12" hoverStyle={{ backgroundColor: '$surface2' }} px={isWeb ? '$spacing8' : '$none'} - py="$spacing8"> + py="$spacing8" + > {icon && ( {icon} @@ -160,6 +126,9 @@ function TransactionSummaryLayout({ textProps={{ color: '$accent1', variant: 'body1' }} /> ) : null} + {(transaction.routing === Routing.DUTCH_V2 || transaction.routing === Routing.DUTCH_LIMIT) && ( + + )} {!inProgress && rightBlock} @@ -188,69 +157,14 @@ function TransactionSummaryLayout({ )} - {showActionsModal && ( - { - setShowActionsModal(false) - setShowCancelModal(true) - }} - onClose={(): void => setShowActionsModal(false)} - onExplore={(): Promise => { - setShowActionsModal(false) - return openTransactionLink(hash, chainId) - }} - onViewMoonpay={ - typeInfo.type === TransactionType.FiatPurchase && - // only display `View on Moonpay` when an explorer url was provided by Moonpay - typeInfo.explorerUrl - ? (): Promise | undefined => { - setShowActionsModal(false) - // avoids type casting - return transaction.typeInfo.type === TransactionType.FiatPurchase - ? openMoonpayTransactionLink(transaction.typeInfo) - : undefined - } - : undefined - } - onViewTokenDetails={ - typeInfo.type === TransactionType.Swap - ? (currencyId: CurrencyId): void | undefined => { - setShowActionsModal(false) - if (transaction.typeInfo.type === TransactionType.Swap) { - navigateToTokenDetails(currencyId) - } - } - : undefined - } - /> - )} - {showCancelModal && ( - setShowCancelModal(false)}> - {transaction && ( - { - setShowActionsModal(true) - setShowCancelModal(false) - }} - onCancel={handleCancel} - /> - )} - - )} {showDetailsModal && ( setShowDetailsModal(false)} /> )} + {renderModals()} ) } diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryTitle.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryTitle.tsx index 973ee4b0eaa..c245cf0b159 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryTitle.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryTitle.tsx @@ -9,10 +9,7 @@ interface TransactionSummaryTitleProps { const ICON_SIZE = 14 -export const TransactionSummaryTitle: React.FC = ({ - transaction, - title, -}) => { +export const TransactionSummaryTitle: React.FC = ({ transaction, title }) => { const isDarkMode = useIsDarkMode() const onRampLogo = transaction.typeInfo.type === TransactionType.OnRampPurchase || diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransferTokenSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransferTokenSummaryItem.tsx index 1d4b8b52f4d..13b6e2d709d 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransferTokenSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransferTokenSummaryItem.tsx @@ -3,16 +3,15 @@ import { useTranslation } from 'react-i18next' import { Unitag } from 'ui/src/components/icons' import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks' import { UniverseChainId } from 'uniswap/src/types/chains' +import { shortenAddress } from 'uniswap/src/utils/addresses' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { LogoWithTxStatus } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' import { AssetType } from 'wallet/src/entities/assets' import { useENS } from 'wallet/src/features/ens/useENS' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TXN_HISTORY_ICON_SIZE } from 'wallet/src/features/transactions/SummaryCards/utils' import { ReceiveTokenTransactionInfo, @@ -20,9 +19,7 @@ import { TransactionDetails, TransactionType, } from 'wallet/src/features/transactions/types' -import { shortenAddress } from 'wallet/src/utils/addresses' import { getFormattedCurrencyAmount } from 'wallet/src/utils/currency' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' export function TransferTokenSummaryItem({ transactionType, @@ -42,7 +39,7 @@ export function TransferTokenSummaryItem({ const currencyInfo = useCurrencyInfo( transaction.typeInfo.assetType === AssetType.Currency ? buildCurrencyId(transaction.chainId, transaction.typeInfo.tokenAddress) - : undefined + : undefined, ) const isCurrency = transaction.typeInfo.assetType === AssetType.Currency @@ -50,11 +47,7 @@ export function TransferTokenSummaryItem({ const currencyAmount = currencyInfo && transaction.typeInfo.currencyAmountRaw && - getFormattedCurrencyAmount( - currencyInfo.currency, - transaction.typeInfo.currencyAmountRaw, - formatter - ) + getFormattedCurrencyAmount(currencyInfo.currency, transaction.typeInfo.currencyAmountRaw, formatter) const icon = useMemo(() => { if (isCurrency) { diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/UnknownSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/UnknownSummaryItem.tsx index c567632b6de..7d0467d3237 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/UnknownSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/UnknownSummaryItem.tsx @@ -1,12 +1,11 @@ import { createElement, useMemo } from 'react' import { useSporeColors } from 'ui/src' import { ContractInteraction } from 'ui/src/components/icons' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { iconSizes } from 'ui/src/theme' +import { getValidAddress, shortenAddress } from 'uniswap/src/utils/addresses' +import { DappLogoWithWCBadge } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TransactionDetails, UnknownTransactionInfo } from 'wallet/src/features/transactions/types' -import { getValidAddress, shortenAddress } from 'wallet/src/utils/addresses' export function UnknownSummaryItem({ transaction, @@ -24,7 +23,17 @@ export function UnknownSummaryItem({ return createElement(layoutElement as React.FunctionComponent, { caption, - icon: , + icon: transaction.typeInfo.dappInfo?.icon ? ( + + ) : ( + + ), transaction, }) } diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/WCSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/WCSummaryItem.tsx index 49cb69b4e4e..8a7d57d990d 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/WCSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/WCSummaryItem.tsx @@ -1,9 +1,6 @@ import { createElement } from 'react' import { DappLogoWithWCBadge } from 'wallet/src/components/CurrencyLogo/LogoWithTxStatus' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TXN_HISTORY_ICON_SIZE } from 'wallet/src/features/transactions/SummaryCards/utils' import { TransactionDetails, WCConfirmInfo } from 'wallet/src/features/transactions/types' diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/WrapSummaryItem.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/WrapSummaryItem.tsx index f6e3e2b188a..18d12ca4545 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/WrapSummaryItem.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/WrapSummaryItem.tsx @@ -1,14 +1,8 @@ import { createElement, useMemo } from 'react' import { SplitLogo } from 'wallet/src/components/CurrencyLogo/SplitLogo' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' -import { - useNativeCurrencyInfo, - useWrappedNativeCurrencyInfo, -} from 'wallet/src/features/tokens/useCurrencyInfo' -import { - SummaryItemProps, - TransactionSummaryLayoutProps, -} from 'wallet/src/features/transactions/SummaryCards/types' +import { useNativeCurrencyInfo, useWrappedNativeCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' +import { SummaryItemProps, TransactionSummaryLayoutProps } from 'wallet/src/features/transactions/SummaryCards/types' import { TXN_HISTORY_ICON_SIZE } from 'wallet/src/features/transactions/SummaryCards/utils' import { TransactionDetails, WrapTransactionInfo } from 'wallet/src/features/transactions/types' import { getFormattedCurrencyAmount } from 'wallet/src/utils/currency' @@ -35,24 +29,14 @@ export function WrapSummaryItem({ const { currency: inputCurrency } = inputCurrencyInfo const { currency: outputCurrency } = outputCurrencyInfo - const currencyAmount = getFormattedCurrencyAmount( - inputCurrency, - transaction.typeInfo.currencyAmountRaw, - formatter - ) + const currencyAmount = getFormattedCurrencyAmount(inputCurrency, transaction.typeInfo.currencyAmountRaw, formatter) const otherCurrencyAmount = getFormattedCurrencyAmount( outputCurrency, transaction.typeInfo.currencyAmountRaw, - formatter + formatter, ) return `${currencyAmount}${inputCurrency.symbol} → ${otherCurrencyAmount}${outputCurrency.symbol}` - }, [ - nativeCurrencyInfo, - transaction.typeInfo.currencyAmountRaw, - unwrapped, - wrappedCurrencyInfo, - formatter, - ]) + }, [nativeCurrencyInfo, transaction.typeInfo.currencyAmountRaw, unwrapped, wrappedCurrencyInfo, formatter]) return createElement(layoutElement as React.FunctionComponent, { caption, diff --git a/packages/wallet/src/features/transactions/SummaryCards/types.ts b/packages/wallet/src/features/transactions/SummaryCards/types.ts index 3ea805d7314..d143d203ac9 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/types.ts +++ b/packages/wallet/src/features/transactions/SummaryCards/types.ts @@ -25,7 +25,7 @@ export interface SwapSummaryCallbacks { useSwapFormTransactionState: ( address: Address | undefined, chainId: WalletChainId | undefined, - txId: string | undefined + txId: string | undefined, ) => TransactionState | undefined onRetryGenerator?: (swapFormState: TransactionState | undefined) => () => void } diff --git a/packages/wallet/src/features/transactions/SummaryCards/utils.ts b/packages/wallet/src/features/transactions/SummaryCards/utils.ts index 427cba8b844..420529d028c 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/utils.ts +++ b/packages/wallet/src/features/transactions/SummaryCards/utils.ts @@ -5,12 +5,7 @@ import { AppTFunction } from 'ui/src/i18n/types' import { iconSizes } from 'ui/src/theme' import { ONE_MINUTE_MS } from 'utilities/src/time/time' import { useInterval } from 'utilities/src/time/timing' -import { - LoadingItem, - SectionHeader, - isLoadingItem, - isSectionHeader, -} from 'wallet/src/features/activity/utils' +import { LoadingItem, SectionHeader, isLoadingItem, isSectionHeader } from 'wallet/src/features/activity/utils' import { FORMAT_DATE_MONTH_DAY, FORMAT_TIME_SHORT, @@ -51,9 +46,7 @@ export function generateActivityItemRenderer( loadingItem: JSX.Element, sectionHeaderElement: React.FunctionComponent<{ title: string }>, swapCallbacks: SwapSummaryCallbacks | undefined, - authTrigger: - | ((args: { successCallback: () => void; failureCallback: () => void }) => Promise) - | undefined + authTrigger: ((args: { successCallback: () => void; failureCallback: () => void }) => Promise) | undefined, ): ActivityItemRenderer { return function ActivityItemComponent({ item }: { item: ActivityItem }): JSX.Element { // if it's a loading item, render the loading placeholder @@ -124,7 +117,7 @@ export function generateActivityItemRenderer( */ function getTransactionTypeVerbs( typeInfo: TransactionDetails['typeInfo'], - t: AppTFunction + t: AppTFunction, ): { success: string pending?: string @@ -304,10 +297,7 @@ function getTransactionTypeVerbs( } } -export function getTransactionSummaryTitle( - tx: TransactionDetails, - t: AppTFunction -): string | undefined { +export function getTransactionSummaryTitle(tx: TransactionDetails, t: AppTFunction): string | undefined { const { success, pending, failed, canceling, canceled } = getTransactionTypeVerbs(tx.typeInfo, t) switch (tx.status) { @@ -346,8 +336,8 @@ export function useFormattedTime(time: number): string { // so for the first 30s it would show 0 minutes `${Math.ceil(localizedDayjs().diff(wrappedAddedTime) / ONE_MINUTE_MS)}m` // within an hour : localizedDayjs().isBefore(wrappedAddedTime.add(24, 'hour')) - ? wrappedAddedTime.format(FORMAT_TIME_SHORT) // within last 24 hours - : wrappedAddedTime.format(FORMAT_DATE_MONTH_DAY) // current year + ? wrappedAddedTime.format(FORMAT_TIME_SHORT) // within last 24 hours + : wrappedAddedTime.format(FORMAT_DATE_MONTH_DAY) // current year // eslint-disable-next-line react-hooks/exhaustive-deps }, [time, unixTime, localizedDayjs]) } diff --git a/packages/wallet/src/features/transactions/TransactionDetails/TransactionDetails.tsx b/packages/wallet/src/features/transactions/TransactionDetails/TransactionDetails.tsx index 8c65885edf0..8f5257c2b50 100644 --- a/packages/wallet/src/features/transactions/TransactionDetails/TransactionDetails.tsx +++ b/packages/wallet/src/features/transactions/TransactionDetails/TransactionDetails.tsx @@ -76,7 +76,8 @@ export function TransactionDetails({ borderRadius="$rounded16" gap="$spacing8" px="$spacing16" - py="$spacing8"> + py="$spacing8" + > @@ -87,11 +88,7 @@ export function TransactionDetails({ )} {gasFee.error && ( - + {t('swap.warning.expectedFailure')} )} @@ -105,22 +102,15 @@ export function TransactionDetails({ justifyContent="center" pb="$spacing4" pt="$spacing8" - onPress={onPressToggleShowChildren}> + onPress={onPressToggleShowChildren} + > {showChildren ? t('swap.details.action.less') : t('swap.details.action.more')} {showChildren ? ( - + ) : ( - + )} diff --git a/packages/wallet/src/features/transactions/TransactionHistoryUpdater.test.tsx b/packages/wallet/src/features/transactions/TransactionHistoryUpdater.test.tsx index cfe41557dfc..b60a6e838e8 100644 --- a/packages/wallet/src/features/transactions/TransactionHistoryUpdater.test.tsx +++ b/packages/wallet/src/features/transactions/TransactionHistoryUpdater.test.tsx @@ -4,7 +4,7 @@ import { AssetActivity, TransactionListQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { TransactionHistoryUpdater, getReceiveNotificationFromData, @@ -222,7 +222,7 @@ describe(getReceiveNotificationFromData, () => { // have to check if the calculation is correct in this test. // It's better to test the calculation in a separate test. currencyAmountRaw: expect.any(String), - }) + }), ) }) @@ -233,11 +233,7 @@ describe(getReceiveNotificationFromData, () => { // Ensure all transactions will be "new" compared to this const newTimestamp = 1 - const notification = getReceiveNotificationFromData( - txnDataWithoutReceiveTxns, - account1.address, - newTimestamp - ) + const notification = getReceiveNotificationFromData(txnDataWithoutReceiveTxns, account1.address, newTimestamp) expect(notification).toBeUndefined() }) diff --git a/packages/wallet/src/features/transactions/TransactionHistoryUpdater.tsx b/packages/wallet/src/features/transactions/TransactionHistoryUpdater.tsx index 63d8e88eb9b..b3164076950 100644 --- a/packages/wallet/src/features/transactions/TransactionHistoryUpdater.tsx +++ b/packages/wallet/src/features/transactions/TransactionHistoryUpdater.tsx @@ -3,6 +3,7 @@ import dayjs from 'dayjs' import { useEffect, useMemo } from 'react' import { View } from 'react-native' import { batch } from 'react-redux' +import { PollingInterval } from 'uniswap/src/constants/misc' import { TransactionHistoryUpdaterQueryResult, TransactionListQuery, @@ -11,7 +12,6 @@ import { } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GQLQueries } from 'uniswap/src/data/graphql/uniswap-data-api/queries' import { ONE_SECOND_MS } from 'utilities/src/time/time' -import { PollingInterval } from 'wallet/src/constants/misc' import { buildReceiveNotification } from 'wallet/src/features/notifications/buildReceiveNotification' import { selectLastTxNotificationUpdate } from 'wallet/src/features/notifications/selectors' import { @@ -19,18 +19,11 @@ import { setLastTxNotificationUpdate, setNotificationStatus, } from 'wallet/src/features/notifications/slice' -import { - ReceiveCurrencyTxNotification, - ReceiveNFTNotification, -} from 'wallet/src/features/notifications/types' +import { ReceiveCurrencyTxNotification, ReceiveNFTNotification } from 'wallet/src/features/notifications/types' import { parseDataResponseToTransactionDetails } from 'wallet/src/features/transactions/history/utils' import { useSelectAddressTransactions } from 'wallet/src/features/transactions/selectors' import { TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' -import { - useAccounts, - useActiveAccountAddress, - useHideSpamTokensSetting, -} from 'wallet/src/features/wallet/hooks' +import { useAccounts, useActiveAccountAddress, useHideSpamTokensSetting } from 'wallet/src/features/wallet/hooks' import { selectActiveAccountAddress } from 'wallet/src/features/wallet/selectors' import { useAppDispatch, useAppSelector } from 'wallet/src/state' @@ -68,10 +61,7 @@ export function TransactionHistoryUpdater(): JSX.Element | null { skip: nonActiveAccountAddresses.length === 0, }) - const combinedPortfoliosData = [ - ...(activeAccountData?.portfolios ?? []), - ...(nonActiveAccountData?.portfolios ?? []), - ] + const combinedPortfoliosData = [...(activeAccountData?.portfolios ?? []), ...(nonActiveAccountData?.portfolios ?? [])] if (!combinedPortfoliosData.length) { return null @@ -85,13 +75,8 @@ export function TransactionHistoryUpdater(): JSX.Element | null { } return ( - - + + ) })} @@ -149,9 +134,7 @@ function AddressTransactionHistoryUpdater({ // Dont flag notification status for txns submitted from app, this is handled in transactionWatcherSaga. const confirmedLocally = localTransactions?.some( // eslint-disable-next-line max-nested-callbacks - (localTx) => - activity.details.__typename === 'TransactionDetails' && - localTx.hash === activity.details.hash + (localTx) => activity.details.__typename === 'TransactionDetails' && localTx.hash === activity.details.hash, ) if (!confirmedLocally) { dispatch(setNotificationStatus({ address, hasNotifications: true })) @@ -165,11 +148,7 @@ function AddressTransactionHistoryUpdater({ if (newTransactionsFound) { // Fetch full recent txn history and dispatch receive notification if needed. if (address === activeAccountAddress) { - await fetchAndDispatchReceiveNotification( - address, - lastTxNotificationUpdateTimestamp, - hideSpamTokens - ) + await fetchAndDispatchReceiveNotification(address, lastTxNotificationUpdateTimestamp, hideSpamTokens) } await apolloClient.refetchQueries({ @@ -203,7 +182,7 @@ function AddressTransactionHistoryUpdater({ export function useFetchAndDispatchReceiveNotification(): ( address: string, lastTxNotificationUpdateTimestamp: number | undefined, - hideSpamTokens: boolean + hideSpamTokens: boolean, ) => Promise { const [fetchFullTransactionData] = useTransactionListLazyQuery() const dispatch = useAppDispatch() @@ -211,7 +190,7 @@ export function useFetchAndDispatchReceiveNotification(): ( return async ( address: string, lastTxNotificationUpdateTimestamp: number | undefined, - hideSpamTokens = false + hideSpamTokens = false, ): Promise => { // Fetch full transaction history for user address. const { data: fullTransactionData } = await fetchFullTransactionData({ @@ -223,7 +202,7 @@ export function useFetchAndDispatchReceiveNotification(): ( fullTransactionData, address, lastTxNotificationUpdateTimestamp, - hideSpamTokens + hideSpamTokens, ) if (notification) { @@ -236,7 +215,7 @@ export function getReceiveNotificationFromData( data: TransactionListQuery | undefined, address: Address, lastTxNotificationUpdateTimestamp: number | undefined, - hideSpamTokens = false + hideSpamTokens = false, ): ReceiveCurrencyTxNotification | ReceiveNFTNotification | undefined { if (!data || !lastTxNotificationUpdateTimestamp) { return @@ -254,7 +233,7 @@ export function getReceiveNotificationFromData( tx.addedTime && tx.addedTime >= lastTxNotificationUpdateTimestamp && tx.typeInfo.type === TransactionType.Receive && - tx.status === TransactionStatus.Success + tx.status === TransactionStatus.Success, ) if (!latestReceivedTx) { diff --git a/packages/wallet/src/features/transactions/TransactionRequest/AddressFooter.tsx b/packages/wallet/src/features/transactions/TransactionRequest/AddressFooter.tsx new file mode 100644 index 00000000000..00b5923f8ef --- /dev/null +++ b/packages/wallet/src/features/transactions/TransactionRequest/AddressFooter.tsx @@ -0,0 +1,73 @@ +import { useTranslation } from 'react-i18next' +import { Flex, Text, Tooltip } from 'ui/src' +import { AlertTriangle } from 'ui/src/components/icons' +import { iconSizes } from 'ui/src/theme' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' +import { isMobileApp } from 'utilities/src/platform' +import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' +import { ContentRow } from 'wallet/src/features/transactions/TransactionRequest/ContentRow' + +export function AddressFooter({ + connectedAccountAddress, + activeAccountAddress, +}: { + connectedAccountAddress?: string + activeAccountAddress: string +}): JSX.Element { + const { t } = useTranslation() + + const variant = isMobileApp ? 'body3' : 'body4' + + const currentAccountAddress = connectedAccountAddress || activeAccountAddress + + const showWarning = connectedAccountAddress && !areAddressesEqual(connectedAccountAddress, activeAccountAddress) + + return ( + + + + {t('dapp.request.approve.label')} + + {showWarning && } + + } + > + + + + ) +} + +const TooltipWarning = (): JSX.Element => { + const { t } = useTranslation() + + return ( + + + + + + + {t('dapp.request.warning.notActive.title')} + + {t('dapp.request.warning.notActive.message')} + + + + + ) +} diff --git a/packages/wallet/src/features/transactions/TransactionRequest/ContentRow.tsx b/packages/wallet/src/features/transactions/TransactionRequest/ContentRow.tsx new file mode 100644 index 00000000000..a9bc2863612 --- /dev/null +++ b/packages/wallet/src/features/transactions/TransactionRequest/ContentRow.tsx @@ -0,0 +1,26 @@ +import { PropsWithChildren } from 'react' +import { Flex, Text, TextProps } from 'ui/src' + +export function ContentRow({ + label, + variant = 'body4', + textColor = '$neutral2', + children, +}: PropsWithChildren<{ + label: string | JSX.Element + variant?: TextProps['variant'] + textColor?: TextProps['color'] +}>): JSX.Element { + return ( + + {typeof label === 'string' ? ( + + {label} + + ) : ( + label + )} + {children} + + ) +} diff --git a/packages/wallet/src/features/transactions/TransactionRequest/NetworkFeeFooter.tsx b/packages/wallet/src/features/transactions/TransactionRequest/NetworkFeeFooter.tsx new file mode 100644 index 00000000000..03a504495be --- /dev/null +++ b/packages/wallet/src/features/transactions/TransactionRequest/NetworkFeeFooter.tsx @@ -0,0 +1,34 @@ +import { useTranslation } from 'react-i18next' +import { Flex, Text } from 'ui/src' +import { iconSizes } from 'ui/src/theme' +import { NetworkLogo } from 'uniswap/src/components/CurrencyLogo/NetworkLogo' +import { WalletChainId } from 'uniswap/src/types/chains' +import { NumberType } from 'utilities/src/format/types' +import { isMobileApp } from 'utilities/src/platform' +import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' +import { ContentRow } from 'wallet/src/features/transactions/TransactionRequest/ContentRow' + +interface NetworkFeeFooterProps { + chainId: WalletChainId + showNetworkLogo: boolean + gasFeeUSD: string | undefined +} + +export function NetworkFeeFooter({ chainId, showNetworkLogo, gasFeeUSD }: NetworkFeeFooterProps): JSX.Element | null { + const { t } = useTranslation() + const { convertFiatAmountFormatted } = useLocalizationContext() + const variant = isMobileApp ? 'body3' : 'body4' + + return ( + + + + {showNetworkLogo && } + + {convertFiatAmountFormatted(gasFeeUSD, NumberType.FiatGasPrice)} + + + + + ) +} diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/SpendingDetails.tsx b/packages/wallet/src/features/transactions/TransactionRequest/SpendingDetails.tsx similarity index 73% rename from apps/mobile/src/components/WalletConnect/RequestModal/SpendingDetails.tsx rename to packages/wallet/src/features/transactions/TransactionRequest/SpendingDetails.tsx index 15dfef1a015..a40a37bcdaf 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/SpendingDetails.tsx +++ b/packages/wallet/src/features/transactions/TransactionRequest/SpendingDetails.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { useTranslation } from 'react-i18next' import { Flex, Text } from 'ui/src' import { iconSizes } from 'ui/src/theme' @@ -6,18 +5,16 @@ import { CurrencyLogo } from 'uniswap/src/components/CurrencyLogo/CurrencyLogo' import { WalletChainId } from 'uniswap/src/types/chains' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' import { NumberType } from 'utilities/src/format/types' +import { isMobileApp } from 'utilities/src/platform' import { useUSDValue } from 'wallet/src/features/gas/hooks' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useNativeCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' +import { ContentRow } from 'wallet/src/features/transactions/TransactionRequest/ContentRow' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' -export function SpendingDetails({ - value, - chainId, -}: { - value: string - chainId: WalletChainId -}): JSX.Element { +export function SpendingDetails({ value, chainId }: { value: string; chainId: WalletChainId }): JSX.Element { + const variant = isMobileApp ? 'body3' : 'body4' + const { t } = useTranslation() const { convertFiatAmountFormatted, formatCurrencyAmount } = useLocalizationContext() @@ -38,17 +35,14 @@ export function SpendingDetails({ const fiatAmount = convertFiatAmountFormatted(usdValue, NumberType.FiatTokenPrice) return ( - - - {t('walletConnect.request.details.label.sending')} - + - {tokenAmountWithSymbol} - - {fiatAmount} + {tokenAmountWithSymbol} + + ({fiatAmount}) - + ) } diff --git a/packages/wallet/src/features/transactions/TransactionReview/TransactionReview.tsx b/packages/wallet/src/features/transactions/TransactionReview/TransactionReview.tsx index 62c82cee702..207894b404d 100644 --- a/packages/wallet/src/features/transactions/TransactionReview/TransactionReview.tsx +++ b/packages/wallet/src/features/transactions/TransactionReview/TransactionReview.tsx @@ -84,7 +84,7 @@ export function TransactionReview({ const formattedInputFiatValue = convertFiatAmountFormatted( inputCurrencyUSDValue?.toExact(), - NumberType.FiatTokenQuantity + NumberType.FiatTokenQuantity, ) return ( @@ -96,7 +96,8 @@ export function TransactionReview({ entering={FadeInUp} // TODO(EXT-526): re-enable `exiting` animation when it's fixed. exiting={isWeb ? undefined : FadeOut} - gap="$spacing4"> + gap="$spacing4" + > {currencyInInfo ? ( @@ -138,25 +139,15 @@ export function TransactionReview({ ) : null} - + {recipient ? ( {/* TODO gary to come back and fix this later. More complicated with nested components */} - {t('send.review.summary.to')} + {t('common.text.recipient')} - + @@ -167,7 +158,8 @@ export function TransactionReview({ // TODO(EXT-526): re-enable `exiting` animation when it's fixed. exiting={isWeb ? undefined : FadeOut} gap="$spacing12" - justifyContent="flex-end"> + justifyContent="flex-end" + > {transactionDetails} diff --git a/packages/wallet/src/features/transactions/cancelTransactionSaga.ts b/packages/wallet/src/features/transactions/cancelTransactionSaga.ts index 2d019babdb4..2fcfa7102bd 100644 --- a/packages/wallet/src/features/transactions/cancelTransactionSaga.ts +++ b/packages/wallet/src/features/transactions/cancelTransactionSaga.ts @@ -10,9 +10,7 @@ const selectTransaction = makeSelectTransaction() // Inspiration: https://github.com/MetaMask/metamask-extension/blob/develop/app/scripts/controllers/transactions/index.js#L744 export function* attemptCancelTransaction(transaction: ClassicTransactionDetails) { const { from, chainId, id } = transaction - const tx = yield* select((state) => - selectTransaction(state, { address: from, chainId, txId: id }) - ) + const tx = yield* select((state) => selectTransaction(state, { address: from, chainId, txId: id })) if (!tx?.cancelRequest) { throw new Error('attempted to cancel a transaction without cancelRequest set') } diff --git a/packages/wallet/src/features/transactions/contexts/SwapFormContext.tsx b/packages/wallet/src/features/transactions/contexts/SwapFormContext.tsx index bb7bc1d36c2..e3dfe8a5e39 100644 --- a/packages/wallet/src/features/transactions/contexts/SwapFormContext.tsx +++ b/packages/wallet/src/features/transactions/contexts/SwapFormContext.tsx @@ -9,15 +9,12 @@ import { useRef, useState, } from 'react' +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' import { AssetType, TradeableAsset } from 'wallet/src/entities/assets' import { useSwapAnalytics } from 'wallet/src/features/transactions/swap/analytics' import { useDerivedSwapInfo } from 'wallet/src/features/transactions/swap/trade/hooks/useDerivedSwapInfo' -import { - CurrencyField, - TradeProtocolPreference, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TradeProtocolPreference } from 'wallet/src/features/transactions/transactionState/types' export type SwapFormState = { customSlippageTolerance?: number @@ -95,7 +92,7 @@ export function SwapFormContextProvider({ setSwapForm((prevState) => ({ ...prevState, ...newState })) }, - [setSwapForm] + [setSwapForm], ) const derivedSwapInfo = useDerivedSwapInfo({ @@ -149,7 +146,7 @@ export function SwapFormContextProvider({ swapForm.txId, derivedSwapInfo, updateSwapForm, - ] + ], ) return {children} diff --git a/packages/wallet/src/features/transactions/contexts/SwapScreenContext.tsx b/packages/wallet/src/features/transactions/contexts/SwapScreenContext.tsx index 36fd4f8f343..7176a1a1118 100644 --- a/packages/wallet/src/features/transactions/contexts/SwapScreenContext.tsx +++ b/packages/wallet/src/features/transactions/contexts/SwapScreenContext.tsx @@ -29,7 +29,7 @@ export function SwapScreenContextProvider({ children }: { children: ReactNode }) screenRef, setScreen: wrappedSetScreen, }), - [screen, wrappedSetScreen] + [screen, wrappedSetScreen], ) return {children} diff --git a/packages/wallet/src/features/transactions/contexts/SwapTxContext.tsx b/packages/wallet/src/features/transactions/contexts/SwapTxContext.tsx index 2c60297e08f..c0889777b49 100644 --- a/packages/wallet/src/features/transactions/contexts/SwapTxContext.tsx +++ b/packages/wallet/src/features/transactions/contexts/SwapTxContext.tsx @@ -12,11 +12,7 @@ type SwapTxContextState = { export const SwapTxContext = createContext(undefined) // Same as above, with different hook for data fetching. -export function SwapTxContextProviderTradingApi({ - children, -}: { - children: ReactNode -}): JSX.Element { +export function SwapTxContextProviderTradingApi({ children }: { children: ReactNode }): JSX.Element { const { derivedSwapInfo } = useSwapFormContext() const { txRequest, approveTxRequest, gasFee, approvalError } = useSwapTxAndGasInfoTradingApi({ @@ -30,7 +26,7 @@ export function SwapTxContextProviderTradingApi({ gasFee, approvalError, }), - [approvalError, approveTxRequest, gasFee, txRequest] + [approvalError, approveTxRequest, gasFee, txRequest], ) return {children} diff --git a/packages/wallet/src/features/transactions/contexts/TransactionModalContext.tsx b/packages/wallet/src/features/transactions/contexts/TransactionModalContext.tsx index e90706d7b22..a1b00e2940e 100644 --- a/packages/wallet/src/features/transactions/contexts/TransactionModalContext.tsx +++ b/packages/wallet/src/features/transactions/contexts/TransactionModalContext.tsx @@ -11,9 +11,7 @@ export type TransactionModalContextState = { authTrigger?: AuthTrigger } -export const TransactionModalContext = createContext( - undefined -) +export const TransactionModalContext = createContext(undefined) export function TransactionModalContextProvider({ children, @@ -41,28 +39,17 @@ export function TransactionModalContextProvider({ openWalletRestoreModal, walletNeedsRestore, }), - [ - BiometricsIcon, - authTrigger, - bottomSheetViewStyles, - onClose, - openWalletRestoreModal, - walletNeedsRestore, - ] + [BiometricsIcon, authTrigger, bottomSheetViewStyles, onClose, openWalletRestoreModal, walletNeedsRestore], ) - return ( - {children} - ) + return {children} } export const useTransactionModalContext = (): TransactionModalContextState => { const context = useContext(TransactionModalContext) if (context === undefined) { - throw new Error( - '`useTransactionModalContext` must be used inside of `TransactionModalContextProvider`' - ) + throw new Error('`useTransactionModalContext` must be used inside of `TransactionModalContextProvider`') } return context diff --git a/packages/wallet/src/features/transactions/getAmountsFromTrade.ts b/packages/wallet/src/features/transactions/getAmountsFromTrade.ts index eade100401c..3b20d7f482c 100644 --- a/packages/wallet/src/features/transactions/getAmountsFromTrade.ts +++ b/packages/wallet/src/features/transactions/getAmountsFromTrade.ts @@ -7,10 +7,7 @@ import { } from 'wallet/src/features/transactions/types' export function getAmountsFromTrade( - typeInfo: - | ExactInputSwapTransactionInfo - | ExactOutputSwapTransactionInfo - | ConfirmedSwapTransactionInfo + typeInfo: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo | ConfirmedSwapTransactionInfo, ): { inputCurrencyAmountRaw: string; outputCurrencyAmountRaw: string } { if (isConfirmedSwapTypeInfo(typeInfo)) { const { inputCurrencyAmountRaw, outputCurrencyAmountRaw } = typeInfo diff --git a/packages/wallet/src/features/transactions/history/conversion/conversion.test.ts b/packages/wallet/src/features/transactions/history/conversion/conversion.test.ts index be123df48fe..79e82606808 100644 --- a/packages/wallet/src/features/transactions/history/conversion/conversion.test.ts +++ b/packages/wallet/src/features/transactions/history/conversion/conversion.test.ts @@ -1,4 +1,5 @@ /* eslint-disable max-lines */ +import { getNativeAddress, getWrappedNativeAddress } from 'uniswap/src/constants/addresses' import { Chain, Currency, @@ -9,7 +10,6 @@ import { TransactionStatus, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getNativeAddress, getWrappedNativeAddress } from 'wallet/src/constants/addresses' import { DAI } from 'wallet/src/constants/tokens' import { extractOnRampTransactionDetails } from 'wallet/src/features/transactions/history/conversion/extractFiatOnRampTransactionDetails' import extractTransactionDetails from 'wallet/src/features/transactions/history/conversion/extractTransactionDetails' @@ -243,6 +243,7 @@ describe(parseNFTMintTransaction, () => { collectionName: 'collection_name', imageURL: 'image_url', tokenId: 'token_id', + address: 'nft_contract_address', }, purchaseCurrencyId: `1-${ERC20_ASSET_ADDRESS}`, purchaseCurrencyAmountRaw: '1000000000000000000', @@ -258,6 +259,7 @@ describe(parseNFTMintTransaction, () => { collectionName: 'collection_name', imageURL: 'image_url', tokenId: 'token_id', + address: 'nft_contract_address', }, purchaseCurrencyId: `1-${getNativeAddress(UniverseChainId.Mainnet)}`, purchaseCurrencyAmountRaw: '1000000000000000000', @@ -343,6 +345,7 @@ describe(parseReceiveTransaction, () => { collectionName: 'collection_name', imageURL: 'image_url', tokenId: 'token_id', + address: 'nft_contract_address', }, }) }) @@ -395,6 +398,7 @@ describe(parseSendTransaction, () => { collectionName: 'collection_name', imageURL: 'image_url', tokenId: 'token_id', + address: 'nft_contract_address', }, }) }) @@ -503,6 +507,7 @@ describe(parseTradeTransaction, () => { collectionName: 'collection_name', imageURL: 'image_url', tokenId: 'asset_name', + address: 'nft_contract_address', }, purchaseCurrencyId: `1-${ERC20_ASSET_ADDRESS}`, purchaseCurrencyAmountRaw: '1000000000000000000', @@ -518,6 +523,7 @@ describe(parseTradeTransaction, () => { collectionName: 'collection_name', imageURL: 'image_url', tokenId: 'asset_name', + address: 'nft_contract_address', }, purchaseCurrencyId: `1-${ERC20_ASSET_ADDRESS}`, purchaseCurrencyAmountRaw: '1000000000000000000', diff --git a/packages/wallet/src/features/transactions/history/conversion/extractFiatOnRampTransactionDetails.ts b/packages/wallet/src/features/transactions/history/conversion/extractFiatOnRampTransactionDetails.ts index b09d76aad11..522031446ae 100644 --- a/packages/wallet/src/features/transactions/history/conversion/extractFiatOnRampTransactionDetails.ts +++ b/packages/wallet/src/features/transactions/history/conversion/extractFiatOnRampTransactionDetails.ts @@ -1,22 +1,23 @@ import { TransactionType as RemoteTransactionType } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain, toSupportedChainId } from 'uniswap/src/features/chains/utils' import { FORTransaction } from 'uniswap/src/features/fiatOnRamp/types' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' import { logger } from 'utilities/src/logger/logger' import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' -import { fromGraphQLChain, toSupportedChainId } from 'wallet/src/features/chains/utils' import { FiatOnRampTransactionDetails } from 'wallet/src/features/fiatOnRamp/types' import parseOnRampTransaction from 'wallet/src/features/transactions/history/conversion/parseOnRampTransaction' import { remoteTxStatusToLocalTxStatus } from 'wallet/src/features/transactions/history/utils' import { FiatPurchaseTransactionInfo, TransactionDetails, + TransactionDetailsType, TransactionListQueryResponse, TransactionStatus, TransactionType, } from 'wallet/src/features/transactions/types' function parseFiatPurchaseTransaction( - transaction: FORTransaction + transaction: FORTransaction, ): FiatPurchaseTransactionInfo & { chainId: WalletChainId } { const { sourceAmount: inputCurrencyAmount, @@ -60,7 +61,7 @@ function statusToTransactionInfoStatus(status: FORTransaction['status']): Transa } export function extractFiatOnRampTransactionDetails( - transaction: FORTransaction + transaction: FORTransaction, ): FiatOnRampTransactionDetails | undefined { try { const { chainId, ...typeInfo } = parseFiatPurchaseTransaction(transaction) ?? { @@ -89,10 +90,8 @@ export function extractFiatOnRampTransactionDetails( } } -export function extractOnRampTransactionDetails( - transaction: TransactionListQueryResponse -): TransactionDetails | null { - if (transaction?.details.__typename !== 'OnRampTransactionDetails') { +export function extractOnRampTransactionDetails(transaction: TransactionListQueryResponse): TransactionDetails | null { + if (transaction?.details.__typename !== TransactionDetailsType.OnRamp) { return null } diff --git a/packages/wallet/src/features/transactions/history/conversion/extractMoonpayTransactionDetails.ts b/packages/wallet/src/features/transactions/history/conversion/extractMoonpayTransactionDetails.ts index 367950bffaa..7d8dd214612 100644 --- a/packages/wallet/src/features/transactions/history/conversion/extractMoonpayTransactionDetails.ts +++ b/packages/wallet/src/features/transactions/history/conversion/extractMoonpayTransactionDetails.ts @@ -1,22 +1,15 @@ +import { getNativeAddress } from 'uniswap/src/constants/addresses' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { WalletChainId } from 'uniswap/src/types/chains' import { logger } from 'utilities/src/logger/logger' -import { getNativeAddress } from 'wallet/src/constants/addresses' import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' -import { - FiatOnRampTransactionDetails, - MoonpayTransactionsResponse, -} from 'wallet/src/features/fiatOnRamp/types' -import { - FiatPurchaseTransactionInfo, - TransactionStatus, - TransactionType, -} from 'wallet/src/features/transactions/types' +import { FiatOnRampTransactionDetails, MoonpayTransactionsResponse } from 'wallet/src/features/fiatOnRamp/types' +import { FiatPurchaseTransactionInfo, TransactionStatus, TransactionType } from 'wallet/src/features/transactions/types' const MOONPAY_ETH_CONTRACT_ADDRESS = '0x0000000000000000000000000000000000000000' function parseFiatPurchaseTransaction( - transaction: Partial + transaction: Partial, ): FiatPurchaseTransactionInfo & { chainId: WalletChainId } { const { currency: outputCurrency, @@ -70,9 +63,7 @@ function parseFiatPurchaseTransaction( } } -function moonpayStatusToTransactionInfoStatus( - status: MoonpayTransactionsResponse[0]['status'] -): TransactionStatus { +function moonpayStatusToTransactionInfoStatus(status: MoonpayTransactionsResponse[0]['status']): TransactionStatus { switch (status) { case 'failed': return TransactionStatus.Failed @@ -89,10 +80,7 @@ function moonpayStatusToTransactionInfoStatus( // MoonPay does not always (ever?) return the transaction id inside `returnUrl` // returnUrl": "https://buy-sandbox.moonpay.com/transaction_receipt // This adds `transactionId` param if required -function formatReturnUrl( - providedReturnUrl: string | undefined, - id: string | undefined -): string | undefined { +function formatReturnUrl(providedReturnUrl: string | undefined, id: string | undefined): string | undefined { if (!providedReturnUrl || !id) { return } @@ -106,7 +94,7 @@ function formatReturnUrl( } export function extractMoonpayTransactionDetails( - transaction?: MoonpayTransactionsResponse[0] + transaction?: MoonpayTransactionsResponse[0], ): FiatOnRampTransactionDetails | undefined { if (!transaction) { return diff --git a/packages/wallet/src/features/transactions/history/conversion/extractTransactionDetails.ts b/packages/wallet/src/features/transactions/history/conversion/extractTransactionDetails.ts index 1db93e81017..ccd4156f76d 100644 --- a/packages/wallet/src/features/transactions/history/conversion/extractTransactionDetails.ts +++ b/packages/wallet/src/features/transactions/history/conversion/extractTransactionDetails.ts @@ -1,8 +1,9 @@ +import { DEFAULT_NATIVE_ADDRESS } from 'uniswap/src/constants/chains' import { TransactionType as RemoteTransactionType } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { UniverseChainId } from 'uniswap/src/types/chains' import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' import { SpamCode } from 'wallet/src/data/types' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import parseApproveTransaction from 'wallet/src/features/transactions/history/conversion/parseApproveTransaction' import parseNFTMintTransaction from 'wallet/src/features/transactions/history/conversion/parseMintTransaction' import parseOnRampTransaction from 'wallet/src/features/transactions/history/conversion/parseOnRampTransaction' @@ -12,6 +13,7 @@ import parseTradeTransaction from 'wallet/src/features/transactions/history/conv import { remoteTxStatusToLocalTxStatus } from 'wallet/src/features/transactions/history/utils' import { TransactionDetails, + TransactionDetailsType, TransactionListQueryResponse, TransactionType, TransactionTypeInfo, @@ -25,9 +27,9 @@ import { * @returns Formatted TransactionDetails object. */ export default function extractTransactionDetails( - transaction: TransactionListQueryResponse + transaction: TransactionListQueryResponse, ): TransactionDetails | null { - if (transaction?.details.__typename !== 'TransactionDetails') { + if (transaction?.details.__typename !== TransactionDetailsType.Transaction) { return null } @@ -43,6 +45,7 @@ export default function extractTransactionDetails( typeInfo = parseReceiveTransaction(transaction) break case RemoteTransactionType.Swap: + case RemoteTransactionType.SwapOrder: typeInfo = parseTradeTransaction(transaction) break case RemoteTransactionType.Mint: @@ -67,19 +70,37 @@ export default function extractTransactionDetails( return false } }) ?? true + + const dappInfo = transaction.details.application?.address + ? { + name: transaction.details.application?.name, + address: transaction.details.application?.address, + icon: transaction.details.application?.icon?.url, + } + : undefined typeInfo = { type: TransactionType.Unknown, tokenAddress: transaction.details.to, isSpam, + dappInfo, } } const chainId = fromGraphQLChain(transaction.chain) + const networkFee = + chainId && transaction.details.networkFee?.quantity && transaction.details.networkFee?.tokenSymbol + ? { + quantity: transaction.details.networkFee.quantity, + tokenSymbol: transaction.details.networkFee.tokenSymbol, + // graphQL returns a null token address for native tokens like ETH + tokenAddress: transaction.details.networkFee.tokenAddress ?? DEFAULT_NATIVE_ADDRESS, + chainId, + } + : undefined + return { - routing: - // TODO(MOB-3535): Update query w/ flag to return SwapOrderDetails & set routing to DUTCH_V2 if details is of type SwapOrderDetails - Routing.CLASSIC, + routing: transaction.details.type === RemoteTransactionType.SwapOrder ? Routing.DUTCH_V2 : Routing.CLASSIC, id: transaction.details.hash, // fallback to mainnet, although this should never happen chainId: chainId ?? UniverseChainId.Mainnet, @@ -89,5 +110,6 @@ export default function extractTransactionDetails( from: transaction.details.from, typeInfo, options: { request: {} }, // Empty request is okay, gate re-submissions on txn type and status. + networkFee, } } diff --git a/packages/wallet/src/features/transactions/history/conversion/extractUniswapXOrderDetails.ts b/packages/wallet/src/features/transactions/history/conversion/extractUniswapXOrderDetails.ts new file mode 100644 index 00000000000..42d6ea039a3 --- /dev/null +++ b/packages/wallet/src/features/transactions/history/conversion/extractUniswapXOrderDetails.ts @@ -0,0 +1,108 @@ +import { + SwapOrderStatus, + SwapOrderType, + TokenStandard, +} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' +import { UniverseChainId } from 'uniswap/src/types/chains' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' +import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' +import { deriveCurrencyAmountFromAssetResponse } from 'wallet/src/features/transactions/history/utils' +import { + ConfirmedSwapTransactionInfo, + TransactionDetails, + TransactionDetailsType, + TransactionListQueryResponse, + TransactionStatus, + TransactionType, +} from 'wallet/src/features/transactions/types' + +export function extractUniswapXOrderDetails(transaction: TransactionListQueryResponse): TransactionDetails | null { + if (transaction?.details.__typename !== TransactionDetailsType.UniswapXOrder) { + return null + } + + const typeInfo = parseUniswapXOrderTransaction(transaction) + const routing = transaction.details.swapOrderType === SwapOrderType.Limit ? Routing.DUTCH_LIMIT : Routing.DUTCH_V2 + + // TODO (MOB-3609): Parse and show pending limit orders in Activity feed + if (!typeInfo || transaction.details.swapOrderType === SwapOrderType.Limit) { + return null + } + + return { + routing, + id: transaction.details.id, + chainId: fromGraphQLChain(transaction.chain) ?? UniverseChainId.Mainnet, + addedTime: transaction.timestamp * 1000, // convert to ms, + status: remoteOrderStatusToLocalTxStatus(transaction.details.orderStatus), + from: transaction.details.offerer, // This transaction is not on-chain, so use the offerer address as the from address + typeInfo, + } +} + +function remoteOrderStatusToLocalTxStatus(orderStatus: SwapOrderStatus): TransactionStatus { + switch (orderStatus) { + case SwapOrderStatus.Open: + return TransactionStatus.Pending + case SwapOrderStatus.Expired: + return TransactionStatus.Expired + case SwapOrderStatus.Error: + return TransactionStatus.Failed + case SwapOrderStatus.InsufficientFunds: + return TransactionStatus.InsufficientFunds + case SwapOrderStatus.Filled: + return TransactionStatus.Success + case SwapOrderStatus.Cancelled: + return TransactionStatus.Canceled + } +} + +export default function parseUniswapXOrderTransaction( + transaction: NonNullable, +): ConfirmedSwapTransactionInfo | null { + if (transaction?.details?.__typename !== TransactionDetailsType.UniswapXOrder) { + return null + } + + const chainId = fromGraphQLChain(transaction.chain) + if (!chainId) { + return null + } + + // Token swap + const inputCurrencyId = transaction.details.inputToken.address + ? buildCurrencyId(chainId, transaction.details.inputToken.address) + : null + const outputCurrencyId = transaction.details.outputToken.address + ? buildCurrencyId(chainId, transaction.details.outputToken.address) + : null + + const inputCurrencyAmountRaw = deriveCurrencyAmountFromAssetResponse( + TokenStandard.Erc20, + transaction.chain, + transaction.details.inputToken.address, + transaction.details.inputToken.decimals, + transaction.details.inputTokenQuantity, + ) + + const outputCurrencyAmountRaw = deriveCurrencyAmountFromAssetResponse( + TokenStandard.Erc20, + transaction.chain, + transaction.details.outputToken.address, + transaction.details.outputToken.decimals, + transaction.details.outputTokenQuantity, + ) + + if (!inputCurrencyId || !outputCurrencyId) { + return null + } + + return { + type: TransactionType.Swap, + inputCurrencyId, + outputCurrencyId, + inputCurrencyAmountRaw, + outputCurrencyAmountRaw, + } +} diff --git a/packages/wallet/src/features/transactions/history/conversion/parseApproveTransaction.ts b/packages/wallet/src/features/transactions/history/conversion/parseApproveTransaction.ts index 322c5228541..38a85edb682 100644 --- a/packages/wallet/src/features/transactions/history/conversion/parseApproveTransaction.ts +++ b/packages/wallet/src/features/transactions/history/conversion/parseApproveTransaction.ts @@ -1,14 +1,15 @@ import { ApproveTransactionInfo, NFTApproveTransactionInfo, + TransactionDetailsType, TransactionListQueryResponse, TransactionType, } from 'wallet/src/features/transactions/types' export default function parseApproveTransaction( - transaction: NonNullable + transaction: NonNullable, ): ApproveTransactionInfo | NFTApproveTransactionInfo | undefined { - if (transaction.details.__typename !== 'TransactionDetails') { + if (transaction.details.__typename !== TransactionDetailsType.Transaction) { return undefined } @@ -24,11 +25,20 @@ export default function parseApproveTransaction( if (!(tokenAddress && spender)) { return undefined } + + const dappInfo = transaction.details.application?.address + ? { + name: transaction.details.application?.name, + address: transaction.details.application.address, + icon: transaction.details.application?.icon?.url, + } + : undefined return { type: TransactionType.Approve, tokenAddress, spender, approvalAmount, + dappInfo, } } return undefined diff --git a/packages/wallet/src/features/transactions/history/conversion/parseMintTransaction.ts b/packages/wallet/src/features/transactions/history/conversion/parseMintTransaction.ts index 21d9f8810b6..efb0fb02d6b 100644 --- a/packages/wallet/src/features/transactions/history/conversion/parseMintTransaction.ts +++ b/packages/wallet/src/features/transactions/history/conversion/parseMintTransaction.ts @@ -1,28 +1,25 @@ -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' +import { buildCurrencyId, buildNativeCurrencyId } from 'uniswap/src/utils/currencyId' import { deriveCurrencyAmountFromAssetResponse, parseUSDValueFromAssetChange, } from 'wallet/src/features/transactions/history/utils' import { NFTMintTransactionInfo, + TransactionDetailsType, TransactionListQueryResponse, TransactionType, } from 'wallet/src/features/transactions/types' -import { buildCurrencyId, buildNativeCurrencyId } from 'wallet/src/utils/currencyId' export default function parseNFTMintTransaction( - transaction: NonNullable + transaction: NonNullable, ): NFTMintTransactionInfo | undefined { - if (transaction.details.__typename !== 'TransactionDetails') { + if (transaction.details.__typename !== TransactionDetailsType.Transaction) { return undefined } - const tokenChange = transaction.details.assetChanges?.find( - (change) => change?.__typename === 'TokenTransfer' - ) - const nftChange = transaction.details.assetChanges?.find( - (change) => change?.__typename === 'NftTransfer' - ) + const tokenChange = transaction.details.assetChanges?.find((change) => change?.__typename === 'TokenTransfer') + const nftChange = transaction.details.assetChanges?.find((change) => change?.__typename === 'NftTransfer') // Mints must include the NFT minted if (!nftChange || nftChange.__typename !== 'NftTransfer') { @@ -35,10 +32,11 @@ export default function parseNFTMintTransaction( const tokenId = nftChange.asset.tokenId const chainId = fromGraphQLChain(transaction.chain) const isSpam = nftChange.asset?.isSpam ?? false + const address = nftChange.asset.nftContract?.address let transactedUSDValue: number | undefined - if (!name || !collectionName || !imageURL || !tokenId || !chainId) { + if (!name || !collectionName || !imageURL || !tokenId || !chainId || !address) { return undefined } @@ -49,19 +47,26 @@ export default function parseNFTMintTransaction( tokenChange.tokenStandard === 'NATIVE' ? buildNativeCurrencyId(chainId) : tokenChange.asset?.address - ? buildCurrencyId(chainId, tokenChange.asset.address) - : undefined + ? buildCurrencyId(chainId, tokenChange.asset.address) + : undefined purchaseCurrencyAmountRaw = deriveCurrencyAmountFromAssetResponse( tokenChange.tokenStandard, tokenChange.asset.chain, tokenChange.asset.address, tokenChange.asset.decimals, - tokenChange.quantity + tokenChange.quantity, ) transactedUSDValue = parseUSDValueFromAssetChange(tokenChange.transactedValue) } + const dappInfo = transaction.details.application?.address + ? { + name: transaction.details.application?.name, + address: transaction.details.application.address, + icon: transaction.details.application?.icon?.url, + } + : undefined return { type: TransactionType.NFTMint, nftSummaryInfo: { @@ -69,10 +74,12 @@ export default function parseNFTMintTransaction( collectionName, imageURL, tokenId, + address, }, purchaseCurrencyId, purchaseCurrencyAmountRaw, transactedUSDValue, isSpam, + dappInfo, } } diff --git a/packages/wallet/src/features/transactions/history/conversion/parseOnRampTransaction.ts b/packages/wallet/src/features/transactions/history/conversion/parseOnRampTransaction.ts index e9f17d4f569..07818ea9e10 100644 --- a/packages/wallet/src/features/transactions/history/conversion/parseOnRampTransaction.ts +++ b/packages/wallet/src/features/transactions/history/conversion/parseOnRampTransaction.ts @@ -1,20 +1,21 @@ -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { getAddressFromAsset } from 'wallet/src/features/transactions/history/utils' import { OnRampPurchaseInfo, OnRampTransactionInfo, OnRampTransferInfo, + TransactionDetailsType, TransactionListQueryResponse, TransactionType, } from 'wallet/src/features/transactions/types' export default function parseOnRampTransaction( - transaction: NonNullable + transaction: NonNullable, ): OnRampPurchaseInfo | OnRampTransferInfo | undefined { let change - if (transaction.details.__typename === 'TransactionDetails') { + if (transaction.details.__typename === TransactionDetailsType.Transaction) { change = transaction.details.assetChanges?.[0] - } else if (transaction.details.__typename === 'OnRampTransactionDetails') { + } else if (transaction.details.__typename === TransactionDetailsType.OnRamp) { change = transaction.details.onRampTransfer } else { return undefined diff --git a/packages/wallet/src/features/transactions/history/conversion/parseReceiveTransaction.ts b/packages/wallet/src/features/transactions/history/conversion/parseReceiveTransaction.ts index 5442756127a..09d5d8fb9c6 100644 --- a/packages/wallet/src/features/transactions/history/conversion/parseReceiveTransaction.ts +++ b/packages/wallet/src/features/transactions/history/conversion/parseReceiveTransaction.ts @@ -1,6 +1,7 @@ +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { SpamCode } from 'wallet/src/data/types' import { AssetType } from 'wallet/src/entities/assets' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { deriveCurrencyAmountFromAssetResponse, getAddressFromAsset, @@ -9,10 +10,10 @@ import { import { FiatPurchaseTransactionInfo, ReceiveTokenTransactionInfo, + TransactionDetailsType, TransactionListQueryResponse, TransactionType, } from 'wallet/src/features/transactions/types' -import { areAddressesEqual } from 'wallet/src/utils/addresses' // Non-exhaustive list of addresses Moonpay uses when sending purchased tokens const MOONPAY_SENDER_ADDRESSES = [ @@ -23,9 +24,9 @@ const MOONPAY_SENDER_ADDRESSES = [ ] export default function parseReceiveTransaction( - transaction: NonNullable + transaction: NonNullable, ): ReceiveTokenTransactionInfo | FiatPurchaseTransactionInfo | undefined { - if (transaction.details.__typename !== 'TransactionDetails') { + if (transaction.details.__typename !== TransactionDetailsType.Transaction) { return undefined } @@ -60,6 +61,7 @@ export default function parseReceiveTransaction( collectionName, imageURL, tokenId, + address: tokenAddress, }, isSpam, } @@ -69,9 +71,7 @@ export default function parseReceiveTransaction( // Found ERC20 transfer if (change.__typename === 'TokenTransfer') { const sender = change.sender - const isMoonpayPurchase = MOONPAY_SENDER_ADDRESSES.some((address) => - areAddressesEqual(address, sender) - ) + const isMoonpayPurchase = MOONPAY_SENDER_ADDRESSES.some((address) => areAddressesEqual(address, sender)) const tokenAddress = getAddressFromAsset({ chain: change.asset.chain, @@ -84,15 +84,13 @@ export default function parseReceiveTransaction( change.asset.chain, change.asset.address, change.asset.decimals, - change.quantity + change.quantity, ) const transactedUSDValue = parseUSDValueFromAssetChange(change.transactedValue) // Filter out receive transactions with tokens that are either marked `isSpam` or with spam code 2 (token with URL name) - const isSpam = Boolean( - change.asset.project?.isSpam || change.asset.project?.spamCode === SpamCode.HIGH - ) + const isSpam = Boolean(change.asset.project?.isSpam || change.asset.project?.spamCode === SpamCode.HIGH) if (!(sender && tokenAddress)) { return undefined diff --git a/packages/wallet/src/features/transactions/history/conversion/parseSendTransaction.ts b/packages/wallet/src/features/transactions/history/conversion/parseSendTransaction.ts index 83db1a02500..2aa21119271 100644 --- a/packages/wallet/src/features/transactions/history/conversion/parseSendTransaction.ts +++ b/packages/wallet/src/features/transactions/history/conversion/parseSendTransaction.ts @@ -7,18 +7,28 @@ import { } from 'wallet/src/features/transactions/history/utils' import { SendTokenTransactionInfo, + TransactionDetailsType, TransactionListQueryResponse, TransactionType, } from 'wallet/src/features/transactions/types' export default function parseSendTransaction( - transaction: NonNullable + transaction: NonNullable, ): SendTokenTransactionInfo | undefined { - if (transaction.details.__typename !== 'TransactionDetails') { + if (transaction.details.__typename !== TransactionDetailsType.Transaction) { return undefined } - const change = transaction.details.assetChanges?.[0] + let change = transaction.details.assetChanges?.[0] + + // For some NFT transfers, the first assetChange is an NftApproval followed by an NftTransfer + if ( + change?.__typename === 'NftApproval' && + transaction.details.assetChanges?.length && + transaction.details.assetChanges.length > 1 + ) { + change = transaction.details.assetChanges[1] + } if (!change) { return undefined @@ -49,6 +59,7 @@ export default function parseSendTransaction( collectionName, imageURL, tokenId, + address: tokenAddress, }, } } @@ -67,15 +78,13 @@ export default function parseSendTransaction( change.asset.chain, change.asset.address, change.asset.decimals, - change.quantity + change.quantity, ) const transactedUSDValue = parseUSDValueFromAssetChange(change.transactedValue) // Filter out send transactions with tokens that are either marked `isSpam` or with spam code 2 (token with URL name) // because send txs can be spoofed with spam tokens - const isSpam = Boolean( - change.asset.project?.isSpam || change.asset.project?.spamCode === SpamCode.HIGH - ) + const isSpam = Boolean(change.asset.project?.isSpam || change.asset.project?.spamCode === SpamCode.HIGH) if (!(recipient && tokenAddress)) { return undefined diff --git a/packages/wallet/src/features/transactions/history/conversion/parseTradeTransaction.ts b/packages/wallet/src/features/transactions/history/conversion/parseTradeTransaction.ts index 7191741617d..11c442cdabe 100644 --- a/packages/wallet/src/features/transactions/history/conversion/parseTradeTransaction.ts +++ b/packages/wallet/src/features/transactions/history/conversion/parseTradeTransaction.ts @@ -5,7 +5,8 @@ import { TokenStandard, TransactionDirection, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' +import { buildCurrencyId, buildNativeCurrencyId, buildWrappedNativeCurrencyId } from 'uniswap/src/utils/currencyId' import { deriveCurrencyAmountFromAssetResponse, parseUSDValueFromAssetChange, @@ -14,15 +15,11 @@ import { ConfirmedSwapTransactionInfo, NFTTradeTransactionInfo, NFTTradeType, + TransactionDetailsType, TransactionListQueryResponse, TransactionType, WrapTransactionInfo, } from 'wallet/src/features/transactions/types' -import { - buildCurrencyId, - buildNativeCurrencyId, - buildWrappedNativeCurrencyId, -} from 'wallet/src/utils/currencyId' type TransferAssetChange = Extract< NonNullable< @@ -35,10 +32,10 @@ type TransferAssetChange = Extract< > export default function parseTradeTransaction( - transaction: NonNullable + transaction: NonNullable, ): ConfirmedSwapTransactionInfo | NFTTradeTransactionInfo | WrapTransactionInfo | undefined { // ignore UniswapX transactions for now - if (transaction?.details?.__typename !== 'TransactionDetails') { + if (transaction?.details?.__typename !== TransactionDetailsType.Transaction) { return undefined } @@ -49,8 +46,7 @@ export default function parseTradeTransaction( const txAssetChanges = transaction.details.assetChanges?.filter( - (t): t is TransferAssetChange => - t?.__typename === 'TokenTransfer' || t?.__typename === 'NftTransfer' + (t): t is TransferAssetChange => t?.__typename === 'TokenTransfer' || t?.__typename === 'NftTransfer', ) ?? [] // for detecting wraps @@ -69,9 +65,7 @@ export default function parseTradeTransaction( } const isRefundInternalTx = - t?.__typename === 'TokenTransfer' && - t.asset.id === sent?.asset.id && - t.tokenStandard === TokenStandard.Native + t?.__typename === 'TokenTransfer' && t.asset.id === sent?.asset.id && t.tokenStandard === TokenStandard.Native if (isRefundInternalTx) { acc.refund = t @@ -84,7 +78,7 @@ export default function parseTradeTransaction( { refund: undefined, received: undefined, - } + }, ) // Invalid input/output info @@ -92,8 +86,7 @@ export default function parseTradeTransaction( return } - const onlyERC20Tokens = - sent.__typename === 'TokenTransfer' && received.__typename === 'TokenTransfer' + const onlyERC20Tokens = sent.__typename === 'TokenTransfer' && received.__typename === 'TokenTransfer' const containsNFT = sent.__typename === 'NftTransfer' || received.__typename === 'NftTransfer' // TODO: [MOB-235] Currently no spec for advanced transfer types. @@ -107,20 +100,20 @@ export default function parseTradeTransaction( sent.tokenStandard === TokenStandard.Native ? buildNativeCurrencyId(chainId) : sent.asset.address - ? buildCurrencyId(chainId, sent.asset.address) - : null + ? buildCurrencyId(chainId, sent.asset.address) + : null const outputCurrencyId = received.tokenStandard === TokenStandard.Native ? buildNativeCurrencyId(chainId) : received.asset.address - ? buildCurrencyId(chainId, received.asset.address) - : null + ? buildCurrencyId(chainId, received.asset.address) + : null let inputCurrencyAmountRaw = deriveCurrencyAmountFromAssetResponse( sent.tokenStandard, sent.asset.chain, sent.asset.address, sent.asset.decimals, - sent.quantity + sent.quantity, ) if (refund && refund.tokenStandard === sent.tokenStandard) { @@ -129,12 +122,10 @@ export default function parseTradeTransaction( refund.asset.chain, refund.asset.address, refund.asset.decimals, - refund.quantity + refund.quantity, ) - inputCurrencyAmountRaw = BigNumber.from(inputCurrencyAmountRaw) - .sub(refundCurrencyAmountRaw) - .toString() + inputCurrencyAmountRaw = BigNumber.from(inputCurrencyAmountRaw).sub(refundCurrencyAmountRaw).toString() } const outputCurrencyAmountRaw = deriveCurrencyAmountFromAssetResponse( @@ -142,7 +133,7 @@ export default function parseTradeTransaction( received.asset.chain, received.asset.address, received.asset.decimals, - received.quantity + received.quantity, ) const transactedUSDValue = parseUSDValueFromAssetChange(sent.transactedValue) @@ -191,26 +182,29 @@ export default function parseTradeTransaction( tokenChange.tokenStandard === TokenStandard.Native ? buildNativeCurrencyId(chainId) : tokenChange.asset?.address - ? buildCurrencyId(chainId, tokenChange.asset.address) - : undefined + ? buildCurrencyId(chainId, tokenChange.asset.address) + : undefined const purchaseCurrencyAmountRaw = deriveCurrencyAmountFromAssetResponse( tokenChange.tokenStandard, tokenChange.asset.chain, tokenChange.asset.address, tokenChange.asset.decimals, - tokenChange.quantity + tokenChange.quantity, ) const tradeType = nftChange.direction === 'IN' ? NFTTradeType.BUY : NFTTradeType.SELL const transactedUSDValue = parseUSDValueFromAssetChange(tokenChange.transactedValue) + const address = nftChange.asset.nftContract?.address + if ( !name || !collectionName || !imageURL || !tokenId || !purchaseCurrencyId || - !purchaseCurrencyAmountRaw + !purchaseCurrencyAmountRaw || + !address ) { return undefined } @@ -222,6 +216,7 @@ export default function parseTradeTransaction( collectionName, imageURL, tokenId, + address, }, purchaseCurrencyId, purchaseCurrencyAmountRaw, diff --git a/packages/wallet/src/features/transactions/history/utils.ts b/packages/wallet/src/features/transactions/history/utils.ts index 151c88672f3..f8f9e6993c9 100644 --- a/packages/wallet/src/features/transactions/history/utils.ts +++ b/packages/wallet/src/features/transactions/history/utils.ts @@ -1,5 +1,6 @@ import { Token } from '@uniswap/sdk-core' import dayjs from 'dayjs' +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { Amount, Chain, @@ -10,26 +11,23 @@ import { TokenStandard, TransactionListQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { CurrencyId } from 'uniswap/src/types/currency' -import { getNativeAddress } from 'wallet/src/constants/addresses' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { CurrencyIdToVisibility } from 'wallet/src/features/favorites/slice' -import { - FORMAT_DATE_MONTH, - FORMAT_DATE_MONTH_YEAR, - LocalizedDayjs, -} from 'wallet/src/features/language/localizedDayjs' +import { FORMAT_DATE_MONTH, FORMAT_DATE_MONTH_YEAR, LocalizedDayjs } from 'wallet/src/features/language/localizedDayjs' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { extractOnRampTransactionDetails } from 'wallet/src/features/transactions/history/conversion/extractFiatOnRampTransactionDetails' import extractTransactionDetails from 'wallet/src/features/transactions/history/conversion/extractTransactionDetails' +import { extractUniswapXOrderDetails } from 'wallet/src/features/transactions/history/conversion/extractUniswapXOrderDetails' import { isUniswapX } from 'wallet/src/features/transactions/swap/trade/utils' import { TransactionDetails, + TransactionDetailsType, TransactionListQueryResponse, TransactionStatus, TransactionType, } from 'wallet/src/features/transactions/types' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' export interface AllFormattedTransactions { @@ -41,16 +39,16 @@ export interface AllFormattedTransactions { export function formatTransactionsByDate( transactions: TransactionDetails[] | undefined, - localizedDayjs: LocalizedDayjs + localizedDayjs: LocalizedDayjs, ): AllFormattedTransactions { // timestamp in ms for start of time periods const msTimestampCutoff24h = dayjs().subtract(24, 'hour').valueOf() const msTimestampCutoffYear = dayjs().startOf('year').valueOf() // Segment by time periods. - const [pending, last24hTransactionList, olderThan24HTransactionList] = ( - transactions ?? [] - ).reduce<[TransactionDetails[], TransactionDetails[], TransactionDetails[]]>( + const [pending, last24hTransactionList, olderThan24HTransactionList] = (transactions ?? []).reduce< + [TransactionDetails[], TransactionDetails[], TransactionDetails[]] + >( (accum, item) => { if ( // Want all incomplete transactions @@ -66,7 +64,7 @@ export function formatTransactionsByDate( } return accum }, - [[], [], []] + [[], [], []], ) const pendingSorted = pending.sort((a, b) => { @@ -95,7 +93,7 @@ export function formatTransactionsByDate( accum[key] = currentMonthList return accum }, - {} + {}, ) return { @@ -112,11 +110,11 @@ export function formatTransactionsByDate( export function parseDataResponseToTransactionDetails( data: TransactionListQuery, hideSpamTokens: boolean, - tokenVisibilityOverrides?: CurrencyIdToVisibility + tokenVisibilityOverrides?: CurrencyIdToVisibility, ): TransactionDetails[] | undefined { if (data.portfolios?.[0]?.assetActivities) { return data.portfolios[0].assetActivities.reduce((accum: TransactionDetails[], t) => { - if (t?.details?.__typename === 'TransactionDetails') { + if (t?.details?.__typename === TransactionDetailsType.Transaction) { const parsed = extractTransactionDetails(t as TransactionListQueryResponse) const isSpam = parsed?.typeInfo.isSpam const currencyId = extractCurrencyIdFromTx(parsed) @@ -125,11 +123,16 @@ export function parseDataResponseToTransactionDetails( if (parsed && !(hideSpamTokens && isSpam && !spamOverride)) { accum.push(parsed) } - } else if (t?.details?.__typename === 'OnRampTransactionDetails') { + } else if (t?.details?.__typename === TransactionDetailsType.OnRamp) { const parsed = extractOnRampTransactionDetails(t as TransactionListQueryResponse) if (parsed) { accum.push(parsed) } + } else if (t?.details?.__typename === TransactionDetailsType.UniswapXOrder) { + const parsed = extractUniswapXOrderDetails(t as TransactionListQueryResponse) + if (parsed) { + accum.push(parsed) + } } return accum @@ -144,14 +147,14 @@ export function parseDataResponseToTransactionDetails( */ export function parseDataResponseToFeedTransactionDetails( data: FeedTransactionListQuery, - hideSpamTokens?: boolean + hideSpamTokens?: boolean, ): TransactionDetails[] | undefined { const allTransactions: TransactionDetails[] = [] for (const portfolio of data.portfolios ?? []) { if (portfolio?.assetActivities) { const transactions = portfolio.assetActivities.reduce((accum: TransactionDetails[], t) => { - if (t?.details?.__typename === 'TransactionDetails') { + if (t?.details?.__typename === TransactionDetailsType.Transaction) { const parsed = extractTransactionDetails(t as TransactionListQueryResponse) const isSpam = parsed?.typeInfo.isSpam @@ -183,7 +186,7 @@ export function deriveCurrencyAmountFromAssetResponse( chain: Chain, address: Maybe, decimals: Maybe, - quantity: string + quantity: string, ): string { const chainId = fromGraphQLChain(chain) if (!chainId) { @@ -194,8 +197,8 @@ export function deriveCurrencyAmountFromAssetResponse( tokenStandard === TokenStandard.Native ? NativeCurrency.onChain(chainId) : address && decimals - ? new Token(chainId, address, decimals) - : undefined + ? new Token(chainId, address, decimals) + : undefined const currencyAmount = getCurrencyAmount({ value: quantity, @@ -235,9 +238,7 @@ export function getAddressFromAsset({ * @param transactedValue Transacted value amount from TokenTransfer API response * @returns parsed USD value as a number if currency is of type USD */ -export function parseUSDValueFromAssetChange( - transactedValue: Maybe> -): number | undefined { +export function parseUSDValueFromAssetChange(transactedValue: Maybe>): number | undefined { return transactedValue?.currency === Currency.Usd ? transactedValue.value ?? undefined : undefined } @@ -262,7 +263,7 @@ function extractCurrencyIdFromTx(transaction: TransactionDetails | null): Curren export function remoteTxStatusToLocalTxStatus( type: RemoteTransactionType, - status: RemoteTransactionStatus + status: RemoteTransactionStatus, ): TransactionStatus { switch (status) { case RemoteTransactionStatus.Failed: diff --git a/packages/wallet/src/features/transactions/hooks.ts b/packages/wallet/src/features/transactions/hooks.ts index 4e0e358bdba..1395d0b83ab 100644 --- a/packages/wallet/src/features/transactions/hooks.ts +++ b/packages/wallet/src/features/transactions/hooks.ts @@ -2,11 +2,10 @@ import { Currency } from '@uniswap/sdk-core' import { BigNumberish } from 'ethers' import { useMemo } from 'react' import { WalletChainId } from 'uniswap/src/types/chains' +import { ensureLeading0x } from 'uniswap/src/utils/addresses' +import { areCurrencyIdsEqual, buildCurrencyId } from 'uniswap/src/utils/currencyId' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { - makeSelectTransaction, - useSelectAddressTransactions, -} from 'wallet/src/features/transactions/selectors' +import { makeSelectTransaction, useSelectAddressTransactions } from 'wallet/src/features/transactions/selectors' import { finalizeTransaction } from 'wallet/src/features/transactions/slice' import { createSwapFormFromTxDetails, @@ -22,12 +21,10 @@ import { } from 'wallet/src/features/transactions/types' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' import { useAppDispatch, useAppSelector } from 'wallet/src/state' -import { ensureLeading0x } from 'wallet/src/utils/addresses' -import { areCurrencyIdsEqual, buildCurrencyId } from 'wallet/src/utils/currencyId' export function usePendingTransactions( address: Address | null, - ignoreTransactionTypes: TransactionType[] = [] + ignoreTransactionTypes: TransactionType[] = [], ): TransactionDetails[] | undefined { const transactions = useSelectAddressTransactions(address) return useMemo(() => { @@ -36,31 +33,26 @@ export function usePendingTransactions( } return transactions.filter( (tx: { status: TransactionStatus; typeInfo: { type: TransactionType } }) => - tx.status === TransactionStatus.Pending && - !ignoreTransactionTypes.includes(tx.typeInfo.type) + tx.status === TransactionStatus.Pending && !ignoreTransactionTypes.includes(tx.typeInfo.type), ) }, [ignoreTransactionTypes, transactions]) } // sorted oldest to newest -export function useSortedPendingTransactions( - address: Address | null -): TransactionDetails[] | undefined { +export function useSortedPendingTransactions(address: Address | null): TransactionDetails[] | undefined { const transactions = usePendingTransactions(address) return useMemo(() => { if (!transactions) { return } - return transactions.sort( - (a: TransactionDetails, b: TransactionDetails) => a.addedTime - b.addedTime - ) + return transactions.sort((a: TransactionDetails, b: TransactionDetails) => a.addedTime - b.addedTime) }, [transactions]) } export function useSelectTransaction( address: Address | undefined, chainId: WalletChainId | undefined, - txId: string | undefined + txId: string | undefined, ): TransactionDetails | undefined { const selectTransaction = useMemo(makeSelectTransaction, []) return useAppSelector((state) => selectTransaction(state, { address, chainId, txId })) @@ -69,19 +61,15 @@ export function useSelectTransaction( export function useCreateSwapFormState( address: Address | undefined, chainId: WalletChainId | undefined, - txId: string | undefined + txId: string | undefined, ): TransactionState | undefined { const transaction = useSelectTransaction(address, chainId, txId) const inputCurrencyId = - transaction?.typeInfo.type === TransactionType.Swap - ? transaction.typeInfo.inputCurrencyId - : undefined + transaction?.typeInfo.type === TransactionType.Swap ? transaction.typeInfo.inputCurrencyId : undefined const outputCurrencyId = - transaction?.typeInfo.type === TransactionType.Swap - ? transaction.typeInfo.outputCurrencyId - : undefined + transaction?.typeInfo.type === TransactionType.Swap ? transaction.typeInfo.outputCurrencyId : undefined const inputCurrencyInfo = useCurrencyInfo(inputCurrencyId) const outputCurrencyInfo = useCurrencyInfo(outputCurrencyId) @@ -104,7 +92,7 @@ export function useCreateWrapFormState( chainId: WalletChainId | undefined, txId: string | undefined, inputCurrency: Maybe, - outputCurrency: Maybe + outputCurrency: Maybe, ): TransactionState | undefined { const transaction = useSelectTransaction(address, chainId, txId) @@ -126,7 +114,7 @@ export function useCreateWrapFormState( */ export function useMergeLocalAndRemoteTransactions( address: Address, - remoteTransactions: TransactionDetails[] | undefined + remoteTransactions: TransactionDetails[] | undefined, ): TransactionDetails[] | undefined { const dispatch = useAppDispatch() const localTransactions = useSelectAddressTransactions(address) @@ -220,10 +208,7 @@ export function useMergeLocalAndRemoteTransactions( return deDupedTxs.sort((a, b) => { // If inclusion times are equal, then sequence approve txs before swap txs if (a.addedTime === b.addedTime) { - if ( - a.typeInfo.type === TransactionType.Approve && - b.typeInfo.type === TransactionType.Swap - ) { + if (a.typeInfo.type === TransactionType.Approve && b.typeInfo.type === TransactionType.Swap) { const aCurrencyId = buildCurrencyId(a.chainId, a.typeInfo.tokenAddress) const bCurrencyId = b.typeInfo.inputCurrencyId if (areCurrencyIdsEqual(aCurrencyId, bCurrencyId)) { @@ -231,10 +216,7 @@ export function useMergeLocalAndRemoteTransactions( } } - if ( - a.typeInfo.type === TransactionType.Swap && - b.typeInfo.type === TransactionType.Approve - ) { + if (a.typeInfo.type === TransactionType.Swap && b.typeInfo.type === TransactionType.Approve) { const aCurrencyId = a.typeInfo.inputCurrencyId const bCurrencyId = buildCurrencyId(b.chainId, b.typeInfo.tokenAddress) if (areCurrencyIdsEqual(aCurrencyId, bCurrencyId)) { diff --git a/packages/wallet/src/features/transactions/hooks/useAllTransactionsBetweenAddresses.ts b/packages/wallet/src/features/transactions/hooks/useAllTransactionsBetweenAddresses.ts index 537352a5464..0b8178447f0 100644 --- a/packages/wallet/src/features/transactions/hooks/useAllTransactionsBetweenAddresses.ts +++ b/packages/wallet/src/features/transactions/hooks/useAllTransactionsBetweenAddresses.ts @@ -9,7 +9,7 @@ import { TransactionDetails, TransactionType } from 'wallet/src/features/transac */ export function useAllTransactionsBetweenAddresses( sender: Address, - recipient: Maybe
+ recipient: Maybe
, ): TransactionDetails[] | undefined { const txnsToSearch = useSelectAddressTransactions(sender) return useMemo(() => { @@ -17,8 +17,7 @@ export function useAllTransactionsBetweenAddresses( return } return txnsToSearch.filter( - (tx: TransactionDetails) => - tx.typeInfo.type === TransactionType.Send && tx.typeInfo.recipient === recipient + (tx: TransactionDetails) => tx.typeInfo.type === TransactionType.Send && tx.typeInfo.recipient === recipient, ) }, [recipient, sender, txnsToSearch]) } diff --git a/packages/wallet/src/features/transactions/hooks/useParsedTransactionWarnings.tsx b/packages/wallet/src/features/transactions/hooks/useParsedTransactionWarnings.tsx index b0ed7ce679d..f8ca869e4f6 100644 --- a/packages/wallet/src/features/transactions/hooks/useParsedTransactionWarnings.tsx +++ b/packages/wallet/src/features/transactions/hooks/useParsedTransactionWarnings.tsx @@ -4,10 +4,7 @@ import { isWeb } from 'ui/src' import { AlertTriangle } from 'ui/src/components/icons' import { useSwapFormContext } from 'wallet/src/features/transactions/contexts/SwapFormContext' import { useSwapTxContext } from 'wallet/src/features/transactions/contexts/SwapTxContext' -import { - isPriceImpactWarning, - useSwapWarnings, -} from 'wallet/src/features/transactions/hooks/useSwapWarnings' +import { isPriceImpactWarning, useSwapWarnings } from 'wallet/src/features/transactions/hooks/useSwapWarnings' import { useTransactionGasWarning } from 'wallet/src/features/transactions/hooks/useTransactionGasWarning' import { Warning, @@ -67,17 +64,11 @@ export function useParsedSendWarnings(allSendWarnings: Warning[]): ParsedWarning function useFormattedWarnings(warnings: Warning[]): ParsedWarnings { return useMemo(() => { const blockingWarning = warnings.find( - (warning) => - warning.action === WarningAction.DisableReview || - warning.action === WarningAction.DisableSubmit + (warning) => warning.action === WarningAction.DisableReview || warning.action === WarningAction.DisableSubmit, ) - const insufficientBalanceWarning = warnings.find( - (warning) => warning.type === WarningLabel.InsufficientFunds - ) - const insufficientGasFundsWarning = warnings.find( - (warning) => warning.type === WarningLabel.InsufficientGasFunds - ) + const insufficientBalanceWarning = warnings.find((warning) => warning.type === WarningLabel.InsufficientFunds) + const insufficientGasFundsWarning = warnings.find((warning) => warning.type === WarningLabel.InsufficientGasFunds) const priceImpactWarning = warnings.find((warning) => isPriceImpactWarning(warning)) return { @@ -92,9 +83,7 @@ function useFormattedWarnings(warnings: Warning[]): ParsedWarnings { }, [warnings]) } -function getReviewScreenWarning( - warnings: Warning[] -): ParsedWarnings['reviewScreenWarning'] | undefined { +function getReviewScreenWarning(warnings: Warning[]): ParsedWarnings['reviewScreenWarning'] | undefined { const reviewWarning = warnings.find((warning) => warning.severity >= WarningSeverity.Medium) if (!reviewWarning) { @@ -105,12 +94,8 @@ function getReviewScreenWarning( } // This function decides which warning to show when there is more than one. -function getFormScreenWarning( - warnings: Warning[] -): ParsedWarnings['reviewScreenWarning'] | undefined { - const insufficientBalanceWarning = warnings.find( - (warning) => warning.type === WarningLabel.InsufficientFunds - ) +function getFormScreenWarning(warnings: Warning[]): ParsedWarnings['reviewScreenWarning'] | undefined { + const insufficientBalanceWarning = warnings.find((warning) => warning.type === WarningLabel.InsufficientFunds) if (insufficientBalanceWarning) { return { @@ -122,8 +107,7 @@ function getFormScreenWarning( } const formWarning = warnings.find( - (warning) => - warning.type === WarningLabel.InsufficientFunds || warning.severity >= WarningSeverity.Low + (warning) => warning.type === WarningLabel.InsufficientFunds || warning.severity >= WarningSeverity.Low, ) if (!formWarning) { @@ -133,8 +117,7 @@ function getFormScreenWarning( return getWarningWithStyle({ warning: formWarning, displayedInline: - formWarning.type !== WarningLabel.InsufficientGasFunds && - (!isWeb || !isPriceImpactWarning(formWarning)), + formWarning.type !== WarningLabel.InsufficientGasFunds && (!isWeb || !isPriceImpactWarning(formWarning)), }) } diff --git a/packages/wallet/src/features/transactions/hooks/useSwapWarnings.tsx b/packages/wallet/src/features/transactions/hooks/useSwapWarnings.tsx index ce1e34b4294..7e5eead551c 100644 --- a/packages/wallet/src/features/transactions/hooks/useSwapWarnings.tsx +++ b/packages/wallet/src/features/transactions/hooks/useSwapWarnings.tsx @@ -6,10 +6,7 @@ import { useTranslation } from 'react-i18next' import { isWeb } from 'ui/src' import { normalizePriceImpact } from 'utilities/src/format/normalizePriceImpact' import { useMemoCompare } from 'utilities/src/react/hooks' -import { - LocalizationContextState, - useLocalizationContext, -} from 'wallet/src/features/language/LocalizationContext' +import { LocalizationContextState, useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { getNetworkWarning } from 'wallet/src/features/transactions/WarningModal/getNetworkWarning' import { Warning, @@ -33,7 +30,7 @@ export function getSwapWarnings( t: TFunction, formatPercent: LocalizationContextState['formatPercent'], derivedSwapInfo: DerivedSwapInfo, - offline: boolean + offline: boolean, ): Warning[] { const warnings: Warning[] = [] @@ -142,10 +139,7 @@ export function useSwapWarnings(derivedSwapInfo: DerivedSwapInfo): Warning[] { // See for more here: https://github.com/react-native-netinfo/react-native-netinfo/pull/444 const offline = isOffline(networkStatus) - return useMemoCompare( - () => getSwapWarnings(t, formatPercent, derivedSwapInfo, offline), - _.isEqual - ) + return useMemoCompare(() => getSwapWarnings(t, formatPercent, derivedSwapInfo, offline), _.isEqual) } const formIncomplete = (derivedSwapInfo: DerivedSwapInfo): boolean => { @@ -164,7 +158,5 @@ const formIncomplete = (derivedSwapInfo: DerivedSwapInfo): boolean => { } export function isPriceImpactWarning(warning: Warning): boolean { - return ( - warning.type === WarningLabel.PriceImpactMedium || warning.type === WarningLabel.PriceImpactHigh - ) + return warning.type === WarningLabel.PriceImpactMedium || warning.type === WarningLabel.PriceImpactHigh } diff --git a/packages/wallet/src/features/transactions/hooks/useSyncFiatAndTokenAmountUpdater.tsx b/packages/wallet/src/features/transactions/hooks/useSyncFiatAndTokenAmountUpdater.tsx index d850bca334d..88dfb9c7cb7 100644 --- a/packages/wallet/src/features/transactions/hooks/useSyncFiatAndTokenAmountUpdater.tsx +++ b/packages/wallet/src/features/transactions/hooks/useSyncFiatAndTokenAmountUpdater.tsx @@ -1,11 +1,8 @@ import { useEffect } from 'react' +import { currencyIdToChain } from 'uniswap/src/utils/currencyId' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useSwapFormContext } from 'wallet/src/features/transactions/contexts/SwapFormContext' -import { - STABLECOIN_AMOUNT_OUT, - useUSDCPrice, -} from 'wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice' -import { currencyIdToChain } from 'wallet/src/utils/currencyId' +import { STABLECOIN_AMOUNT_OUT, useUSDCPrice } from 'wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' // Used for rounding in conversion math @@ -20,20 +17,14 @@ const NUM_DECIMALS_DISPLAY_FIAT = 2 * amount. This allows us to toggle between 2 modes, without losing the entered amount. */ export function useSyncFiatAndTokenAmountUpdater(): void { - const { - isFiatMode, - updateSwapForm, - exactAmountToken, - exactAmountFiat, - derivedSwapInfo, - exactCurrencyField, - } = useSwapFormContext() + const { isFiatMode, updateSwapForm, exactAmountToken, exactAmountFiat, derivedSwapInfo, exactCurrencyField } = + useSwapFormContext() const exactCurrency = derivedSwapInfo.currencies[exactCurrencyField] const usdPriceOfCurrency = useUSDCPrice(exactCurrency?.currency ?? undefined) const { convertFiatAmount } = useLocalizationContext() - const conversionRate = convertFiatAmount().amount + const conversionRate = convertFiatAmount(1).amount const chainId = currencyIdToChain(exactCurrency?.currencyId ?? '') useEffect(() => { @@ -42,17 +33,14 @@ export function useSyncFiatAndTokenAmountUpdater(): void { } if (isFiatMode) { - const fiatAmount = - exactAmountFiat && !isNaN(parseFloat(exactAmountFiat)) ? parseFloat(exactAmountFiat) : 0 + const fiatAmount = exactAmountFiat && !isNaN(parseFloat(exactAmountFiat)) ? parseFloat(exactAmountFiat) : 0 const usdAmount = (fiatAmount / conversionRate).toFixed(NUM_DECIMALS_FIAT_ROUNDING) const stablecoinAmount = getCurrencyAmount({ value: usdAmount, valueType: ValueType.Exact, currency: STABLECOIN_AMOUNT_OUT[chainId]?.currency, }) - const tokenAmount = stablecoinAmount - ? usdPriceOfCurrency?.invert().quote(stablecoinAmount) - : undefined + const tokenAmount = stablecoinAmount ? usdPriceOfCurrency?.invert().quote(stablecoinAmount) : undefined updateSwapForm({ exactAmountToken: tokenAmount?.toExact() }) } diff --git a/packages/wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts.tsx b/packages/wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts.tsx index d0e4a4ac5d6..7c08f8bab1c 100644 --- a/packages/wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts.tsx +++ b/packages/wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts.tsx @@ -30,17 +30,13 @@ export function useTokenAndFiatDisplayAmounts({ isFiatMode, }: FormattedDisplayAmountsProps): string { const appFiatCurrency = useAppFiatCurrencyInfo() - const { convertFiatAmountFormatted, formatCurrencyAmount, addFiatSymbolToNumber } = - useLocalizationContext() + const { convertFiatAmountFormatted, formatCurrencyAmount, addFiatSymbolToNumber } = useLocalizationContext() const formattedCurrencyAmount = currencyAmount ? formatCurrencyAmount({ value: currencyAmount, type: NumberType.TokenTx }) : '' - const formattedFiatValue: string = convertFiatAmountFormatted( - usdValue?.toExact(), - NumberType.FiatTokenQuantity - ) + const formattedFiatValue: string = convertFiatAmountFormatted(usdValue?.toExact(), NumberType.FiatTokenQuantity) // In fiat mode, show equivalent token amount. In token mode, show equivalent fiat amount return useMemo((): string => { diff --git a/packages/wallet/src/features/transactions/hooks/useTokenFormActionHandlers.ts b/packages/wallet/src/features/transactions/hooks/useTokenFormActionHandlers.ts index 4bcb5636e42..c31924dcb57 100644 --- a/packages/wallet/src/features/transactions/hooks/useTokenFormActionHandlers.ts +++ b/packages/wallet/src/features/transactions/hooks/useTokenFormActionHandlers.ts @@ -16,13 +16,13 @@ export function useTokenFormActionHandlers(dispatch: React.Dispatch): const onUpdateExactTokenAmount = useCallback( (field: CurrencyField, amount: string) => dispatch(transactionStateActions.updateExactAmountToken({ field, amount })), - [dispatch] + [dispatch], ) const onUpdateExactUSDAmount = useCallback( (field: CurrencyField, amount: string) => dispatch(transactionStateActions.updateExactAmountFiat({ field, amount })), - [dispatch] + [dispatch], ) const onSetExactAmount = useCallback( @@ -30,7 +30,7 @@ export function useTokenFormActionHandlers(dispatch: React.Dispatch): const updater = isFiatInput ? onUpdateExactUSDAmount : onUpdateExactTokenAmount updater(field, value) }, - [onUpdateExactUSDAmount, onUpdateExactTokenAmount] + [onUpdateExactUSDAmount, onUpdateExactTokenAmount], ) const onSetMax = useCallback( @@ -38,13 +38,11 @@ export function useTokenFormActionHandlers(dispatch: React.Dispatch): // when setting max amount, always switch to token mode because // our token/usd updater doesnt handle this case yet dispatch(transactionStateActions.toggleFiatInput(false)) - dispatch( - transactionStateActions.updateExactAmountToken({ field: CurrencyField.INPUT, amount }) - ) + dispatch(transactionStateActions.updateExactAmountToken({ field: CurrencyField.INPUT, amount })) // Unfocus the CurrencyInputField by setting focusOnCurrencyField to null dispatch(transactionStateActions.onFocus(null)) }, - [dispatch] + [dispatch], ) const onSwitchCurrencies = useCallback(() => { @@ -53,22 +51,13 @@ export function useTokenFormActionHandlers(dispatch: React.Dispatch): const onToggleFiatInput = useCallback( (isFiatInput: boolean) => dispatch(transactionStateActions.toggleFiatInput(isFiatInput)), - [dispatch] + [dispatch], ) - const onCreateTxId = useCallback( - (txId: string) => dispatch(transactionStateActions.setTxId(txId)), - [dispatch] - ) + const onCreateTxId = useCallback((txId: string) => dispatch(transactionStateActions.setTxId(txId)), [dispatch]) - const onFocusInput = useCallback( - () => dispatch(transactionStateActions.onFocus(CurrencyField.INPUT)), - [dispatch] - ) - const onFocusOutput = useCallback( - () => dispatch(transactionStateActions.onFocus(CurrencyField.OUTPUT)), - [dispatch] - ) + const onFocusInput = useCallback(() => dispatch(transactionStateActions.onFocus(CurrencyField.INPUT)), [dispatch]) + const onFocusOutput = useCallback(() => dispatch(transactionStateActions.onFocus(CurrencyField.OUTPUT)), [dispatch]) return { onCreateTxId, onFocusInput, diff --git a/packages/wallet/src/features/transactions/hooks/useTokenSelectorActionHandlers.ts b/packages/wallet/src/features/transactions/hooks/useTokenSelectorActionHandlers.ts index ed1b9bbfe58..75e3c7290ce 100644 --- a/packages/wallet/src/features/transactions/hooks/useTokenSelectorActionHandlers.ts +++ b/packages/wallet/src/features/transactions/hooks/useTokenSelectorActionHandlers.ts @@ -3,17 +3,17 @@ import { Currency } from '@uniswap/sdk-core' import { useCallback } from 'react' import { WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' +import { currencyAddress } from 'uniswap/src/utils/currencyId' import { flowToModalName } from 'wallet/src/components/TokenSelector/flowToModalName' import { AssetType } from 'wallet/src/entities/assets' import { SearchContext } from 'wallet/src/features/search/SearchContext' import { transactionStateActions } from 'wallet/src/features/transactions/transactionState/transactionState' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { TokenSelectorFlow } from 'wallet/src/features/transactions/transfer/types' -import { currencyAddress } from 'wallet/src/utils/currencyId' export function useTokenSelectorActionHandlers( dispatch: React.Dispatch, - flow: TokenSelectorFlow + flow: TokenSelectorFlow, ): { onShowTokenSelector: (field: CurrencyField) => void onHideTokenSelector: () => void @@ -21,12 +21,12 @@ export function useTokenSelectorActionHandlers( } { const onShowTokenSelector = useCallback( (field: CurrencyField) => dispatch(transactionStateActions.showTokenSelector(field)), - [dispatch] + [dispatch], ) const onHideTokenSelector = useCallback( () => dispatch(transactionStateActions.showTokenSelector(undefined)), - [dispatch] + [dispatch], ) const onSelectCurrency = useCallback( @@ -39,7 +39,7 @@ export function useTokenSelectorActionHandlers( chainId: currency.chainId, type: AssetType.Currency, }, - }) + }), ) // log event that a currency was selected @@ -58,7 +58,7 @@ export function useTokenSelectorActionHandlers( // hide screen when done selecting onHideTokenSelector() }, - [dispatch, flow, onHideTokenSelector] + [dispatch, flow, onHideTokenSelector], ) return { onSelectCurrency, onShowTokenSelector, onHideTokenSelector } } diff --git a/packages/wallet/src/features/transactions/refetchGQLQueriesSaga.ts b/packages/wallet/src/features/transactions/refetchGQLQueriesSaga.ts index df17438ffb6..a68f4664d25 100644 --- a/packages/wallet/src/features/transactions/refetchGQLQueriesSaga.ts +++ b/packages/wallet/src/features/transactions/refetchGQLQueriesSaga.ts @@ -1,22 +1,18 @@ import { ApolloClient, NormalizedCacheObject } from '@apollo/client' import { call, delay } from 'typed-redux-saga' +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { PortfolioBalancesDocument, PortfolioBalancesQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { CurrencyId } from 'uniswap/src/types/currency' import { ONE_SECOND_MS } from 'utilities/src/time/time' -import { getNativeAddress } from 'wallet/src/constants/addresses' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { GQL_QUERIES_TO_REFETCH_ON_TXN_UPDATE } from 'wallet/src/features/transactions/TransactionHistoryUpdater' import { TransactionDetails, TransactionType } from 'wallet/src/features/transactions/types' -import { - buildCurrencyId, - buildNativeCurrencyId, - buildWrappedNativeCurrencyId, -} from 'wallet/src/utils/currencyId' +import { buildCurrencyId, buildNativeCurrencyId, buildWrappedNativeCurrencyId } from 'wallet/src/utils/currencyId' type CurrencyIdToBalance = Record @@ -78,9 +74,7 @@ export function* refetchGQLQueries({ } // based on transaction data, determine which currencies we expect to see a balance update on -function getCurrenciesWithExpectedUpdates( - transaction: TransactionDetails -): Set | undefined { +function getCurrenciesWithExpectedUpdates(transaction: TransactionDetails): Set | undefined { const currenciesWithBalToUpdate: Set = new Set() const txChainId = transaction.chainId @@ -97,9 +91,7 @@ function getCurrenciesWithExpectedUpdates( currenciesWithBalToUpdate.add(transaction.typeInfo.outputCurrencyId.toLowerCase()) break case TransactionType.Send: - currenciesWithBalToUpdate.add( - buildCurrencyId(txChainId, transaction.typeInfo.tokenAddress).toLowerCase() - ) + currenciesWithBalToUpdate.add(buildCurrencyId(txChainId, transaction.typeInfo.tokenAddress).toLowerCase()) break case TransactionType.Wrap: currenciesWithBalToUpdate.add(buildWrappedNativeCurrencyId(txChainId)) @@ -125,7 +117,7 @@ function readBalancesFromCache({ const currencyIdToBalance: CurrencyIdToBalance = Array.from(currencyIdsToUpdate).reduce( (currIdToBal, currencyId) => ({ ...currIdToBal, [currencyId]: 0 }), // assume 0 balance and update later if found in cache - {} + {}, ) const cachedBalancesData = apolloClient.readQuery({ @@ -157,10 +149,7 @@ function readBalancesFromCache({ return currencyIdToBalance } -function checkIfBalancesUpdated( - balance1: CurrencyIdToBalance, - balance2: Maybe -) { +function checkIfBalancesUpdated(balance1: CurrencyIdToBalance, balance2: Maybe) { if (!balance2) { return true } // if no currencies to check, then assume balances are updated diff --git a/packages/wallet/src/features/transactions/replaceTransactionSaga.ts b/packages/wallet/src/features/transactions/replaceTransactionSaga.ts index 62ccab26a55..d0c404b0391 100644 --- a/packages/wallet/src/features/transactions/replaceTransactionSaga.ts +++ b/packages/wallet/src/features/transactions/replaceTransactionSaga.ts @@ -1,6 +1,7 @@ import { BigNumber, providers } from 'ethers' import { call, put } from 'typed-redux-saga' import i18n from 'uniswap/src/i18n/i18n' +import { getValidAddress } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' @@ -11,19 +12,15 @@ import { TransactionDetails, TransactionStatus, } from 'wallet/src/features/transactions/types' -import { - createTransactionId, - getSerializableTransactionRequest, -} from 'wallet/src/features/transactions/utils' +import { createTransactionId, getSerializableTransactionRequest } from 'wallet/src/features/transactions/utils' import { getProvider, getSignerManager } from 'wallet/src/features/wallet/context' import { selectAccounts } from 'wallet/src/features/wallet/selectors' import { appSelect } from 'wallet/src/state' -import { getValidAddress } from 'wallet/src/utils/addresses' export function* attemptReplaceTransaction( transaction: ClassicTransactionDetails, newTxRequest: providers.TransactionRequest, - isCancellation = false + isCancellation = false, ) { const { chainId, hash, options } = transaction logger.debug('replaceTransaction', '', 'Attempting tx replacement', hash) @@ -59,7 +56,7 @@ export function* attemptReplaceTransaction( request, account, provider, - signerManager + signerManager, ) logger.debug('replaceTransaction', '', 'Tx submitted. New hash:', transactionResponse.hash) @@ -92,7 +89,7 @@ export function* attemptReplaceTransaction( address: transaction.from, id: replacementTxnId, chainId: transaction.chainId, - }) + }), ) yield* put( @@ -102,7 +99,7 @@ export function* attemptReplaceTransaction( errorMessage: isCancellation ? i18n.t('transaction.notification.error.cancel') : i18n.t('transaction.notification.error.replace'), - }) + }), ) } } diff --git a/packages/wallet/src/features/transactions/selectors.ts b/packages/wallet/src/features/transactions/selectors.ts index 97baff1c9fb..9ec796b93fe 100644 --- a/packages/wallet/src/features/transactions/selectors.ts +++ b/packages/wallet/src/features/transactions/selectors.ts @@ -1,6 +1,7 @@ import { createSelector, Selector } from '@reduxjs/toolkit' import { useMemo } from 'react' import { WalletChainId } from 'uniswap/src/types/chains' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { unique } from 'utilities/src/primitives/array' import { flattenObjectOfObjects } from 'utilities/src/primitives/objects' import { SearchableRecipient } from 'wallet/src/features/address/types' @@ -17,7 +18,6 @@ import { } from 'wallet/src/features/transactions/types' import { useAccounts } from 'wallet/src/features/wallet/hooks' import { RootState, useAppSelector } from 'wallet/src/state' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' export const selectTransactions = (state: RootState): TransactionStateMap => state.transactions @@ -69,19 +69,17 @@ export const makeSelectAddressTransactions = (): Selector< tx2.options.request.chainId && tx2.options.request.chainId === tx.options.request.chainId && tx.options.request.nonce && - tx2.options.request.nonce === tx.options.request.nonce + tx2.options.request.nonce === tx.options.request.nonce, ) if (duplicate) { return tx.addedTime > duplicate.addedTime } return true }) - } + }, ) -export function useSelectAddressTransactions( - address: Address | null -): TransactionDetails[] | undefined { +export function useSelectAddressTransactions(address: Address | null): TransactionDetails[] | undefined { const selectAddressTransactions = useMemo(makeSelectAddressTransactions, []) return useAppSelector((state) => selectAddressTransactions(state, address)) } @@ -90,15 +88,13 @@ export function useCurrencyIdToVisibility(): CurrencyIdToVisibility { const accounts = useAccounts() const addresses = Object.values(accounts).map((account) => account.address) const manuallySetTokenVisibility = useAppSelector(selectTokensVisibility) - const selectLocalTxCurrencyIds: ( - state: RootState, - addresses: Address[] - ) => CurrencyIdToVisibility = useMemo(makeSelectTokenVisibilityFromLocalTxs, []) - - const tokenVisibilityFromLocalTxs = useAppSelector((state) => - selectLocalTxCurrencyIds(state, addresses) + const selectLocalTxCurrencyIds: (state: RootState, addresses: Address[]) => CurrencyIdToVisibility = useMemo( + makeSelectTokenVisibilityFromLocalTxs, + [], ) + const tokenVisibilityFromLocalTxs = useAppSelector((state) => selectLocalTxCurrencyIds(state, addresses)) + return { ...tokenVisibilityFromLocalTxs, // Tokens the user has individually shown/hidden in the app should take preference over local txs @@ -106,11 +102,7 @@ export function useCurrencyIdToVisibility(): CurrencyIdToVisibility { } } -const makeSelectTokenVisibilityFromLocalTxs = (): Selector< - RootState, - CurrencyIdToVisibility, - [Address[]] -> => +const makeSelectTokenVisibilityFromLocalTxs = (): Selector => createSelector( selectTransactions, (_: RootState, addresses: Address[]) => addresses, @@ -133,7 +125,7 @@ const makeSelectTokenVisibilityFromLocalTxs = (): Selector< }) return acc - }, {}) + }, {}), ) interface MakeSelectParams { @@ -142,11 +134,7 @@ interface MakeSelectParams { txId: string | undefined } -export const makeSelectTransaction = (): Selector< - RootState, - TransactionDetails | undefined, - [MakeSelectParams] -> => +export const makeSelectTransaction = (): Selector => createSelector( selectTransactions, (_: RootState, { address, chainId, txId }: MakeSelectParams) => ({ @@ -165,22 +153,19 @@ export const makeSelectTransaction = (): Selector< } return Object.values(addressTxs).find((txDetails) => txDetails.id === txId) - } + }, ) // Returns a list of past recipients ordered from most to least recent // TODO: [MOB-232] either revert this to return addresses or keep but also return displayName so that it's searchable for RecipientSelect export const selectRecipientsByRecency = (state: RootState): SearchableRecipient[] => { const transactionsByChainId = flattenObjectOfObjects(state.transactions) - const sendTransactions = transactionsByChainId.reduce( - (accum, transactions) => { - const sendTransactionsWithRecipients = Object.values(transactions).filter( - (tx) => tx.typeInfo.type === TransactionType.Send && tx.typeInfo.recipient - ) - return [...accum, ...sendTransactionsWithRecipients] - }, - [] - ) + const sendTransactions = transactionsByChainId.reduce((accum, transactions) => { + const sendTransactionsWithRecipients = Object.values(transactions).filter( + (tx) => tx.typeInfo.type === TransactionType.Send && tx.typeInfo.recipient, + ) + return [...accum, ...sendTransactionsWithRecipients] + }, []) const sortedRecipients = sendTransactions .sort((a, b) => (a.addedTime < b.addedTime ? 1 : -1)) .map((transaction) => { @@ -196,10 +181,7 @@ export const selectIncompleteTransactions = (state: RootState): TransactionDetai const transactionsByChainId = flattenObjectOfObjects(state.transactions) return transactionsByChainId.reduce((accum, transactions) => { const pendingTxs = Object.values(transactions).filter( - (tx) => - Boolean(!tx.receipt) && - tx.status !== TransactionStatus.Failed && - tx.status !== TransactionStatus.Success + (tx) => Boolean(!tx.receipt) && tx.status !== TransactionStatus.Failed && tx.status !== TransactionStatus.Success, ) return [...accum, ...pendingTxs] }, []) diff --git a/packages/wallet/src/features/transactions/sendTransactionSaga.ts b/packages/wallet/src/features/transactions/sendTransactionSaga.ts index ea38fef563b..ee50915027b 100644 --- a/packages/wallet/src/features/transactions/sendTransactionSaga.ts +++ b/packages/wallet/src/features/transactions/sendTransactionSaga.ts @@ -15,10 +15,7 @@ import { TransactionType, TransactionTypeInfo, } from 'wallet/src/features/transactions/types' -import { - createTransactionId, - getSerializableTransactionRequest, -} from 'wallet/src/features/transactions/utils' +import { createTransactionId, getSerializableTransactionRequest } from 'wallet/src/features/transactions/utils' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { getProvider, getSignerManager } from 'wallet/src/features/wallet/context' import { SignerManager } from 'wallet/src/features/wallet/signing/SignerManager' @@ -42,11 +39,7 @@ export function* sendTransaction(params: SendTransactionParams) { const { chainId, account, options } = params const request = options.request - logger.debug( - 'sendTransaction', - '', - `Sending tx on ${UNIVERSE_CHAIN_INFO[chainId].label} to ${request.to}` - ) + logger.debug('sendTransaction', '', `Sending tx on ${UNIVERSE_CHAIN_INFO[chainId].label} to ${request.to}`) if (account.type === AccountType.Readonly) { throw new Error('Account must support signing') @@ -61,7 +54,7 @@ export function* sendTransaction(params: SendTransactionParams) { request, account, provider, - signerManager + signerManager, ) logger.debug('sendTransaction', '', 'Tx submitted:', transactionResponse.hash) @@ -74,7 +67,7 @@ export async function signAndSendTransaction( request: providers.TransactionRequest, account: Account, provider: providers.Provider, - signerManager: SignerManager + signerManager: SignerManager, ): Promise<{ transactionResponse: providers.TransactionResponse populatedRequest: providers.TransactionRequest @@ -91,7 +84,7 @@ export async function signAndSendTransaction( function* addTransaction( { chainId, typeInfo, account, options, txId, analytics }: SendTransactionParams, hash: string, - populatedRequest: providers.TransactionRequest + populatedRequest: providers.TransactionRequest, ) { const id = txId ?? createTransactionId() const request = getSerializableTransactionRequest(populatedRequest, chainId) diff --git a/packages/wallet/src/features/transactions/slice.ts b/packages/wallet/src/features/transactions/slice.ts index 260384ba6dd..f2cb38e0bbe 100644 --- a/packages/wallet/src/features/transactions/slice.ts +++ b/packages/wallet/src/features/transactions/slice.ts @@ -25,31 +25,19 @@ const slice = createSlice({ reducers: { addTransaction: (state, { payload: transaction }: PayloadAction) => { const { chainId, id, from } = transaction - assert( - !state?.[from]?.[chainId]?.[id], - `addTransaction: Attempted to overwrite tx with id ${id}` - ) + assert(!state?.[from]?.[chainId]?.[id], `addTransaction: Attempted to overwrite tx with id ${id}`) state[from] ??= {} state[from]![chainId] ??= {} state[from]![chainId]![id] = transaction }, updateTransaction: (state, { payload: transaction }: PayloadAction) => { const { chainId, id, from } = transaction - assert( - state?.[from]?.[chainId]?.[id], - `updateTransaction: Attempted to update a missing tx with id ${id}` - ) + assert(state?.[from]?.[chainId]?.[id], `updateTransaction: Attempted to update a missing tx with id ${id}`) state[from]![chainId]![id] = transaction }, - finalizeTransaction: ( - state, - { payload: transaction }: PayloadAction - ) => { + finalizeTransaction: (state, { payload: transaction }: PayloadAction) => { const { chainId, id, status, receipt, from } = transaction - assert( - state?.[from]?.[chainId]?.[id], - `finalizeTransaction: Attempted to finalize a missing tx with id ${id}` - ) + assert(state?.[from]?.[chainId]?.[id], `finalizeTransaction: Attempted to finalize a missing tx with id ${id}`) state[from]![chainId]![id]!.status = status if (receipt) { state[from]![chainId]![id]!.receipt = receipt @@ -57,11 +45,11 @@ const slice = createSlice({ }, deleteTransaction: ( state, - { payload: { chainId, id, address } }: PayloadAction + { payload: { chainId, id, address } }: PayloadAction, ) => { assert( state?.[address]?.[chainId]?.[id], - `deleteTransaction: Attempted to delete a tx that doesn't exist with id ${id}` + `deleteTransaction: Attempted to delete a tx that doesn't exist with id ${id}`, ) delete state[address]![chainId]![id] }, @@ -69,13 +57,11 @@ const slice = createSlice({ state, { payload: { chainId, id, address, cancelRequest }, - }: PayloadAction< - TransactionId & { address: string; cancelRequest: providers.TransactionRequest } - > + }: PayloadAction, ) => { assert( state?.[address]?.[chainId]?.[id], - `cancelTransaction: Attempted to cancel a tx that doesn't exist with id ${id}` + `cancelTransaction: Attempted to cancel a tx that doesn't exist with id ${id}`, ) state[address]![chainId]![id]!.status = TransactionStatus.Cancelling state[address]![chainId]![id]!.cancelRequest = cancelRequest @@ -88,11 +74,11 @@ const slice = createSlice({ TransactionId & { newTxParams: providers.TransactionRequest } & { address: string } - > + >, ) => { assert( state?.[address]?.[chainId]?.[id], - `replaceTransaction: Attempted to replace a tx that doesn't exist with id ${id}` + `replaceTransaction: Attempted to replace a tx that doesn't exist with id ${id}`, ) state[address]![chainId]![id]!.status = TransactionStatus.Replacing }, @@ -100,9 +86,7 @@ const slice = createSlice({ // fiat onramp transactions re-use this slice to store (off-chain) pending txs upsertFiatOnRampTransaction: ( state, - { - payload: transaction, - }: PayloadAction + { payload: transaction }: PayloadAction, ) => { const { chainId, @@ -115,9 +99,7 @@ const slice = createSlice({ state[from] ??= {} state[from]![chainId] ??= {} - const oldTypeInfo = state[from]![chainId]![id]?.typeInfo as - | FiatPurchaseTransactionInfo - | undefined + const oldTypeInfo = state[from]![chainId]![id]?.typeInfo as FiatPurchaseTransactionInfo | undefined state[from]![chainId]![id] = { ...transaction, typeInfo: { ...oldTypeInfo, ...transaction.typeInfo }, @@ -127,9 +109,7 @@ const slice = createSlice({ }) // This action is fired, when user has come back from Moonpay flow using Return to Uniswap button -export const forceFetchFiatOnRampTransactions = createAction( - 'transactions/forceFetchFiatOnRampTransactions' -) +export const forceFetchFiatOnRampTransactions = createAction('transactions/forceFetchFiatOnRampTransactions') export const { addTransaction, diff --git a/packages/wallet/src/features/transactions/swap/CurrencyInputPanel.tsx b/packages/wallet/src/features/transactions/swap/CurrencyInputPanel.tsx index 63258e1bc69..7d2c18cf1fd 100644 --- a/packages/wallet/src/features/transactions/swap/CurrencyInputPanel.tsx +++ b/packages/wallet/src/features/transactions/swap/CurrencyInputPanel.tsx @@ -1,42 +1,14 @@ /* eslint-disable complexity */ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' -import { - RefObject, - forwardRef, - memo, - useCallback, - useEffect, - useImperativeHandle, - useRef, -} from 'react' -import { - NativeSyntheticEvent, - TextInput, - TextInputProps, - TextInputSelectionChangeEventData, -} from 'react-native' -import { - Easing, - useAnimatedStyle, - useSharedValue, - withRepeat, - withSequence, - withTiming, -} from 'react-native-reanimated' -import { - Flex, - FlexProps, - Text, - TouchableArea, - isWeb, - useIsShortMobileDevice, - useSporeColors, -} from 'ui/src' +import { RefObject, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef } from 'react' +import { NativeSyntheticEvent, TextInput, TextInputProps, TextInputSelectionChangeEventData } from 'react-native' +import { Easing, useAnimatedStyle, useSharedValue, withRepeat, withSequence, withTiming } from 'react-native-reanimated' +import { Flex, FlexProps, Text, TouchableArea, isWeb, useIsShortMobileDevice, useSporeColors } from 'ui/src' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { fonts } from 'ui/src/theme' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { ElementName } from 'uniswap/src/features/telemetry/constants' -import { isDetoxBuild } from 'utilities/src/environment' +import { isDetoxBuild } from 'utilities/src/environment/constants' import { NumberType } from 'utilities/src/format/types' import { usePrevious } from 'utilities/src/react/hooks' import { SelectTokenButton } from 'wallet/src/components/TokenSelector/SelectTokenButton' @@ -111,7 +83,7 @@ export const CurrencyInputPanel = memo( onPressDisabled, ...rest }, - forwardedRef + forwardedRef, ): JSX.Element { const colors = useSporeColors() const isShortMobileDevice = useIsShortMobileDevice() @@ -125,7 +97,7 @@ export const CurrencyInputPanel = memo( () => ({ transform: [{ translateX: shakeValue.value }], }), - [shakeValue.value] + [shakeValue.value], ) const triggerShakeAnimation = useCallback(() => { @@ -161,19 +133,12 @@ export const CurrencyInputPanel = memo( } else if (!focus && isTextInputRefActuallyFocused) { inputRef.current?.blur() } - }, [ - currencyField, - focus, - inputRef, - isTextInputRefActuallyFocused, - resetSelection, - value?.length, - ]) + }, [currencyField, focus, inputRef, isTextInputRefActuallyFocused, resetSelection, value?.length]) const { onLayout, fontSize, onSetFontSize } = useDynamicFontSizing( MAX_CHAR_PIXEL_WIDTH, MAX_INPUT_FONT_SIZE, - MIN_INPUT_FONT_SIZE + MIN_INPUT_FONT_SIZE, ) // This is needed to ensure that the text resizes when modified from outside the component (e.g. custom numpad) @@ -192,7 +157,7 @@ export const CurrencyInputPanel = memo( selection: { start, end }, }, }: NativeSyntheticEvent) => selectionChange?.(start, end), - [selectionChange] + [selectionChange], ) // Hide balance if panel is output, and no balance @@ -223,10 +188,10 @@ export const CurrencyInputPanel = memo( loadingFlexProgress.value = withRepeat( withSequence( withTiming(0.4, { duration: 400, easing: Easing.ease }), - withTiming(1, { duration: 400, easing: Easing.ease }) + withTiming(1, { duration: 400, easing: Easing.ease }), ), -1, - true + true, ) } @@ -234,7 +199,7 @@ export const CurrencyInputPanel = memo( () => ({ opacity: isLoading ? loadingFlexProgress.value : 1, }), - [isLoading, loadingFlexProgress] + [isLoading, loadingFlexProgress], ) const onPressDisabledWithShakeAnimation = useCallback((): void => { @@ -248,30 +213,27 @@ export const CurrencyInputPanel = memo( (amount: string) => { onSetMax?.(amount, currencyField) }, - [currencyField, onSetMax] + [currencyField, onSetMax], ) return ( + onPress={disabled ? onPressDisabledWithShakeAnimation : currencyInfo ? onPressIn : onShowTokenSelector} + > + py={isWeb ? '$spacing24' : isShortMobileDevice ? '$spacing8' : '$spacing20'} + > + style={shakeStyle} + > {isFiatMode && ( + mr="$spacing4" + > {fiatCurrencySymbol} )} @@ -292,7 +255,8 @@ export const CurrencyInputPanel = memo( mr="$spacing8" overflow="hidden" style={loadingStyle} - onLayout={onLayout}> + onLayout={onLayout} + > {currencyInfo ? ( {disabled && ( @@ -316,9 +280,7 @@ export const CurrencyInputPanel = memo( // (the text input height is greater than the font size and the input is // centered vertically, so the caret is cut off but the text is not) fontSize={fontSize} - maxDecimals={ - isFiatMode ? MAX_FIAT_INPUT_DECIMALS : currencyInfo.currency.decimals - } + maxDecimals={isFiatMode ? MAX_FIAT_INPUT_DECIMALS : currencyInfo.currency.decimals} maxFontSizeMultiplier={fonts.heading2.maxFontSizeMultiplier} minHeight={2 * MAX_INPUT_FONT_SIZE} overflow="visible" @@ -348,9 +310,7 @@ export const CurrencyInputPanel = memo( @@ -359,7 +319,9 @@ export const CurrencyInputPanel = memo( {currencyInfo && ( + flexShrink={1} + onPress={disabled ? onPressDisabledWithShakeAnimation : _onToggleIsFiatMode} + > {inputPanelFormattedValue} @@ -390,5 +352,5 @@ export const CurrencyInputPanel = memo( ) - }) + }), ) diff --git a/packages/wallet/src/features/transactions/swap/DecimalPad.tsx b/packages/wallet/src/features/transactions/swap/DecimalPad.tsx index 4106ebfa9ac..aee56bbc116 100644 --- a/packages/wallet/src/features/transactions/swap/DecimalPad.tsx +++ b/packages/wallet/src/features/transactions/swap/DecimalPad.tsx @@ -142,8 +142,7 @@ export const DecimalPad = memo(function DecimalPad({ {row.map((key, keyIndex) => { const isNumberKey = - key.label.charCodeAt(0) >= '0'.charCodeAt(0) && - key.label.charCodeAt(0) <= '9'.charCodeAt(0) + key.label.charCodeAt(0) >= '0'.charCodeAt(0) && key.label.charCodeAt(0) <= '9'.charCodeAt(0) const isKeyDisabled = disabled || disabledKeys[key.label] const shouldTriggerShake = isKeyDisabled && isNumberKey @@ -231,7 +230,8 @@ const KeyButton = memo(function KeyButton({ testID={'decimal-pad-' + label} width={keyWidth} onLongPress={handleLongPress} - onPress={handlePress}> + onPress={handlePress} + > {label === 'backspace' ? ( I18nManager.isRTL ? ( @@ -245,7 +245,8 @@ const KeyButton = memo(function KeyButton({ lineHeight: fonts.heading2.lineHeight * sizeMultiplier.lineHeight, fontSize: fonts.heading2.fontSize * sizeMultiplier.fontSize, }} - textAlign="center"> + textAlign="center" + > { label === '.' ? decimalSeparator : label /* respect phone settings to show decimal separator in the numpad, diff --git a/packages/wallet/src/features/transactions/swap/DecimalPadInput.tsx b/packages/wallet/src/features/transactions/swap/DecimalPadInput.tsx index 5da56eba8bd..78e4427b3eb 100644 --- a/packages/wallet/src/features/transactions/swap/DecimalPadInput.tsx +++ b/packages/wallet/src/features/transactions/swap/DecimalPadInput.tsx @@ -1,12 +1,4 @@ -import { - forwardRef, - memo, - useCallback, - useEffect, - useImperativeHandle, - useMemo, - useState, -} from 'react' +import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' import { TextInputProps } from 'uniswap/src/components/input/TextInput' import { maxDecimalsReached } from 'utilities/src/format/truncateToMaxDecimals' import { DecimalPad, KeyAction, KeyLabel } from 'wallet/src/features/transactions/swap/DecimalPad' @@ -43,7 +35,7 @@ export const DecimalPadInput = memo( maxDecimals, onTriggerInputShakeAnimation, }, - ref + ref, ): JSX.Element { const [disabledKeys, setDisabledKeys] = useState>>({}) const [maxHeight, setMaxHeight] = useState(null) @@ -86,7 +78,7 @@ export const DecimalPadInput = memo( return cursorAtStart || v.length === 0 }, }), - [getCurrentSelection, maxDecimals] + [getCurrentSelection, maxDecimals], ) const updateDisabledKeys = useCallback( @@ -100,7 +92,7 @@ export const DecimalPadInput = memo( isUpdated = true } return [key, isDisabled] - }) + }), ) // Prevent unnecessary re-renders and return the same value // if no key was updated (react state won't be updated if value is the @@ -109,7 +101,7 @@ export const DecimalPadInput = memo( return isUpdated ? newDisabledKeys : prevDisabledKeys }) }, - [disableKeysConditions] + [disableKeysConditions], ) const updateValue = useCallback( @@ -117,7 +109,7 @@ export const DecimalPadInput = memo( setValue(newValue) updateDisabledKeys(newValue) }, - [setValue, updateDisabledKeys] + [setValue, updateDisabledKeys], ) // TODO(MOB-140): in USD mode, prevent user from typing in more than 2 decimals @@ -133,7 +125,7 @@ export const DecimalPadInput = memo( updateValue(valueRef.current.slice(0, start) + label + valueRef.current.slice(end)) } }, - [updateValue, resetSelection, valueRef, getCurrentSelection] + [updateValue, resetSelection, valueRef, getCurrentSelection], ) const handleDelete = useCallback((): void => { @@ -164,7 +156,7 @@ export const DecimalPadInput = memo( handleDelete() } }, - [disabled, handleInsert, handleDelete] + [disabled, handleInsert, handleDelete], ) const onLongPress = useCallback( @@ -175,7 +167,7 @@ export const DecimalPadInput = memo( resetSelection({ start: 0, end: 0 }) updateValue('') }, - [disabled, updateValue, resetSelection] + [disabled, updateValue, resetSelection], ) return ( @@ -190,5 +182,5 @@ export const DecimalPadInput = memo( onTriggerInputShakeAnimation={onTriggerInputShakeAnimation} /> ) - }) + }), ) diff --git a/packages/wallet/src/features/transactions/swap/GasAndWarningRows.native.tsx b/packages/wallet/src/features/transactions/swap/GasAndWarningRows.native.tsx index 5337541cbe1..528111297e1 100644 --- a/packages/wallet/src/features/transactions/swap/GasAndWarningRows.native.tsx +++ b/packages/wallet/src/features/transactions/swap/GasAndWarningRows.native.tsx @@ -60,10 +60,7 @@ export function GasAndWarningRows({ renderEmptyRows }: GasAndWarningRowsProps): return ( <> {showWarningModal && formScreenWarning && ( - setShowWarningModal(false)} - /> + setShowWarningModal(false)} /> )} {/* @@ -88,9 +85,7 @@ export function GasAndWarningRows({ renderEmptyRows }: GasAndWarningRowsProps): {showGasFee && ( - }> + }> @@ -103,13 +98,7 @@ export function GasAndWarningRows({ renderEmptyRows }: GasAndWarningRowsProps): {showFormWarning && ( - + {formScreenWarning.Icon && ( {showWarningModal && formScreenWarning && ( - setShowWarningModal(false)} - /> + setShowWarningModal(false)} /> )} {/* @@ -105,13 +102,8 @@ export function GasAndWarningRows({ placement="bottom" tooltipTrigger={ - - + + {gasFeeFormatted} @@ -146,7 +138,8 @@ export function GasAndWarningRows({ // TODO(EXT-526): re-enable `exiting` animation when it's fixed. exiting={undefined} gap="$spacing8" - px="$spacing16"> + px="$spacing16" + > {formScreenWarning.Icon && ( ({ strokeDashoffset: CIRCLE_LENGTH * (1 - progress.value), }), - [progress] + [progress], ) useEffect(() => { @@ -61,7 +58,8 @@ export function HoldToSwapProgressCircle(): JSX.Element { fill="none" height={PROGRESS_CIRCLE_SIZE} viewBox={`0 0 ${PROGRESS_CIRCLE_SIZE} ${PROGRESS_CIRCLE_SIZE}`} - width={PROGRESS_CIRCLE_SIZE}> + width={PROGRESS_CIRCLE_SIZE} + > diff --git a/packages/wallet/src/features/transactions/swap/SwapArrowButton.tsx b/packages/wallet/src/features/transactions/swap/SwapArrowButton.tsx index 76ce922ce74..a782386f7a1 100644 --- a/packages/wallet/src/features/transactions/swap/SwapArrowButton.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapArrowButton.tsx @@ -10,14 +10,7 @@ type SwapArrowButtonProps = Pick< export function SwapArrowButton(props: SwapArrowButtonProps): JSX.Element { const colors = useSporeColors() - const { - testID, - onPress, - disabled, - backgroundColor = '$surface2', - size = iconSizes.icon24, - ...rest - } = props + const { testID, onPress, disabled, backgroundColor = '$surface2', size = iconSizes.icon24, ...rest } = props return useMemo( () => ( + {...rest} + > {/* hack to add 2px more padding without adjusting design system values */} ), - [backgroundColor, disabled, onPress, testID, rest, colors.neutral2.val, size] + [backgroundColor, disabled, onPress, testID, rest, colors.neutral2.val, size], ) } diff --git a/packages/wallet/src/features/transactions/swap/SwapDetails.tsx b/packages/wallet/src/features/transactions/swap/SwapDetails.tsx index 7358b1243fc..7848f3be39d 100644 --- a/packages/wallet/src/features/transactions/swap/SwapDetails.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapDetails.tsx @@ -22,7 +22,7 @@ import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount const getFeeAmountUsd = ( trade: Trade, - outputCurrencyPricePerUnitExact?: string + outputCurrencyPricePerUnitExact?: string, ): number | undefined => { if (!trade.swapFee || !outputCurrencyPricePerUnitExact) { return @@ -101,9 +101,7 @@ export function SwapDetails({ : undefined // Make text the warning color if user is setting custom slippage higher than auto slippage value - const showSlippageWarning = autoSlippageTolerance - ? acceptedTrade.slippageTolerance > autoSlippageTolerance - : false + const showSlippageWarning = autoSlippageTolerance ? acceptedTrade.slippageTolerance > autoSlippageTolerance : false const feeOnTransferProps: FeeOnTransferFeeGroupProps = useMemo( () => ({ @@ -121,7 +119,7 @@ export function SwapDetails({ acceptedTrade.inputTax, acceptedTrade.outputAmount.currency.symbol, acceptedTrade.outputTax, - ] + ], ) return ( @@ -144,7 +142,8 @@ export function SwapDetails({ swapFeeInfo={swapFeeInfo} transactionUSDValue={derivedSwapInfo.currencyAmountsUSDValue[CurrencyField.OUTPUT]} warning={warning} - onShowWarning={onShowWarning}> + onShowWarning={onShowWarning} + > {t('swap.details.rate')} @@ -164,12 +163,7 @@ export function SwapDetails({ {!customSlippageTolerance ? ( - + {t('swap.settings.slippage.control.auto')} @@ -197,14 +191,10 @@ function AcceptNewQuoteRow({ const { formatCurrencyAmount } = useLocalizationContext() const derivedCurrencyField = - derivedSwapInfo.exactCurrencyField === CurrencyField.INPUT - ? CurrencyField.OUTPUT - : CurrencyField.INPUT + derivedSwapInfo.exactCurrencyField === CurrencyField.INPUT ? CurrencyField.OUTPUT : CurrencyField.INPUT const derivedAmount = derivedSwapInfo.currencyAmounts[derivedCurrencyField] - const derivedSymbol = getSymbolDisplayText( - derivedSwapInfo.currencies[derivedCurrencyField]?.currency.symbol - ) + const derivedSymbol = getSymbolDisplayText(derivedSwapInfo.currencies[derivedCurrencyField]?.currency.symbol) const formattedDerivedAmount = formatCurrencyAmount({ value: derivedAmount, type: NumberType.TokenTx, @@ -227,7 +217,8 @@ function AcceptNewQuoteRow({ justifyContent="space-between" pl="$spacing12" pr="$spacing8" - py="$spacing8"> + py="$spacing8" + > {derivedSwapInfo.exactCurrencyField === CurrencyField.INPUT @@ -235,14 +226,8 @@ function AcceptNewQuoteRow({ : t('swap.details.newQuote.input')} - - {formattedDerivedAmount} {derivedSymbol}{' '} - ({percentageDifference}%) + + {formattedDerivedAmount} {derivedSymbol} ({percentageDifference}%) @@ -253,7 +238,8 @@ function AcceptNewQuoteRow({ borderRadius="$rounded12" px="$spacing8" py="$spacing4" - onPress={onAcceptTrade}> + onPress={onAcceptTrade} + > {t('common.button.accept')} @@ -272,9 +258,7 @@ function calculatePercentageDifference({ acceptedDerivedSwapInfo: DerivedSwapInfo }): string | null { const derivedCurrencyField = - derivedSwapInfo.exactCurrencyField === CurrencyField.INPUT - ? CurrencyField.OUTPUT - : CurrencyField.INPUT + derivedSwapInfo.exactCurrencyField === CurrencyField.INPUT ? CurrencyField.OUTPUT : CurrencyField.INPUT // It's important to convert these to fractions before doing math on them in order to preserve full precision on each step. const newAmount = derivedSwapInfo.currencyAmounts[derivedCurrencyField]?.asFraction diff --git a/packages/wallet/src/features/transactions/swap/SwapFlow.tsx b/packages/wallet/src/features/transactions/swap/SwapFlow.tsx index cae8ca189d6..d441f8e69cf 100644 --- a/packages/wallet/src/features/transactions/swap/SwapFlow.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapFlow.tsx @@ -4,10 +4,7 @@ import { BottomSheetModal } from 'uniswap/src/components/modals/BottomSheetModal import Trace from 'uniswap/src/features/telemetry/Trace' import { ModalName, SectionName } from 'uniswap/src/features/telemetry/constants' import { logger } from 'utilities/src/logger/logger' -import { - SwapFormContextProvider, - SwapFormState, -} from 'wallet/src/features/transactions/contexts/SwapFormContext' +import { SwapFormContextProvider, SwapFormState } from 'wallet/src/features/transactions/contexts/SwapFormContext' import { SwapScreen, SwapScreenContextProvider, @@ -103,7 +100,8 @@ function CurrentScreen({ + name={ModalName.SwapReview} + > diff --git a/packages/wallet/src/features/transactions/swap/SwapFormButton.tsx b/packages/wallet/src/features/transactions/swap/SwapFormButton.tsx index a8c7406f4b9..a543ed4ac59 100644 --- a/packages/wallet/src/features/transactions/swap/SwapFormButton.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapFormButton.tsx @@ -11,10 +11,7 @@ import { selectHasViewedReviewScreen, } from 'wallet/src/features/behaviorHistory/selectors' import { useSwapFormContext } from 'wallet/src/features/transactions/contexts/SwapFormContext' -import { - SwapScreen, - useSwapScreenContext, -} from 'wallet/src/features/transactions/contexts/SwapScreenContext' +import { SwapScreen, useSwapScreenContext } from 'wallet/src/features/transactions/contexts/SwapScreenContext' import { useTransactionModalContext } from 'wallet/src/features/transactions/contexts/TransactionModalContext' import { useParsedSwapWarnings } from 'wallet/src/features/transactions/hooks/useParsedTransactionWarnings' import { @@ -50,8 +47,7 @@ export function SwapFormButton(): JSX.Element { const noValidSwap = !isWrapAction(wrapType) && !trade.trade - const reviewButtonDisabled = - noValidSwap || !!blockingWarning || isBlocked || isBlockedLoading || walletNeedsRestore + const reviewButtonDisabled = noValidSwap || !!blockingWarning || isBlocked || isBlockedLoading || walletNeedsRestore const isHoldToSwapPressed = screen === SwapScreen.SwapReviewHoldingToSwap || isSubmitting @@ -70,7 +66,7 @@ export function SwapFormButton(): JSX.Element { updateSwapForm({ txId: createTransactionId() }) setScreen(nextScreen) }, - [setScreen, updateSwapForm] + [setScreen, updateSwapForm], ) const onReviewPress = useCallback(() => { @@ -100,11 +96,7 @@ export function SwapFormButton(): JSX.Element { const hasButtonWarning = !!blockingWarning?.buttonText const buttonText = blockingWarning?.buttonText ?? t('swap.button.review') const buttonTextColor = hasButtonWarning ? '$neutral2' : '$white' - const buttonBgColor = hasButtonWarning - ? '$surface3' - : isHoldToSwapPressed - ? '$accent2' - : '$accent1' + const buttonBgColor = hasButtonWarning ? '$surface3' : isHoldToSwapPressed ? '$accent2' : '$accent1' return ( @@ -123,17 +115,13 @@ export function SwapFormButton(): JSX.Element { onLongPress={onLongPressHoldToSwap} onPress={onReviewPress} onResponderRelease={onReleaseHoldToSwap} - onResponderTerminate={onReleaseHoldToSwap}> + onResponderTerminate={onReleaseHoldToSwap} + > {isHoldToSwapPressed ? ( - + {holdButtonText} diff --git a/packages/wallet/src/features/transactions/swap/SwapFormHeader.tsx b/packages/wallet/src/features/transactions/swap/SwapFormHeader.tsx index 4a19782e585..ce2979153f2 100644 --- a/packages/wallet/src/features/transactions/swap/SwapFormHeader.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapFormHeader.tsx @@ -21,8 +21,7 @@ export function SwapFormHeader(): JSX.Element { const account = useActiveAccountWithThrow() const { onClose } = useTransactionModalContext() - const { updateSwapForm, customSlippageTolerance, derivedSwapInfo, tradeProtocolPreference } = - useSwapFormContext() + const { updateSwapForm, customSlippageTolerance, derivedSwapInfo, tradeProtocolPreference } = useSwapFormContext() const [showSwapSettingsModal, setShowSettingsModal] = useState(false) const [showViewOnlyModal, setShowViewOnlyModal] = useState(false) @@ -42,14 +41,14 @@ export function SwapFormHeader(): JSX.Element { customSlippageTolerance: newCustomeSlippageTolerance, }) }, - [updateSwapForm] + [updateSwapForm], ) const setTradeProtocolPreference = useCallback( (newProtocolPreference: TradeProtocolPreference) => { updateSwapForm({ tradeProtocolPreference: newProtocolPreference }) }, - [updateSwapForm] + [updateSwapForm], ) const onCloseSettingsModal = useCallback(() => setShowSettingsModal(false), []) @@ -66,7 +65,8 @@ export function SwapFormHeader(): JSX.Element { mt={isWeb ? '$spacing4' : '$spacing8'} pl={isWeb ? '$none' : '$spacing12'} pr={isWeb ? '$spacing4' : customSlippageTolerance ? '$spacing4' : '$spacing16'} - testID={ElementName.SwapFormHeader}> + testID={ElementName.SwapFormHeader} + > {isWeb && ( + py="$spacing4" + > @@ -91,7 +92,8 @@ export function SwapFormHeader(): JSX.Element { justifyContent="center" px="$spacing8" py="$spacing4" - onPress={onPressViewOnlyModal}> + onPress={onPressViewOnlyModal} + > @@ -102,10 +104,7 @@ export function SwapFormHeader(): JSX.Element { )} {!isViewOnlyWallet && ( - + + py="$spacing4" + > {customSlippageTolerance ? ( {t('swap.form.slippage', { @@ -121,10 +121,7 @@ export function SwapFormHeader(): JSX.Element { })} ) : null} - + )} diff --git a/packages/wallet/src/features/transactions/swap/SwapFormScreen.tsx b/packages/wallet/src/features/transactions/swap/SwapFormScreen.tsx index 193959cfcbc..55b647e1bb3 100644 --- a/packages/wallet/src/features/transactions/swap/SwapFormScreen.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapFormScreen.tsx @@ -3,15 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { LayoutChangeEvent, StyleSheet, TextInputProps } from 'react-native' -import { - AnimatePresence, - Flex, - Text, - TouchableArea, - isWeb, - useIsShortMobileDevice, - useSporeColors, -} from 'ui/src' +import { AnimatePresence, Flex, Text, TouchableArea, isWeb, useIsShortMobileDevice, useSporeColors } from 'ui/src' import { InfoCircleFilled } from 'ui/src/components/icons' import { iconSizes, spacing } from 'ui/src/theme' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' @@ -24,14 +16,8 @@ import { NumberType } from 'utilities/src/format/types' import { useSwapFormContext } from 'wallet/src/features/transactions/contexts/SwapFormContext' import { useTransactionModalContext } from 'wallet/src/features/transactions/contexts/TransactionModalContext' import { useSyncFiatAndTokenAmountUpdater } from 'wallet/src/features/transactions/hooks/useSyncFiatAndTokenAmountUpdater' -import { - CurrencyInputPanel, - CurrencyInputPanelRef, -} from 'wallet/src/features/transactions/swap/CurrencyInputPanel' -import { - DecimalPadInput, - DecimalPadInputRef, -} from 'wallet/src/features/transactions/swap/DecimalPadInput' +import { CurrencyInputPanel, CurrencyInputPanelRef } from 'wallet/src/features/transactions/swap/CurrencyInputPanel' +import { DecimalPadInput, DecimalPadInputRef } from 'wallet/src/features/transactions/swap/DecimalPadInput' import { GasAndWarningRows } from 'wallet/src/features/transactions/swap/GasAndWarningRows' import { SwapArrowButton } from 'wallet/src/features/transactions/swap/SwapArrowButton' import { SwapFormButton } from 'wallet/src/features/transactions/swap/SwapFormButton' @@ -165,31 +151,20 @@ function SwapFormContent(): JSX.Element { [CurrencyField.INPUT]: inputSelectionRef, [CurrencyField.OUTPUT]: outputSelectionRef, }), - [inputSelectionRef, outputSelectionRef] + [inputSelectionRef, outputSelectionRef], ) const resetSelection = useCallback( - ({ - start, - end, - currencyField, - }: { - start: number - end?: number - currencyField?: CurrencyField - }) => { + ({ start, end, currencyField }: { start: number; end?: number; currencyField?: CurrencyField }) => { // Update refs first to have the latest selection state available in the DecimalPadInput // component and properly update disabled keys of the decimal pad. // We reset the native selection on the next tick because we need to wait for the native input to be updated. // This is needed because of the combination of state (delayed update) + ref (instant update) to improve performance. const _currencyField = currencyField ?? decimalPadControlledField - const selectionRef = - _currencyField === CurrencyField.INPUT ? inputSelectionRef : outputSelectionRef + const selectionRef = _currencyField === CurrencyField.INPUT ? inputSelectionRef : outputSelectionRef const inputFieldRef = - _currencyField === CurrencyField.INPUT - ? inputRef.current?.textInputRef - : outputRef.current?.textInputRef + _currencyField === CurrencyField.INPUT ? inputRef.current?.textInputRef : outputRef.current?.textInputRef selectionRef.current = { start, end } @@ -199,7 +174,7 @@ function SwapFormContent(): JSX.Element { }, 0) } }, - [decimalPadControlledField] + [decimalPadControlledField], ) const moveCursorToEnd = useCallback( @@ -210,8 +185,8 @@ function SwapFormContent(): JSX.Element { decimalPadControlledField === derivedCurrencyField ? formattedDerivedValueRef : _isFiatMode - ? exactAmountFiatRef - : exactAmountTokenRef + ? exactAmountFiatRef + : exactAmountTokenRef resetSelection({ start: amountRef.current.length, @@ -225,7 +200,7 @@ function SwapFormContent(): JSX.Element { exactAmountTokenRef, isFiatMode, resetSelection, - ] + ], ) const maxDecimals = isFiatMode @@ -248,7 +223,7 @@ function SwapFormContent(): JSX.Element { focusOnCurrencyField: decimalPadControlledField, }) }, - [decimalPadControlledField, isFiatMode, maxDecimals, updateSwapForm] + [decimalPadControlledField, isFiatMode, maxDecimals, updateSwapForm], ) const [decimalPadReady, setDecimalPadReady] = useState(false) @@ -283,7 +258,7 @@ function SwapFormContent(): JSX.Element { inputSelectionRef.current = { start, end } decimalPadRef.current?.updateDisabledKeys() }, - [amountUpdatedTimeRef] + [amountUpdatedTimeRef], ) const onOutputSelectionChange = useCallback( @@ -295,7 +270,7 @@ function SwapFormContent(): JSX.Element { outputSelectionRef.current = { start, end } decimalPadRef.current?.updateDisabledKeys() }, - [amountUpdatedTimeRef] + [amountUpdatedTimeRef], ) const onFocusInput = useCallback( @@ -303,7 +278,7 @@ function SwapFormContent(): JSX.Element { updateSwapForm({ focusOnCurrencyField: CurrencyField.INPUT, }), - [updateSwapForm] + [updateSwapForm], ) const onFocusOutput = useCallback( @@ -311,7 +286,7 @@ function SwapFormContent(): JSX.Element { updateSwapForm({ focusOnCurrencyField: CurrencyField.OUTPUT, }), - [updateSwapForm] + [updateSwapForm], ) const onShowTokenSelectorInput = useCallback((): void => { @@ -339,7 +314,7 @@ function SwapFormContent(): JSX.Element { exactAmountToken: amount, exactCurrencyField: CurrencyField.INPUT, }), - [isFiatMode, updateSwapForm] + [isFiatMode, updateSwapForm], ) const onSetExactAmountOutput = useCallback( @@ -355,7 +330,7 @@ function SwapFormContent(): JSX.Element { exactAmountToken: amount, exactCurrencyField: CurrencyField.OUTPUT, }), - [isFiatMode, updateSwapForm] + [isFiatMode, updateSwapForm], ) const onSetMax = useCallback( @@ -373,7 +348,7 @@ function SwapFormContent(): JSX.Element { decimalPadRef.current?.updateDisabledKeys() }, 0) }, - [moveCursorToEnd, updateSwapForm] + [moveCursorToEnd, updateSwapForm], ) // Reset selection based the new input value (token, or fiat), and toggle fiat mode @@ -388,7 +363,7 @@ function SwapFormContent(): JSX.Element { // We want this update to happen on the next tick, after the input value is updated. setTimeout(() => moveCursorToEnd({ overrideIsFiatMode: newIsFiatMode }), 0) }, - [isFiatMode, moveCursorToEnd, updateSwapForm] + [isFiatMode, moveCursorToEnd, updateSwapForm], ) const onSwitchCurrencies = useCallback(() => { @@ -396,8 +371,8 @@ function SwapFormContent(): JSX.Element { const newExactCurrencyField = exactOutputWouldFailIfCurrenciesSwitched ? CurrencyField.INPUT : exactFieldIsInput - ? CurrencyField.OUTPUT - : CurrencyField.INPUT + ? CurrencyField.OUTPUT + : CurrencyField.INPUT updateSwapForm({ exactCurrencyField: newExactCurrencyField, focusOnCurrencyField: newExactCurrencyField, @@ -446,8 +421,7 @@ function SwapFormContent(): JSX.Element { const exactValue = isFiatMode ? exactAmountFiat : exactAmountToken const exactValueRef = isFiatMode ? exactAmountFiatRef : exactAmountTokenRef - const decimalPadValueRef = - decimalPadControlledField === exactCurrencyField ? exactValueRef : formattedDerivedValueRef + const decimalPadValueRef = decimalPadControlledField === exactCurrencyField ? exactValueRef : formattedDerivedValueRef const containerShadowProps = { shadowColor: colors.surface3.val, @@ -483,28 +457,22 @@ function SwapFormContent(): JSX.Element { return ( - + + pb={currencies[CurrencyField.INPUT] ? '$spacing4' : '$none'} + > {showWebInputTokenSelector ? ( ) : ( @@ -532,11 +500,7 @@ function SwapFormContent(): JSX.Element { - {showSwitchCurrencies ? ( - - ) : ( - - )} + {showSwitchCurrencies ? : } {!showWebInputTokenSelector && ( <> @@ -546,9 +510,7 @@ function SwapFormContent(): JSX.Element { shrink animateOnly={['backgroundColor', 'opacity', 'shadowOpacity']} animation="quick" - backgroundColor={ - isWeb || focusOnCurrencyField === CurrencyField.OUTPUT ? '$surface1' : '$surface2' - } + backgroundColor={isWeb || focusOnCurrencyField === CurrencyField.OUTPUT ? '$surface1' : '$surface2'} borderColor="$surface3" borderRadius="$rounded20" borderWidth={1} @@ -556,7 +518,8 @@ function SwapFormContent(): JSX.Element { opacity={showWebInputTokenSelector ? WEB_CURRENCY_PANEL_INACTIVE_OPACITY : 1} overflow="hidden" position="relative" - pt={currencies[CurrencyField.OUTPUT] ? '$spacing4' : '$none'}> + pt={currencies[CurrencyField.OUTPUT] ? '$spacing4' : '$none'} + > {showWebOutputTokenSelector ? ( @@ -599,7 +562,8 @@ function SwapFormContent(): JSX.Element { borderTopWidth={1} gap="$spacing8" px="$spacing12" - py="$spacing12"> + py="$spacing12" + > {t('swap.form.warning.restore')} @@ -620,10 +584,7 @@ function SwapFormContent(): JSX.Element { {showWarning && ( - + )} {!showWarning && } @@ -642,11 +603,7 @@ function SwapFormContent(): JSX.Element { putting it inside this container in order to avoid any overflows while the `DecimalPad` is automatically resizing to find the right size for the screen. */} - + + right={0} + > void -}): JSX.Element => { +const SwitchCurrenciesButton = ({ onSwitchCurrencies }: { onSwitchCurrencies: () => void }): JSX.Element => { const isShortMobileDevice = useIsShortMobileDevice() const smallOrRegular = isShortMobileDevice ? 'small' : 'regular' @@ -699,7 +653,8 @@ const SwitchCurrenciesButton = ({ ) ) / 2 } - position="absolute"> + position="absolute" + > + variant={isPrimary ? 'body3' : 'body4'} + > {latestRate} {latestUSDPrice && ` (${latestFiatPriceFormatted})`} diff --git a/packages/wallet/src/features/transactions/swap/SwapReviewScreen.tsx b/packages/wallet/src/features/transactions/swap/SwapReviewScreen.tsx index ce9ba2be14f..8cab3890738 100644 --- a/packages/wallet/src/features/transactions/swap/SwapReviewScreen.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapReviewScreen.tsx @@ -1,15 +1,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { FadeIn } from 'react-native-reanimated' -import { - Button, - Flex, - HapticFeedback, - Separator, - SpinningLoader, - isWeb, - useIsShortMobileDevice, -} from 'ui/src' +import { Button, Flex, HapticFeedback, Separator, SpinningLoader, isWeb, useIsShortMobileDevice } from 'ui/src' import { BackArrow } from 'ui/src/components/icons' import { AnimatedFlex } from 'ui/src/components/layout/AnimatedFlex' import { iconSizes } from 'ui/src/theme' @@ -21,10 +13,7 @@ import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' import { TransactionDetails } from 'wallet/src/features/transactions/TransactionDetails/TransactionDetails' import { useSwapFormContext } from 'wallet/src/features/transactions/contexts/SwapFormContext' -import { - SwapScreen, - useSwapScreenContext, -} from 'wallet/src/features/transactions/contexts/SwapScreenContext' +import { SwapScreen, useSwapScreenContext } from 'wallet/src/features/transactions/contexts/SwapScreenContext' import { useSwapTxContext } from 'wallet/src/features/transactions/contexts/SwapTxContext' import { useTransactionModalContext } from 'wallet/src/features/transactions/contexts/TransactionModalContext' import { useParsedSwapWarnings } from 'wallet/src/features/transactions/hooks/useParsedTransactionWarnings' @@ -58,8 +47,7 @@ export function SwapReviewScreen({ hideContent }: { hideContent: boolean }): JSX const [warningAcknowledged, setWarningAcknowledged] = useState(false) const [shouldSubmitTx, setShouldSubmitTx] = useState(false) - const { bottomSheetViewStyles, onClose, BiometricsIcon, authTrigger } = - useTransactionModalContext() + const { bottomSheetViewStyles, onClose, BiometricsIcon, authTrigger } = useTransactionModalContext() const { screen, screenRef, setScreen } = useSwapScreenContext() @@ -123,7 +111,7 @@ export function SwapReviewScreen({ hideContent }: { hideContent: boolean }): JSX pushNotification({ type: AppNotificationType.SwapPending, wrapType, - }) + }), ) }, [dispatch, wrapType]) @@ -137,7 +125,7 @@ export function SwapReviewScreen({ hideContent }: { hideContent: boolean }): JSX wrapType, navigateToNextScreen, txRequest, - txId + txId, ) const onSwap = useSwapCallback( @@ -151,7 +139,7 @@ export function SwapReviewScreen({ hideContent }: { hideContent: boolean }): JSX navigateToNextScreen, txId, screen === SwapScreen.SwapReviewHoldingToSwap, - isFiatMode + isFiatMode, ) const submitTransaction = useCallback(() => { @@ -319,9 +307,7 @@ export function SwapReviewScreen({ hideContent }: { hideContent: boolean }): JSX return ( <> - + {showWarningModal && reviewScreenWarning?.warning.title && ( + onPress={onSubmitTransaction} + > {actionText} diff --git a/packages/wallet/src/features/transactions/swap/SwapTokenSelector.tsx b/packages/wallet/src/features/transactions/swap/SwapTokenSelector.tsx index 9c1a79f7f4f..859e2ed5eda 100644 --- a/packages/wallet/src/features/transactions/swap/SwapTokenSelector.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapTokenSelector.tsx @@ -3,6 +3,7 @@ import { useCallback } from 'react' import { isWeb } from 'ui/src' import { WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' +import { currencyAddress } from 'uniswap/src/utils/currencyId' import { TokenSelector, TokenSelectorModal, @@ -12,13 +13,9 @@ import { import { flowToModalName } from 'wallet/src/components/TokenSelector/flowToModalName' import { AssetType, TradeableAsset } from 'wallet/src/entities/assets' import { SearchContext } from 'wallet/src/features/search/SearchContext' -import { - SwapFormState, - useSwapFormContext, -} from 'wallet/src/features/transactions/contexts/SwapFormContext' +import { SwapFormState, useSwapFormContext } from 'wallet/src/features/transactions/contexts/SwapFormContext' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { TokenSelectorFlow } from 'wallet/src/features/transactions/transfer/types' -import { currencyAddress } from 'wallet/src/utils/currencyId' export function SwapTokenSelector(): JSX.Element { const swapContext = useSwapFormContext() @@ -84,7 +81,7 @@ export function SwapTokenSelector(): JSX.Element { // Hide screen when done selecting. onHideTokenSelector() }, - [exactCurrencyField, input, onHideTokenSelector, output, updateSwapForm] + [exactCurrencyField, input, onHideTokenSelector, output, updateSwapForm], ) const props: TokenSelectorProps = { diff --git a/packages/wallet/src/features/transactions/swap/TransactionAmountsReview.tsx b/packages/wallet/src/features/transactions/swap/TransactionAmountsReview.tsx index 84c6a753e38..137324c0042 100644 --- a/packages/wallet/src/features/transactions/swap/TransactionAmountsReview.tsx +++ b/packages/wallet/src/features/transactions/swap/TransactionAmountsReview.tsx @@ -21,16 +21,10 @@ export function TransactionAmountsReview({ }): JSX.Element { const { t } = useTranslation() const colors = useSporeColors() - const { convertFiatAmountFormatted, formatCurrencyAmount, formatNumberOrString } = - useLocalizationContext() + const { convertFiatAmountFormatted, formatCurrencyAmount, formatNumberOrString } = useLocalizationContext() - const { - currencies, - currencyAmounts, - currencyAmountsUSDValue, - exactAmountToken, - exactCurrencyField, - } = acceptedDerivedSwapInfo + const { currencies, currencyAmounts, currencyAmountsUSDValue, exactAmountToken, exactCurrencyField } = + acceptedDerivedSwapInfo const currencyInInfo = currencies[CurrencyField.INPUT] const currencyOutInfo = currencies[CurrencyField.OUTPUT] @@ -45,17 +39,10 @@ export function TransactionAmountsReview({ ? currencyAmountsUSDValue[CurrencyField.OUTPUT]?.toExact() : acceptedDerivedSwapInfo?.currencyAmountsUSDValue[CurrencyField.OUTPUT]?.toExact() - const formattedFiatAmountIn = convertFiatAmountFormatted( - usdAmountIn, - NumberType.FiatTokenQuantity - ) - const formattedFiatAmountOut = convertFiatAmountFormatted( - usdAmountOut, - NumberType.FiatTokenQuantity - ) + const formattedFiatAmountIn = convertFiatAmountFormatted(usdAmountIn, NumberType.FiatTokenQuantity) + const formattedFiatAmountOut = convertFiatAmountFormatted(usdAmountOut, NumberType.FiatTokenQuantity) - const derivedCurrencyField = - exactCurrencyField === CurrencyField.INPUT ? CurrencyField.OUTPUT : CurrencyField.INPUT + const derivedCurrencyField = exactCurrencyField === CurrencyField.INPUT ? CurrencyField.OUTPUT : CurrencyField.INPUT const derivedAmount = formatCurrencyAmount({ value: acceptedDerivedSwapInfo?.currencyAmounts[derivedCurrencyField], @@ -84,9 +71,7 @@ export function TransactionAmountsReview({ !acceptedDerivedSwapInfo.currencyAmounts[CurrencyField.OUTPUT] ) { // This should never happen. It's just to keep TS happy. - throw new Error( - 'Missing required props in `derivedSwapInfo` to render `TransactionAmountsReview` screen.' - ) + throw new Error('Missing required props in `derivedSwapInfo` to render `TransactionAmountsReview` screen.') } return ( diff --git a/packages/wallet/src/features/transactions/swap/TransactionModal.native.tsx b/packages/wallet/src/features/transactions/swap/TransactionModal.native.tsx index 2242e2e5b93..9f53a356b77 100644 --- a/packages/wallet/src/features/transactions/swap/TransactionModal.native.tsx +++ b/packages/wallet/src/features/transactions/swap/TransactionModal.native.tsx @@ -1,9 +1,4 @@ -import { - BottomSheetFooter, - BottomSheetView, - KEYBOARD_STATE, - useBottomSheetInternal, -} from '@gorhom/bottom-sheet' +import { BottomSheetFooter, BottomSheetView, KEYBOARD_STATE, useBottomSheetInternal } from '@gorhom/bottom-sheet' import { useMemo } from 'react' import { StyleProp, TouchableWithoutFeedback, ViewStyle } from 'react-native' import { @@ -53,7 +48,7 @@ export function TransactionModal({ animatedPosition.value, [0, insets.top], [0, borderRadii.rounded24], - Extrapolate.CLAMP + Extrapolate.CLAMP, ) return { borderTopLeftRadius: interpolatedRadius, borderTopRightRadius: interpolatedRadius } }) @@ -67,7 +62,7 @@ export function TransactionModal({ }, animatedBorderRadius, ], - [animatedBorderRadius, backgroundColorValue, fullContentHeight, fullscreen] + [animatedBorderRadius, backgroundColorValue, fullContentHeight, fullscreen], ) return ( @@ -81,11 +76,13 @@ export function TransactionModal({ fullScreen={fullscreen} hideHandlebar={fullscreen} name={modalName} - onClose={onClose}> + onClose={onClose} + > + {...transactionContextProps} + > {children} @@ -111,11 +108,7 @@ export function TransactionModalInnerContainer({ {fullscreen && } - + {children} @@ -126,9 +119,7 @@ export function TransactionModalInnerContainer({ ) } -export function TransactionModalFooterContainer({ - children, -}: TransactionModalFooterContainerProps): JSX.Element { +export function TransactionModalFooterContainer({ children }: TransactionModalFooterContainerProps): JSX.Element { const insets = useDeviceInsets() const colors = useSporeColors() @@ -164,12 +155,7 @@ export function TransactionModalFooterContainer({ return ( - + {children} {/* diff --git a/packages/wallet/src/features/transactions/swap/TransactionModal.tsx b/packages/wallet/src/features/transactions/swap/TransactionModal.tsx index dfc810a732c..98524a12694 100644 --- a/packages/wallet/src/features/transactions/swap/TransactionModal.tsx +++ b/packages/wallet/src/features/transactions/swap/TransactionModal.tsx @@ -8,14 +8,10 @@ export function TransactionModal(_: TransactionModalProps): JSX.Element { throw new Error('Implemented in `.native.tsx` and `.web.tsx` files') } -export function TransactionModalInnerContainer( - _: TransactionModalInnerContainerProps -): JSX.Element { +export function TransactionModalInnerContainer(_: TransactionModalInnerContainerProps): JSX.Element { throw new Error('Implemented in `.native.tsx` and `.web.tsx` files') } -export function TransactionModalFooterContainer( - _: TransactionModalFooterContainerProps -): JSX.Element { +export function TransactionModalFooterContainer(_: TransactionModalFooterContainerProps): JSX.Element { throw new Error('Implemented in `.native.tsx` and `.web.tsx` files') } diff --git a/packages/wallet/src/features/transactions/swap/TransactionModal.web.tsx b/packages/wallet/src/features/transactions/swap/TransactionModal.web.tsx index bebc31bcbf5..a5a635280b2 100644 --- a/packages/wallet/src/features/transactions/swap/TransactionModal.web.tsx +++ b/packages/wallet/src/features/transactions/swap/TransactionModal.web.tsx @@ -20,7 +20,8 @@ export function TransactionModal({ bottomSheetViewStyles={{}} openWalletRestoreModal={openWalletRestoreModal} walletNeedsRestore={walletNeedsRestore} - onClose={onClose}> + onClose={onClose} + > {children} @@ -34,9 +35,7 @@ export function TransactionModalInnerContainer({ return {children} } -export function TransactionModalFooterContainer({ - children, -}: TransactionModalFooterContainerProps): JSX.Element { +export function TransactionModalFooterContainer({ children }: TransactionModalFooterContainerProps): JSX.Element { return ( {children} diff --git a/packages/wallet/src/features/transactions/swap/analytics.ts b/packages/wallet/src/features/transactions/swap/analytics.ts index 9b0f8f7cfcb..b1746e14cfc 100644 --- a/packages/wallet/src/features/transactions/swap/analytics.ts +++ b/packages/wallet/src/features/transactions/swap/analytics.ts @@ -3,16 +3,13 @@ import { Currency, TradeType } from '@uniswap/sdk-core' import { useEffect } from 'react' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { SwapTradeBaseProperties } from 'uniswap/src/features/telemetry/types' +import { getCurrencyAddressForAnalytics } from 'uniswap/src/utils/currencyId' import { NumberType } from 'utilities/src/format/types' -import { - LocalizationContextState, - useLocalizationContext, -} from 'wallet/src/features/language/LocalizationContext' +import { LocalizationContextState, useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { getClassicQuoteFromResponse } from 'wallet/src/features/transactions/swap/trade/tradingApi/utils' import { Trade } from 'wallet/src/features/transactions/swap/trade/types' import { DerivedSwapInfo } from 'wallet/src/features/transactions/swap/types' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' -import { getCurrencyAddressForAnalytics } from 'wallet/src/utils/currencyId' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' // hook-based analytics because this one is data-lifecycle dependent @@ -29,10 +26,7 @@ export function useSwapAnalytics(derivedSwapInfo: DerivedSwapInfo): void { return } - sendAnalyticsEvent( - SwapEventName.SWAP_QUOTE_RECEIVED, - getBaseTradeAnalyticsProperties({ formatter, trade }) - ) + sendAnalyticsEvent(SwapEventName.SWAP_QUOTE_RECEIVED, getBaseTradeAnalyticsProperties({ formatter, trade })) // We only want to re-run this when we get a new `quoteId`. // eslint-disable-next-line react-hooks/exhaustive-deps }, [quoteId]) @@ -57,9 +51,7 @@ export function getBaseTradeAnalyticsProperties({ const quoteId = getClassicQuoteFromResponse(trade?.quote)?.quoteId - const finalOutputAmount = feeCurrencyAmount - ? trade.outputAmount.subtract(feeCurrencyAmount) - : trade.outputAmount + const finalOutputAmount = feeCurrencyAmount ? trade.outputAmount.subtract(feeCurrencyAmount) : trade.outputAmount return { token_in_symbol: trade.inputAmount.currency.symbol, @@ -92,12 +84,9 @@ export function getBaseTradeAnalyticsPropertiesFromSwapInfo({ const { chainId, currencyAmounts } = derivedSwapInfo const inputCurrencyAmount = currencyAmounts[CurrencyField.INPUT] const outputCurrencyAmount = currencyAmounts[CurrencyField.OUTPUT] - const slippageTolerance = - derivedSwapInfo.customSlippageTolerance ?? derivedSwapInfo.autoSlippageTolerance + const slippageTolerance = derivedSwapInfo.customSlippageTolerance ?? derivedSwapInfo.autoSlippageTolerance - const portionAmount = getClassicQuoteFromResponse( - derivedSwapInfo.trade?.trade?.quote - )?.portionAmount + const portionAmount = getClassicQuoteFromResponse(derivedSwapInfo.trade?.trade?.quote)?.portionAmount const feeCurrencyAmount = getCurrencyAmount({ value: portionAmount, @@ -106,22 +95,14 @@ export function getBaseTradeAnalyticsPropertiesFromSwapInfo({ }) const finalOutputAmount = - outputCurrencyAmount && feeCurrencyAmount - ? outputCurrencyAmount.subtract(feeCurrencyAmount) - : outputCurrencyAmount + outputCurrencyAmount && feeCurrencyAmount ? outputCurrencyAmount.subtract(feeCurrencyAmount) : outputCurrencyAmount return { token_in_symbol: inputCurrencyAmount?.currency.symbol, token_out_symbol: outputCurrencyAmount?.currency.symbol, - token_in_address: inputCurrencyAmount - ? getCurrencyAddressForAnalytics(inputCurrencyAmount?.currency) - : '', - token_out_address: outputCurrencyAmount - ? getCurrencyAddressForAnalytics(outputCurrencyAmount?.currency) - : '', - price_impact_basis_points: derivedSwapInfo.trade.trade?.priceImpact - .multiply(100) - .toSignificant(), + token_in_address: inputCurrencyAmount ? getCurrencyAddressForAnalytics(inputCurrencyAmount?.currency) : '', + token_out_address: outputCurrencyAmount ? getCurrencyAddressForAnalytics(outputCurrencyAmount?.currency) : '', + price_impact_basis_points: derivedSwapInfo.trade.trade?.priceImpact.multiply(100).toSignificant(), estimated_network_fee_usd: undefined, chain_id: chainId, token_in_amount: inputCurrencyAmount?.toExact() ?? '', diff --git a/packages/wallet/src/features/transactions/swap/createSwapFormFromTxDetails.ts b/packages/wallet/src/features/transactions/swap/createSwapFormFromTxDetails.ts index 7fdb98c1c5d..533a6cbaaf6 100644 --- a/packages/wallet/src/features/transactions/swap/createSwapFormFromTxDetails.ts +++ b/packages/wallet/src/features/transactions/swap/createSwapFormFromTxDetails.ts @@ -1,13 +1,10 @@ import { Currency, TradeType } from '@uniswap/sdk-core' +import { currencyAddress, currencyIdToAddress } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' import { AssetType, CurrencyAsset } from 'wallet/src/entities/assets' import { getAmountsFromTrade } from 'wallet/src/features/transactions/getAmountsFromTrade' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { TransactionDetails, TransactionType } from 'wallet/src/features/transactions/types' -import { currencyAddress, currencyIdToAddress } from 'wallet/src/utils/currencyId' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' interface Props { @@ -35,9 +32,7 @@ export function createSwapFormFromTxDetails({ const { typeInfo } = transactionDetails if (typeInfo.type !== TransactionType.Swap) { - throw new Error( - `Tx hash ${txHash} does not correspond to a swap tx. It is of type ${typeInfo.type}` - ) + throw new Error(`Tx hash ${txHash} does not correspond to a swap tx. It is of type ${typeInfo.type}`) } const { inputCurrencyAmountRaw, outputCurrencyAmountRaw } = getAmountsFromTrade(typeInfo) @@ -100,9 +95,7 @@ export function createWrapFormFromTxDetails({ const { typeInfo } = transactionDetails if (typeInfo.type !== TransactionType.Wrap) { - throw new Error( - `Tx hash ${txHash} does not correspond to a wrap tx. It is of type ${typeInfo.type}` - ) + throw new Error(`Tx hash ${txHash} does not correspond to a wrap tx. It is of type ${typeInfo.type}`) } const currencyAmountRaw = typeInfo.currencyAmountRaw diff --git a/packages/wallet/src/features/transactions/swap/customRpc.ts b/packages/wallet/src/features/transactions/swap/customRpc.ts index e21f965c336..838121a5a73 100644 --- a/packages/wallet/src/features/transactions/swap/customRpc.ts +++ b/packages/wallet/src/features/transactions/swap/customRpc.ts @@ -18,7 +18,5 @@ export function useShouldUseMEVBlocker(chainId: Maybe): boolean { const isSwapProtectionSettingEnabled = useSwapProtectionSetting() === SwapProtectionSetting.On const isMevBlockerSupportedOnChain = chainId ? isPrivateRpcSupportedOnChain(chainId) : false - return Boolean( - isMevBlockerFeatureEnabled && isSwapProtectionSettingEnabled && isMevBlockerSupportedOnChain - ) + return Boolean(isMevBlockerFeatureEnabled && isSwapProtectionSettingEnabled && isMevBlockerSupportedOnChain) } diff --git a/packages/wallet/src/features/transactions/swap/hooks/useExactOutputWillFail.ts b/packages/wallet/src/features/transactions/swap/hooks/useExactOutputWillFail.ts index 2938d4ddef6..deebdb42493 100644 --- a/packages/wallet/src/features/transactions/swap/hooks/useExactOutputWillFail.ts +++ b/packages/wallet/src/features/transactions/swap/hooks/useExactOutputWillFail.ts @@ -29,10 +29,10 @@ export function useExactOutputWillFail({ exactOutputWouldFailIfCurrenciesSwitched: boolean } { const { hasBuyTax: inputTokenHasBuyTax, hasSellTax: inputTokenHasSellTax } = hasTokenFee( - currencies[CurrencyField.INPUT] + currencies[CurrencyField.INPUT], ) const { hasBuyTax: outputTokenHasBuyTax, hasSellTax: outputTokenHasSellTax } = hasTokenFee( - currencies[CurrencyField.OUTPUT] + currencies[CurrencyField.OUTPUT], ) const exactOutputWillFail = inputTokenHasSellTax || outputTokenHasBuyTax const exactOutputWouldFailIfCurrenciesSwitched = inputTokenHasBuyTax || outputTokenHasSellTax diff --git a/packages/wallet/src/features/transactions/swap/hooks/useGasFeeHighRelativeToValue.ts b/packages/wallet/src/features/transactions/swap/hooks/useGasFeeHighRelativeToValue.ts index 90578135f12..1f89663522e 100644 --- a/packages/wallet/src/features/transactions/swap/hooks/useGasFeeHighRelativeToValue.ts +++ b/packages/wallet/src/features/transactions/swap/hooks/useGasFeeHighRelativeToValue.ts @@ -3,7 +3,7 @@ import { useMemo } from 'react' export function useGasFeeHighRelativeToValue( gasFeeUSD: string | undefined, - value: Maybe> + value: Maybe>, ): boolean { return useMemo(() => { if (!value) { diff --git a/packages/wallet/src/features/transactions/swap/hooks/useSwapPrefilledState.ts b/packages/wallet/src/features/transactions/swap/hooks/useSwapPrefilledState.ts index 536ac4abdb0..e06c6773d23 100644 --- a/packages/wallet/src/features/transactions/swap/hooks/useSwapPrefilledState.ts +++ b/packages/wallet/src/features/transactions/swap/hooks/useSwapPrefilledState.ts @@ -1,6 +1,7 @@ import { useMemo } from 'react' +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { WalletChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { AssetType, CurrencyAsset } from 'wallet/src/entities/assets' import { SwapFormState } from 'wallet/src/features/transactions/contexts/SwapFormContext' import { @@ -8,11 +9,8 @@ import { TradeProtocolPreference, TransactionState, } from 'wallet/src/features/transactions/transactionState/types' -import { areAddressesEqual } from 'wallet/src/utils/addresses' -export function useSwapPrefilledState( - initialState: TransactionState | undefined -): SwapFormState | undefined { +export function useSwapPrefilledState(initialState: TransactionState | undefined): SwapFormState | undefined { const swapPrefilledState = useMemo( (): SwapFormState | undefined => initialState @@ -31,7 +29,7 @@ export function useSwapPrefilledState( tradeProtocolPreference: TradeProtocolPreference.Default, } : undefined, - [initialState] + [initialState], ) return swapPrefilledState diff --git a/packages/wallet/src/features/transactions/swap/modals/FeeOnTransferWarning.tsx b/packages/wallet/src/features/transactions/swap/modals/FeeOnTransferWarning.tsx index 6162bc6955e..04b8a7116b8 100644 --- a/packages/wallet/src/features/transactions/swap/modals/FeeOnTransferWarning.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/FeeOnTransferWarning.tsx @@ -43,7 +43,8 @@ export function FeeOnTransferWarning({ children }: PropsWithChildren): JSX.Eleme text: caption, title, placement: 'top', - }}> + }} + > {children} ) diff --git a/packages/wallet/src/features/transactions/swap/modals/NetworkFeeWarning.tsx b/packages/wallet/src/features/transactions/swap/modals/NetworkFeeWarning.tsx index 7894f826e0e..5071ab4a2a8 100644 --- a/packages/wallet/src/features/transactions/swap/modals/NetworkFeeWarning.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/NetworkFeeWarning.tsx @@ -38,12 +38,7 @@ export function NetworkFeeWarning({ backgroundIconColor: colors.surface2.get(), caption: text, closeText: t('common.button.close'), - icon: ( - - ), + icon: , modalName: ModalName.NetworkFeeInfo, severity: WarningSeverity.None, title: t('transaction.networkCost.label'), @@ -53,7 +48,8 @@ export function NetworkFeeWarning({ placement, icon: gasFeeHighRelativeToValue ? : null, }} - trigger={tooltipTrigger}> + trigger={tooltipTrigger} + > {children} ) diff --git a/packages/wallet/src/features/transactions/swap/modals/PriceImpactWarning.tsx b/packages/wallet/src/features/transactions/swap/modals/PriceImpactWarning.tsx index f929f104398..0587cd1e3c8 100644 --- a/packages/wallet/src/features/transactions/swap/modals/PriceImpactWarning.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/PriceImpactWarning.tsx @@ -5,10 +5,7 @@ import { ModalName } from 'uniswap/src/features/telemetry/constants' import { WarningInfo } from 'wallet/src/components/modals/WarningModal/WarningInfo' import { Warning } from 'wallet/src/features/transactions/WarningModal/types' -export function PriceImpactWarning({ - children, - warning, -}: PropsWithChildren<{ warning: Warning }>): JSX.Element { +export function PriceImpactWarning({ children, warning }: PropsWithChildren<{ warning: Warning }>): JSX.Element { const { t } = useTranslation() const caption = warning.message diff --git a/packages/wallet/src/features/transactions/swap/modals/SlippageInfoModal.tsx b/packages/wallet/src/features/transactions/swap/modals/SlippageInfoModal.tsx index 8598bb36cf7..88de1c67313 100644 --- a/packages/wallet/src/features/transactions/swap/modals/SlippageInfoModal.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/SlippageInfoModal.tsx @@ -41,15 +41,10 @@ export function SlippageInfoModal({ type: NumberType.TokenTx, }) const tokenSymbol = - trade.tradeType === TradeType.EXACT_INPUT - ? trade.outputAmount.currency.symbol - : trade.inputAmount.currency.symbol + trade.tradeType === TradeType.EXACT_INPUT ? trade.outputAmount.currency.symbol : trade.inputAmount.currency.symbol return ( - + @@ -68,26 +63,21 @@ export function SlippageInfoModal({ gap="$spacing8" px="$spacing16" py="$spacing12" - width="100%"> + width="100%" + > {t('swap.settings.slippage.control.title')} {!isCustomSlippage ? ( - + {t('swap.settings.slippage.control.auto')} ) : null} - + {formatPercent(slippageTolerance)} diff --git a/packages/wallet/src/features/transactions/swap/modals/SwapFeeWarning.tsx b/packages/wallet/src/features/transactions/swap/modals/SwapFeeWarning.tsx index 08c15cfc40e..b797693e996 100644 --- a/packages/wallet/src/features/transactions/swap/modals/SwapFeeWarning.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/SwapFeeWarning.tsx @@ -7,10 +7,7 @@ import { WarningInfo } from 'wallet/src/components/modals/WarningModal/WarningIn import { WarningSeverity } from 'wallet/src/features/transactions/WarningModal/types' import { openUri } from 'wallet/src/utils/linking' -export function SwapFeeWarning({ - noFee, - children, -}: PropsWithChildren<{ noFee: boolean }>): JSX.Element { +export function SwapFeeWarning({ noFee, children }: PropsWithChildren<{ noFee: boolean }>): JSX.Element { const colors = useSporeColors() const { t } = useTranslation() @@ -18,9 +15,7 @@ export function SwapFeeWarning({ await openUri(uniswapUrls.helpArticleUrls.swapFeeInfo) } - const caption = noFee - ? t('swap.warning.uniswapFee.message.default') - : t('swap.warning.uniswapFee.message.included') + const caption = noFee ? t('swap.warning.uniswapFee.message.default') : t('swap.warning.uniswapFee.message.included') return ( + tooltipProps={{ text: caption, placement: 'top' }} + > {children} ) diff --git a/packages/wallet/src/features/transactions/swap/modals/SwapProtectionModal.tsx b/packages/wallet/src/features/transactions/swap/modals/SwapProtectionModal.tsx index 41488f81fe5..ebb1678da19 100644 --- a/packages/wallet/src/features/transactions/swap/modals/SwapProtectionModal.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/SwapProtectionModal.tsx @@ -18,7 +18,8 @@ export function SwapProtectionInfoModal({ onClose }: { onClose: () => void }): J icon={} modalName={ModalName.SwapProtection} title={t('swap.settings.protection.title')} - onClose={onClose}> + onClose={onClose} + > ) diff --git a/packages/wallet/src/features/transactions/swap/modals/UniswapXInfoModal.tsx b/packages/wallet/src/features/transactions/swap/modals/UniswapXInfoModal.tsx index 20eac89d0fe..791520b19bb 100644 --- a/packages/wallet/src/features/transactions/swap/modals/UniswapXInfoModal.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/UniswapXInfoModal.tsx @@ -17,10 +17,9 @@ export function UniswapXInfoModal({ onClose }: { onClose: () => void }): JSX.Ele closeText={t('common.button.close')} icon={} modalName={ModalName.UniswapXInfo} - title={ - {t('uniswapx.label')} - } - onClose={onClose}> + title={{t('uniswapx.label')}} + onClose={onClose} + > ) diff --git a/packages/wallet/src/features/transactions/swap/modals/settings/ProtocolPreferenceScreen.tsx b/packages/wallet/src/features/transactions/swap/modals/settings/ProtocolPreferenceScreen.tsx index 66ba1817eb9..d47dbf89d72 100644 --- a/packages/wallet/src/features/transactions/swap/modals/settings/ProtocolPreferenceScreen.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/settings/ProtocolPreferenceScreen.tsx @@ -46,10 +46,7 @@ export function ProtocolPreferenceScreen({ ) } -export function getTitleFromProtocolPreference( - preference: TradeProtocolPreference, - t: TFunction -): string { +export function getTitleFromProtocolPreference(preference: TradeProtocolPreference, t: TFunction): string { switch (preference) { case TradeProtocolPreference.Default: return t('swap.settings.routingPreference.option.default.title') @@ -79,10 +76,12 @@ function OptionRow({ {title} - {description && ( + {typeof description === 'string' ? ( {description} + ) : ( + description )} {/* Only log this event if toggle value is off, and then turned on */} @@ -94,14 +93,10 @@ function OptionRow({ borderRadius="$roundedFull" borderWidth="$spacing2" height="$spacing24" - width="$spacing24"> + width="$spacing24" + > {active && ( - + )} @@ -126,12 +121,7 @@ function DefaultOptionDescription(): JSX.Element { - ), + icon: , gradient: , info: ( @@ -35,12 +31,7 @@ export function SlippageSettingsRow({ {formatPercent(currentSlippage)} - + diff --git a/packages/wallet/src/features/transactions/swap/modals/settings/SlippageSettingsRow.web.tsx b/packages/wallet/src/features/transactions/swap/modals/settings/SlippageSettingsRow.web.tsx index 85f7f77016d..e36ea0d6104 100644 --- a/packages/wallet/src/features/transactions/swap/modals/settings/SlippageSettingsRow.web.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/settings/SlippageSettingsRow.web.tsx @@ -7,10 +7,7 @@ import { useSlippageSettings } from 'wallet/src/features/transactions/swap/modal const INPUT_MIN_WIDTH = 44 -export function SlippageSettingsRow({ - derivedSwapInfo, - onSlippageChange, -}: SlippageSettingsRowProps): JSX.Element { +export function SlippageSettingsRow({ derivedSwapInfo, onSlippageChange }: SlippageSettingsRowProps): JSX.Element { const { t } = useTranslation() const [inputWidth, setInputWidth] = useState(0) @@ -31,9 +28,7 @@ export function SlippageSettingsRow({ } const backgroundColor = isEditingSlippage ? '$surface2' : '$surface1' - const inputValue = autoSlippageEnabled - ? autoSlippageTolerance.toFixed(2).toString() - : inputSlippageTolerance + const inputValue = autoSlippageEnabled ? autoSlippageTolerance.toFixed(2).toString() : inputSlippageTolerance return ( @@ -49,13 +44,15 @@ export function SlippageSettingsRow({ borderWidth={1} gap="$spacing8" p="$spacing4" - style={inputAnimatedStyle}> + style={inputAnimatedStyle} + > + onPress={onPressAutoSlippage} + > {t('swap.settings.slippage.control.auto')} @@ -85,7 +82,8 @@ export function SlippageSettingsRow({ style={{ position: 'absolute' }} variant="subheading2" zIndex={-1} - onLayout={onInputTextLayout}> + onLayout={onInputTextLayout} + > {inputValue} diff --git a/packages/wallet/src/features/transactions/swap/modals/settings/SlippageSettingsScreen.tsx b/packages/wallet/src/features/transactions/swap/modals/settings/SlippageSettingsScreen.tsx index 28141a27fa2..b8077168e5c 100644 --- a/packages/wallet/src/features/transactions/swap/modals/settings/SlippageSettingsScreen.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/settings/SlippageSettingsScreen.tsx @@ -64,7 +64,8 @@ export function SlippageSettingsScreen({ borderWidth={1} gap="$spacing12" p="$spacing16" - style={inputAnimatedStyle}> + style={inputAnimatedStyle} + > {t('swap.settings.slippage.control.auto')} @@ -82,11 +83,7 @@ export function SlippageSettingsScreen({ }), }} textAlign="center" - value={ - autoSlippageEnabled - ? autoSlippageTolerance.toFixed(2).toString() - : inputSlippageTolerance - } + value={autoSlippageEnabled ? autoSlippageTolerance.toFixed(2).toString() : inputSlippageTolerance} onBlur={onBlurSlippageInput} onChangeText={onChangeSlippageInput} onFocus={onFocusSlippageInput} diff --git a/packages/wallet/src/features/transactions/swap/modals/settings/SwapSettingsMessage.tsx b/packages/wallet/src/features/transactions/swap/modals/settings/SwapSettingsMessage.tsx index e40a283f400..be461cc783f 100644 --- a/packages/wallet/src/features/transactions/swap/modals/settings/SwapSettingsMessage.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/settings/SwapSettingsMessage.tsx @@ -51,7 +51,7 @@ export function SwapSettingsMessage({ {getSymbolDisplayText( trade.tradeType === TradeType.EXACT_INPUT ? trade.outputAmount.currency.symbol - : trade.inputAmount.currency.symbol + : trade.inputAmount.currency.symbol, )} {showSlippageWarning ? ( diff --git a/packages/wallet/src/features/transactions/swap/modals/settings/SwapSettingsModal.tsx b/packages/wallet/src/features/transactions/swap/modals/settings/SwapSettingsModal.tsx index 1ce21fb2181..86d6f66b764 100644 --- a/packages/wallet/src/features/transactions/swap/modals/settings/SwapSettingsModal.tsx +++ b/packages/wallet/src/features/transactions/swap/modals/settings/SwapSettingsModal.tsx @@ -52,9 +52,7 @@ export function SwapSettingsModal({ const [view, setView] = useState(SwapSettingsModalView.Options) const { customSlippageTolerance } = derivedSwapInfo - const [customSlippageInput, setCustomSlippageInput] = useState( - customSlippageTolerance - ) + const [customSlippageInput, setCustomSlippageInput] = useState(customSlippageTolerance) const getTitle = (): string => { switch (view) { @@ -91,10 +89,7 @@ export function SwapSettingsModal({ ) case SwapSettingsModalView.Slippage: return ( - + ) case SwapSettingsModalView.RoutePreference: return ( @@ -121,11 +116,9 @@ export function SwapSettingsModal({ backgroundColor={colors.surface1.get()} isModalOpen={isOpen} name={ModalName.SwapSettings} - onClose={onSettingsClose}> - + onClose={onSettingsClose} + > + setView(SwapSettingsModalView.Options)}> {innerContent} - @@ -187,12 +175,12 @@ function SwapSettingsOptions({ <> - + {t('swap.settings.routingPreference.title')} - setView(SwapSettingsModalView.RoutePreference)}> - - + setView(SwapSettingsModalView.RoutePreference)}> + + {tradeProtocolPreferenceTitle} diff --git a/packages/wallet/src/features/transactions/swap/modals/settings/useSlippageSettings.ts b/packages/wallet/src/features/transactions/swap/modals/settings/useSlippageSettings.ts index e228985bf3d..cbf4e453b08 100644 --- a/packages/wallet/src/features/transactions/swap/modals/settings/useSlippageSettings.ts +++ b/packages/wallet/src/features/transactions/swap/modals/settings/useSlippageSettings.ts @@ -4,10 +4,7 @@ import { StyleProp, ViewStyle } from 'react-native' import { useAnimatedStyle, useSharedValue } from 'react-native-reanimated' import { HapticFeedback } from 'ui/src' import { PlusMinusButtonType } from 'wallet/src/components/buttons/PlusMinusButton' -import { - MAX_AUTO_SLIPPAGE_TOLERANCE, - MAX_CUSTOM_SLIPPAGE_TOLERANCE, -} from 'wallet/src/constants/transactions' +import { MAX_AUTO_SLIPPAGE_TOLERANCE, MAX_CUSTOM_SLIPPAGE_TOLERANCE } from 'wallet/src/constants/transactions' import { Trade } from 'wallet/src/features/transactions/swap/trade/types' import { DerivedSwapInfo } from 'wallet/src/features/transactions/swap/types' import { errorShakeAnimation } from 'wallet/src/utils/animations' @@ -48,7 +45,7 @@ export function useSlippageSettings({ const [isEditingSlippage, setIsEditingSlippage] = useState(false) const [autoSlippageEnabled, setAutoSlippageEnabled] = useState(!customSlippageTolerance) const [inputSlippageTolerance, setInputSlippageTolerance] = useState( - customSlippageTolerance?.toFixed(2)?.toString() ?? '' + customSlippageTolerance?.toFixed(2)?.toString() ?? '', ) const [inputWarning, setInputWarning] = useState() @@ -71,7 +68,7 @@ export function useSlippageSettings({ () => ({ transform: [{ translateX: inputShakeX.value }], }), - [inputShakeX] + [inputShakeX], ) const onPressAutoSlippage = (): void => { @@ -113,7 +110,7 @@ export function useSlippageSettings({ setInputWarning( t('swap.settings.slippage.warning.max', { maxSlippageTolerance: MAX_CUSTOM_SLIPPAGE_TOLERANCE, - }) + }), ) setInputSlippageTolerance('') } @@ -131,7 +128,7 @@ export function useSlippageSettings({ setInputSlippageTolerance(value) onSlippageChange(parsedValue) }, - [inputShakeX, onSlippageChange, t] + [inputShakeX, onSlippageChange, t], ) const onFocusSlippageInput = useCallback((): void => { @@ -164,8 +161,7 @@ export function useSlippageSettings({ } const newSlippage = - currentSlippageToleranceNum + - (type === PlusMinusButtonType.Plus ? SLIPPAGE_INCREMENT : -SLIPPAGE_INCREMENT) + currentSlippageToleranceNum + (type === PlusMinusButtonType.Plus ? SLIPPAGE_INCREMENT : -SLIPPAGE_INCREMENT) const constrainedNewSlippage = type === PlusMinusButtonType.Plus ? Math.min(newSlippage, MAX_CUSTOM_SLIPPAGE_TOLERANCE) @@ -180,7 +176,7 @@ export function useSlippageSettings({ setInputSlippageTolerance(constrainedNewSlippage.toFixed(2).toString()) onSlippageChange(constrainedNewSlippage) }, - [autoSlippageEnabled, currentSlippageToleranceNum, onSlippageChange, t] + [autoSlippageEnabled, currentSlippageToleranceNum, onSlippageChange, t], ) return { diff --git a/packages/wallet/src/features/transactions/swap/swapSaga.test.ts b/packages/wallet/src/features/transactions/swap/swapSaga.test.ts index 71d9a363d3a..17cce19628a 100644 --- a/packages/wallet/src/features/transactions/swap/swapSaga.test.ts +++ b/packages/wallet/src/features/transactions/swap/swapSaga.test.ts @@ -6,6 +6,7 @@ import { UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk' import { expectSaga } from 'redux-saga-test-plan' import { EffectProviders, StaticProvider } from 'redux-saga-test-plan/providers' import { UniverseChainId } from 'uniswap/src/types/chains' +import { currencyId } from 'uniswap/src/utils/currencyId' import { DAI } from 'wallet/src/constants/tokens' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { sendTransaction } from 'wallet/src/features/transactions/sendTransactionSaga' @@ -21,7 +22,6 @@ import { selectWalletSwapProtectionSetting } from 'wallet/src/features/wallet/se import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice' import { signerMnemonicAccount } from 'wallet/src/test/fixtures' import { getTxProvidersMocks } from 'wallet/src/test/mocks' -import { currencyId } from 'wallet/src/utils/currencyId' const account = signerMnemonicAccount() diff --git a/packages/wallet/src/features/transactions/swap/swapSaga.ts b/packages/wallet/src/features/transactions/swap/swapSaga.ts index e665278e94e..e2c2627eb1b 100644 --- a/packages/wallet/src/features/transactions/swap/swapSaga.ts +++ b/packages/wallet/src/features/transactions/swap/swapSaga.ts @@ -6,18 +6,11 @@ import { RPCType } from 'uniswap/src/types/chains' import { logger } from 'utilities/src/logger/logger' import { isPrivateRpcSupportedOnChain } from 'wallet/src/features/providers' import { makeSelectAddressTransactions } from 'wallet/src/features/transactions/selectors' -import { - SendTransactionParams, - sendTransaction, -} from 'wallet/src/features/transactions/sendTransactionSaga' +import { SendTransactionParams, sendTransaction } from 'wallet/src/features/transactions/sendTransactionSaga' import { getBaseTradeAnalyticsProperties } from 'wallet/src/features/transactions/swap/analytics' import { isClassic } from 'wallet/src/features/transactions/swap/trade/utils' import { tradeToTransactionInfo } from 'wallet/src/features/transactions/swap/utils' -import { - TransactionStatus, - TransactionType, - TransactionTypeInfo, -} from 'wallet/src/features/transactions/types' +import { TransactionStatus, TransactionType, TransactionTypeInfo } from 'wallet/src/features/transactions/types' import { Account } from 'wallet/src/features/wallet/accounts/types' import { getProvider } from 'wallet/src/features/wallet/context' import { selectWalletSwapProtectionSetting } from 'wallet/src/features/wallet/selectors' @@ -41,12 +34,7 @@ export function* approveAndSwap(params: SwapParams) { } const { chainId } = swapTxRequest const submitViaPrivateRpc = yield* call(shouldSubmitViaPrivateRpc, chainId) - const nonce = yield* call( - getNonceForApproveAndSwap, - account.address, - chainId, - submitViaPrivateRpc - ) + const nonce = yield* call(getNonceForApproveAndSwap, account.address, chainId, submitViaPrivateRpc) if (approveTxRequest && approveTxRequest.to) { const typeInfo: TransactionTypeInfo = { @@ -96,11 +84,7 @@ export const { actions: swapActions, } = createMonitoredSaga(approveAndSwap, 'swap') -function* getNonceForApproveAndSwap( - address: Address, - chainId: number, - submitViaPrivateRpc: boolean -) { +function* getNonceForApproveAndSwap(address: Address, chainId: number, submitViaPrivateRpc: boolean) { const rpcType = submitViaPrivateRpc ? RPCType.Private : RPCType.Public const provider = yield* call(getProvider, chainId, rpcType) const nonce = yield* call([provider, provider.getTransactionCount], address, 'pending') @@ -136,6 +120,6 @@ function* getPendingPrivateTxCount(address: Address, chainId: number) { tx.chainId === chainId && tx.status === TransactionStatus.Pending && isClassic(tx) && - Boolean(tx.options.submitViaPrivateRpc) + Boolean(tx.options.submitViaPrivateRpc), ).length } diff --git a/packages/wallet/src/features/transactions/swap/trade/hooks/useDerivedSwapInfo.ts b/packages/wallet/src/features/transactions/swap/trade/hooks/useDerivedSwapInfo.ts index 72a64f2bb23..b5f1da6f507 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useDerivedSwapInfo.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useDerivedSwapInfo.ts @@ -3,6 +3,7 @@ import { useMemo } from 'react' import { FeatureFlags } from 'uniswap/src/features/gating/flags' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { UniverseChainId } from 'uniswap/src/types/chains' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { useOnChainCurrencyBalance } from 'wallet/src/features/portfolio/api' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { useSetTradeSlippage } from 'wallet/src/features/transactions/swap/trade/hooks/useSetTradeSlippage' @@ -10,12 +11,8 @@ import { useUSDCValue } from 'wallet/src/features/transactions/swap/trade/hooks/ import { useTradingApiTrade } from 'wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTradingApiTrade' import { DerivedSwapInfo } from 'wallet/src/features/transactions/swap/types' import { getWrapType, isWrapAction } from 'wallet/src/features/transactions/swap/utils' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { useActiveAccount } from 'wallet/src/features/wallet/hooks' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' /** Returns information derived from the current swap state */ @@ -36,13 +33,11 @@ export function useDerivedSwapInfo(state: TransactionState): DerivedSwapInfo { const activeAccount = useActiveAccount() const currencyInInfo = useCurrencyInfo( - currencyAssetIn ? buildCurrencyId(currencyAssetIn.chainId, currencyAssetIn.address) : undefined + currencyAssetIn ? buildCurrencyId(currencyAssetIn.chainId, currencyAssetIn.address) : undefined, ) const currencyOutInfo = useCurrencyInfo( - currencyAssetOut - ? buildCurrencyId(currencyAssetOut.chainId, currencyAssetOut.address) - : undefined + currencyAssetOut ? buildCurrencyId(currencyAssetOut.chainId, currencyAssetOut.address) : undefined, ) const currencies = useMemo(() => { @@ -58,10 +53,7 @@ export function useDerivedSwapInfo(state: TransactionState): DerivedSwapInfo { const chainId = currencyIn?.chainId ?? currencyOut?.chainId ?? UniverseChainId.Mainnet const { balance: tokenInBalance } = useOnChainCurrencyBalance(currencyIn, activeAccount?.address) - const { balance: tokenOutBalance } = useOnChainCurrencyBalance( - currencyOut, - activeAccount?.address - ) + const { balance: tokenOutBalance } = useOnChainCurrencyBalance(currencyOut, activeAccount?.address) const isExactIn = exactCurrencyField === CurrencyField.INPUT const wrapType = getWrapType(currencyIn, currencyOut) @@ -95,35 +87,22 @@ export function useDerivedSwapInfo(state: TransactionState): DerivedSwapInfo { const tradingApiTrade = useTradingApiTrade(tradeParams) // Calculate auto slippage tolerance for trade. If customSlippageTolerance is undefined, then the Trade slippage is set to the calculated value. - const { trade, autoSlippageTolerance } = useSetTradeSlippage( - tradingApiTrade, - customSlippageTolerance - ) + const { trade, autoSlippageTolerance } = useSetTradeSlippage(tradingApiTrade, customSlippageTolerance) const currencyAmounts = useMemo( () => shouldGetQuote ? { [CurrencyField.INPUT]: - exactCurrencyField === CurrencyField.INPUT - ? amountSpecified - : trade.trade?.inputAmount, + exactCurrencyField === CurrencyField.INPUT ? amountSpecified : trade.trade?.inputAmount, [CurrencyField.OUTPUT]: - exactCurrencyField === CurrencyField.OUTPUT - ? amountSpecified - : trade.trade?.outputAmount, + exactCurrencyField === CurrencyField.OUTPUT ? amountSpecified : trade.trade?.outputAmount, } : { [CurrencyField.INPUT]: amountSpecified, [CurrencyField.OUTPUT]: amountSpecified, }, - [ - shouldGetQuote, - exactCurrencyField, - amountSpecified, - trade.trade?.inputAmount, - trade.trade?.outputAmount, - ] + [shouldGetQuote, exactCurrencyField, amountSpecified, trade.trade?.inputAmount, trade.trade?.outputAmount], ) const inputCurrencyUSDValue = useUSDCValue(currencyAmounts[CurrencyField.INPUT]) diff --git a/packages/wallet/src/features/transactions/swap/trade/hooks/useSetTradeSlippage.ts b/packages/wallet/src/features/transactions/swap/trade/hooks/useSetTradeSlippage.ts index 6c5f5f37896..71e6e846ffa 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useSetTradeSlippage.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useSetTradeSlippage.ts @@ -1,11 +1,8 @@ import { useMemo } from 'react' +import { isL2Chain, toSupportedChainId } from 'uniswap/src/features/chains/utils' import { DynamicConfigs } from 'uniswap/src/features/gating/configs' import { useDynamicConfig } from 'uniswap/src/features/gating/hooks' -import { - MAX_AUTO_SLIPPAGE_TOLERANCE, - MIN_AUTO_SLIPPAGE_TOLERANCE, -} from 'wallet/src/constants/transactions' -import { isL2Chain, toSupportedChainId } from 'wallet/src/features/chains/utils' +import { MAX_AUTO_SLIPPAGE_TOLERANCE, MIN_AUTO_SLIPPAGE_TOLERANCE } from 'wallet/src/constants/transactions' import { useUSDCValue } from 'wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice' import { getClassicQuoteFromResponse, @@ -15,7 +12,7 @@ import { Trade, TradeWithStatus } from 'wallet/src/features/transactions/swap/tr export function useSetTradeSlippage( trade: TradeWithStatus, - userSetSlippage?: number + userSetSlippage?: number, ): { trade: TradeWithStatus; autoSlippageTolerance: number } { // Always calculate and return autoSlippageTolerance so the UI can warn user when custom slippage is set higher than auto slippage const autoSlippageTolerance = useCalculateAutoSlippage(trade?.trade) @@ -66,9 +63,7 @@ export function useSetTradeSlippage( function useCalculateAutoSlippage(trade: Maybe): number { const outputAmountUSD = useUSDCValue(trade?.outputAmount)?.toExact() - const minAutoSlippageToleranceL2 = useSlippageValueFromDynamicConfig( - SlippageConfigName.MinAutoSlippageToleranceL2 - ) + const minAutoSlippageToleranceL2 = useSlippageValueFromDynamicConfig(SlippageConfigName.MinAutoSlippageToleranceL2) return useMemo(() => { const quote = getClassicQuoteFromResponse(trade?.quote) diff --git a/packages/wallet/src/features/transactions/swap/trade/hooks/useShowSwapNetworkNotification.ts b/packages/wallet/src/features/transactions/swap/trade/hooks/useShowSwapNetworkNotification.ts index 0d36bfbb04d..077fd4db4c1 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useShowSwapNetworkNotification.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useShowSwapNetworkNotification.ts @@ -20,7 +20,7 @@ export function useShowSwapNetworkNotification(chainId?: WalletChainId): void { chainId, flow: 'swap', hideDelay: 2000, - }) + }), ) }, [chainId, prevChainId, appDispatch]) } diff --git a/packages/wallet/src/features/transactions/swap/trade/hooks/useSwapCallback.ts b/packages/wallet/src/features/transactions/swap/trade/hooks/useSwapCallback.ts index d2836cd9c0f..0b0274d4a94 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useSwapCallback.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useSwapCallback.ts @@ -31,7 +31,7 @@ export function useSwapCallback( txId?: string, isHoldToSwap?: boolean, - isFiatInputMode?: boolean + isFiatInputMode?: boolean, ): () => void { const appDispatch = useAppDispatch() const account = useActiveAccount() @@ -70,18 +70,12 @@ export function useSwapCallback( ...getBaseTradeAnalyticsProperties({ formatter, trade }), estimated_network_fee_wei: gasFee.value, gas_limit: toStringish(swapTxRequest.gasLimit), - token_in_amount_usd: currencyInAmountUSD - ? parseFloat(currencyInAmountUSD.toFixed(2)) - : undefined, - token_out_amount_usd: currencyOutAmountUSD - ? parseFloat(currencyOutAmountUSD.toFixed(2)) - : undefined, + token_in_amount_usd: currencyInAmountUSD ? parseFloat(currencyInAmountUSD.toFixed(2)) : undefined, + token_out_amount_usd: currencyOutAmountUSD ? parseFloat(currencyOutAmountUSD.toFixed(2)) : undefined, transaction_deadline_seconds: trade.deadline, swap_quote_block_number: blockNumber, is_auto_slippage: isAutoSlippage, - swap_flow_duration_milliseconds: swapStartTimestamp - ? Date.now() - swapStartTimestamp - : undefined, + swap_flow_duration_milliseconds: swapStartTimestamp ? Date.now() - swapStartTimestamp : undefined, is_hold_to_swap: isHoldToSwap, is_fiat_input_mode: isFiatInputMode, }) diff --git a/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice.ts b/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice.ts index 0f452a4e966..cf980377e5e 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice.ts @@ -1,7 +1,8 @@ import { Currency, CurrencyAmount, Price, Token, TradeType } from '@uniswap/sdk-core' import { useMemo } from 'react' +import { PollingInterval } from 'uniswap/src/constants/misc' import { UniverseChainId } from 'uniswap/src/types/chains' -import { PollingInterval } from 'wallet/src/constants/misc' +import { areCurrencyIdsEqual, currencyId } from 'uniswap/src/utils/currencyId' import { CUSD, USDB, @@ -18,7 +19,6 @@ import { } from 'wallet/src/constants/tokens' import { useTradingApiTrade } from 'wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTradingApiTrade' import { isClassic } from 'wallet/src/features/transactions/swap/trade/utils' -import { areCurrencyIdsEqual, currencyId } from 'wallet/src/utils/currencyId' // Stablecoin amounts used when calculating spot price for a given currency. // The amount is large enough to filter low liquidity pairs. @@ -49,7 +49,7 @@ export function useUSDCPrice(currency?: Currency): Price | u // avoid requesting quotes for stablecoin input const currencyIsStablecoin = Boolean( - stablecoin && currency && areCurrencyIdsEqual(currencyId(currency), currencyId(stablecoin)) + stablecoin && currency && areCurrencyIdsEqual(currencyId(currency), currencyId(stablecoin)), ) const amountSpecified = currencyIsStablecoin ? undefined : quoteAmount @@ -81,7 +81,7 @@ export function useUSDCPrice(currency?: Currency): Price | u } export function useUSDCValue( - currencyAmount: CurrencyAmount | undefined | null + currencyAmount: CurrencyAmount | undefined | null, ): CurrencyAmount | null { const price = useUSDCPrice(currencyAmount?.currency) diff --git a/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDTokenUpdater.ts b/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDTokenUpdater.ts index 0b130e603bf..0ed4d9902aa 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDTokenUpdater.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDTokenUpdater.ts @@ -3,10 +3,7 @@ import React, { useEffect, useRef } from 'react' import { AnyAction } from 'redux' import { NumberType } from 'utilities/src/format/types' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' -import { - STABLECOIN_AMOUNT_OUT, - useUSDCPrice, -} from 'wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice' +import { STABLECOIN_AMOUNT_OUT, useUSDCPrice } from 'wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice' import { updateExactAmountFiat, updateExactAmountToken, @@ -21,12 +18,12 @@ export function useUSDTokenUpdater( isFiatInput: boolean, exactAmountToken: string, exactAmountFiat: string, - exactCurrency?: Currency + exactCurrency?: Currency, ): void { const price = useUSDCPrice(exactCurrency) const shouldUseUSDRef = useRef(isFiatInput) const { convertFiatAmount, formatCurrencyAmount } = useLocalizationContext() - const conversionRate = convertFiatAmount().amount + const conversionRate = convertFiatAmount(1).amount useEffect(() => { shouldUseUSDRef.current = isFiatInput @@ -55,7 +52,7 @@ export function useUSDTokenUpdater( type: NumberType.SwapTradeAmount, placeholder: '', }), - }) + }), ) } @@ -67,9 +64,7 @@ export function useUSDTokenUpdater( const usdPrice = exactCurrencyAmount ? price?.quote(exactCurrencyAmount) : undefined const fiatPrice = parseFloat(usdPrice?.toExact() ?? '0') * conversionRate - return dispatch( - updateExactAmountFiat({ amount: fiatPrice ? fiatPrice.toFixed(NUM_DECIMALS_DISPLAY) : '' }) - ) + return dispatch(updateExactAmountFiat({ amount: fiatPrice ? fiatPrice.toFixed(NUM_DECIMALS_DISPLAY) : '' })) }, [ dispatch, shouldUseUSDRef, diff --git a/packages/wallet/src/features/transactions/swap/trade/hooks/useWrapCallback.ts b/packages/wallet/src/features/transactions/swap/trade/hooks/useWrapCallback.ts index 6fc431f8318..8c73418f5a8 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useWrapCallback.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useWrapCallback.ts @@ -13,7 +13,7 @@ export function useWrapCallback( wrapType: WrapType, onSuccess: () => void, txRequest?: providers.TransactionRequest, - txId?: string + txId?: string, ): { wrapCallback: () => void } { diff --git a/packages/wallet/src/features/transactions/swap/trade/hooks/useWrapTransactionRequest.ts b/packages/wallet/src/features/transactions/swap/trade/hooks/useWrapTransactionRequest.ts index e314fb9a535..2dc61138f84 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useWrapTransactionRequest.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useWrapTransactionRequest.ts @@ -10,9 +10,7 @@ import { WrapType } from 'wallet/src/features/transactions/types' import { useProvider } from 'wallet/src/features/wallet/context' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' -export function useWrapTransactionRequest( - derivedSwapInfo: DerivedSwapInfo -): providers.TransactionRequest | undefined { +export function useWrapTransactionRequest(derivedSwapInfo: DerivedSwapInfo): providers.TransactionRequest | undefined { const address = useActiveAccountAddressWithThrow() const { chainId, wrapType, currencyAmounts } = derivedSwapInfo const provider = useProvider(chainId) @@ -22,13 +20,7 @@ export function useWrapTransactionRequest( return } - return getWrapTransactionRequest( - provider, - chainId, - address, - wrapType, - currencyAmounts[CurrencyField.INPUT] - ) + return getWrapTransactionRequest(provider, chainId, address, wrapType, currencyAmounts[CurrencyField.INPUT]) }, [address, chainId, wrapType, currencyAmounts, provider]) return useAsyncData(transactionFetcher).data @@ -39,7 +31,7 @@ const getWrapTransactionRequest = async ( chainId: WalletChainId, address: Address, wrapType: WrapType, - currencyAmountIn: Maybe> + currencyAmountIn: Maybe>, ): Promise => { if (!currencyAmountIn) { return @@ -51,9 +43,7 @@ const getWrapTransactionRequest = async ( ? await wethContract.populateTransaction.deposit({ value: `0x${currencyAmountIn.quotient.toString(16)}`, }) - : await wethContract.populateTransaction.withdraw( - `0x${currencyAmountIn.quotient.toString(16)}` - ) + : await wethContract.populateTransaction.withdraw(`0x${currencyAmountIn.quotient.toString(16)}`) return { ...wethTx, from: address, chainId } } diff --git a/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useSwapTxAndGasInfoTradingApi.ts b/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useSwapTxAndGasInfoTradingApi.ts index 0b9b30a2939..c62f6bb6a3c 100644 --- a/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useSwapTxAndGasInfoTradingApi.ts +++ b/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useSwapTxAndGasInfoTradingApi.ts @@ -37,8 +37,7 @@ export function useSwapTxAndGasInfoTradingApi({ const approvalError = tokenApprovalInfo?.action === ApprovalAction.Unknown const gasFeeError = transactionRequestInfo.gasFeeResult.error || approvalError - const areValuesReady = - tokenApprovalInfo && transactionRequestInfo.gasFeeResult.value !== undefined + const areValuesReady = tokenApprovalInfo && transactionRequestInfo.gasFeeResult.value !== undefined // Do not populate gas fee: // - If errors exist on swap or approval requests. diff --git a/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTokenApprovalInfo.ts b/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTokenApprovalInfo.ts index 8bd5cf1ae8c..d2ecada4a28 100644 --- a/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTokenApprovalInfo.ts +++ b/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTokenApprovalInfo.ts @@ -11,10 +11,7 @@ import { getTokenAddressForApi, toTradingApiSupportedChainId, } from 'wallet/src/features/transactions/swap/trade/tradingApi/utils' -import { - ApprovalAction, - TokenApprovalInfo, -} from 'wallet/src/features/transactions/swap/trade/types' +import { ApprovalAction, TokenApprovalInfo } from 'wallet/src/features/transactions/swap/trade/types' import { WrapType } from 'wallet/src/features/transactions/types' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' @@ -26,7 +23,7 @@ interface TokenApprovalInfoParams { } export function useTokenApprovalInfo( - params: TokenApprovalInfoParams + params: TokenApprovalInfoParams, ): (TokenApprovalInfo & { gasFee?: string }) | undefined { const { chainId, wrapType, currencyInAmount, skip } = params @@ -62,7 +59,7 @@ export function useTokenApprovalInfo( skip: skip || !approvalRequestArgs || isWrap, }, 'POST', - TradingApiApolloClient + TradingApiApolloClient, ) return useMemo(() => { diff --git a/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTradingApiTrade.ts b/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTradingApiTrade.ts index 3d8bde8f72c..dec997a3c22 100644 --- a/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTradingApiTrade.ts +++ b/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTradingApiTrade.ts @@ -1,20 +1,18 @@ import { ApolloError, NetworkStatus } from '@apollo/client' import { TradeType } from '@uniswap/sdk-core' import { useMemo } from 'react' +import { PollingInterval } from 'uniswap/src/constants/misc' import { uniswapUrls } from 'uniswap/src/constants/urls' import { useRestQuery } from 'uniswap/src/data/rest' +import { isL2Chain } from 'uniswap/src/features/chains/utils' import { FeatureFlags } from 'uniswap/src/features/gating/flags' import { useFeatureFlag } from 'uniswap/src/features/gating/hooks' import { WalletChainId } from 'uniswap/src/types/chains' +import { areCurrencyIdsEqual, currencyId } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' import { ONE_SECOND_MS, inXMinutesUnix } from 'utilities/src/time/time' import { useDebounceWithStatus } from 'utilities/src/time/timing' -import { PollingInterval } from 'wallet/src/constants/misc' -import { - QuoteRequest, - TradeType as TradingApiTradeType, -} from 'wallet/src/data/tradingApi/__generated__/index' -import { isL2Chain } from 'wallet/src/features/chains/utils' +import { QuoteRequest, TradeType as TradingApiTradeType } from 'wallet/src/data/tradingApi/__generated__/index' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { TradingApiApolloClient } from 'wallet/src/features/transactions/swap/trade/tradingApi/client' import { @@ -31,7 +29,6 @@ import { } from 'wallet/src/features/transactions/swap/trade/types' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' -import { areCurrencyIdsEqual, currencyId } from 'wallet/src/utils/currencyId' // error strings hardcoded in @uniswap/unified-routing-api // https://github.com/Uniswap/unified-routing-api/blob/020ea371a00d4cc25ce9f9906479b00a43c65f2c/lib/util/errors.ts#L4 @@ -65,36 +62,24 @@ export function useTradingApiTrade(args: UseTradeArgs): TradeWithStatus { /***** Format request arguments ******/ - const [debouncedAmountSpecified, isDebouncing] = useDebounceWithStatus( - amountSpecified, - SWAP_FORM_DEBOUNCE_TIME_MS - ) - const shouldDebounce = - amountSpecified && debouncedAmountSpecified?.currency.chainId === otherCurrency?.chainId + const [debouncedAmountSpecified, isDebouncing] = useDebounceWithStatus(amountSpecified, SWAP_FORM_DEBOUNCE_TIME_MS) + const shouldDebounce = amountSpecified && debouncedAmountSpecified?.currency.chainId === otherCurrency?.chainId const amount = shouldDebounce ? debouncedAmountSpecified : amountSpecified const currencyIn = tradeType === TradeType.EXACT_INPUT ? amount?.currency : otherCurrency const currencyOut = tradeType === TradeType.EXACT_OUTPUT ? amount?.currency : otherCurrency const currencyInEqualsCurrencyOut = - currencyIn && - currencyOut && - areCurrencyIdsEqual(currencyId(currencyIn), currencyId(currencyOut)) + currencyIn && currencyOut && areCurrencyIdsEqual(currencyId(currencyIn), currencyId(currencyOut)) const tokenInChainId = toTradingApiSupportedChainId(currencyIn?.chainId) const tokenOutChainId = toTradingApiSupportedChainId(currencyOut?.chainId) const tokenInAddress = getTokenAddressForApi(currencyIn) const tokenOutAddress = getTokenAddressForApi(currencyOut) - const routingPreference = getRoutingPreferenceForSwapRequest( - tradeProtocolPreference, - uniswapXEnabled, - isUSDQuote - ) + const routingPreference = getRoutingPreferenceForSwapRequest(tradeProtocolPreference, uniswapXEnabled, isUSDQuote) const requestTradeType = - tradeType === TradeType.EXACT_INPUT - ? TradingApiTradeType.EXACT_INPUT - : TradingApiTradeType.EXACT_OUTPUT + tradeType === TradeType.EXACT_INPUT ? TradingApiTradeType.EXACT_INPUT : TradingApiTradeType.EXACT_OUTPUT const skipQuery = skip || @@ -166,7 +151,7 @@ export function useTradingApiTrade(args: UseTradeArgs): TradeWithStatus { notifyOnNetworkStatusChange: true, }, 'POST', - TradingApiApolloClient + TradingApiApolloClient, ) const { error, data, loading, networkStatus } = response @@ -214,8 +199,7 @@ export function useTradingApiTrade(args: UseTradeArgs): TradeWithStatus { data, }) - const exactCurrencyField = - tradeType === TradeType.EXACT_INPUT ? CurrencyField.INPUT : CurrencyField.OUTPUT + const exactCurrencyField = tradeType === TradeType.EXACT_INPUT ? CurrencyField.INPUT : CurrencyField.OUTPUT const trade = validateTrade({ trade: formattedTrade, @@ -264,7 +248,5 @@ export function useTradingApiTrade(args: UseTradeArgs): TradeWithStatus { } function getPollIntervalByChain(chainId?: WalletChainId): number { - return isL2Chain(chainId) - ? PollingInterval.AverageL2BlockTime - : PollingInterval.AverageL1BlockTime + return isL2Chain(chainId) ? PollingInterval.AverageL2BlockTime : PollingInterval.AverageL1BlockTime } diff --git a/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTransactionRequestInfo.ts b/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTransactionRequestInfo.ts index 845711020db..55ea136078f 100644 --- a/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTransactionRequestInfo.ts +++ b/packages/wallet/src/features/transactions/swap/trade/tradingApi/hooks/useTransactionRequestInfo.ts @@ -18,10 +18,7 @@ import { getBaseTradeAnalyticsPropertiesFromSwapInfo } from 'wallet/src/features import { useWrapTransactionRequest } from 'wallet/src/features/transactions/swap/trade/hooks/useWrapTransactionRequest' import { TradingApiApolloClient } from 'wallet/src/features/transactions/swap/trade/tradingApi/client' import { getClassicQuoteFromResponse } from 'wallet/src/features/transactions/swap/trade/tradingApi/utils' -import { - ApprovalAction, - TokenApprovalInfo, -} from 'wallet/src/features/transactions/swap/trade/types' +import { ApprovalAction, TokenApprovalInfo } from 'wallet/src/features/transactions/swap/trade/types' import { DerivedSwapInfo } from 'wallet/src/features/transactions/swap/types' import { usePermit2SignatureWithData } from 'wallet/src/features/transactions/swap/usePermit2Signature' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' @@ -60,7 +57,7 @@ export function useTransactionRequestInfo({ const signatureInfo = usePermit2SignatureWithData( currencyAmounts[CurrencyField.INPUT], permitData, - /**skip=*/ !requiresPermit2Sig || skip + /**skip=*/ !requiresPermit2Sig || skip, ) /** @@ -113,10 +110,7 @@ export function useTransactionRequestInfo({ const skipTransactionRequest = !swapRequestArgs || isWrapApplicable || skip - const { data, error, loading } = useRestQuery< - CreateSwapResponse, - CreateSwapRequest | Record - >( + const { data, error, loading } = useRestQuery>( uniswapUrls.tradingApiPaths.swap, swapRequestArgs ?? {}, ['swap', 'gasFee', 'requestId', 'txFailureReasons'], @@ -127,7 +121,7 @@ export function useTransactionRequestInfo({ skip: skipTransactionRequest, }, 'POST', - TradingApiApolloClient + TradingApiApolloClient, ) // We use the gasFee estimate from quote, as its more accurate @@ -135,12 +129,10 @@ export function useTransactionRequestInfo({ const swapGasFee = swapQuote?.gasFee // This is a case where simulation fails on backend, meaning txn is expected to fail - const simulationError = swapQuote?.txFailureReasons?.includes( - TransactionFailureReason.SIMULATION_ERROR - ) + const simulationError = swapQuote?.txFailureReasons?.includes(TransactionFailureReason.SIMULATION_ERROR) const gasEstimateError = useMemo( () => (simulationError ? new Error(UNKNOWN_SIM_ERROR) : error), - [simulationError, error] + [simulationError, error], ) const gasFeeResult = { diff --git a/packages/wallet/src/features/transactions/swap/trade/tradingApi/utils.ts b/packages/wallet/src/features/transactions/swap/trade/tradingApi/utils.ts index 75045536c07..63b3425d8ab 100644 --- a/packages/wallet/src/features/transactions/swap/trade/tradingApi/utils.ts +++ b/packages/wallet/src/features/transactions/swap/trade/tradingApi/utils.ts @@ -4,6 +4,8 @@ import { UnsignedV2DutchOrderInfo } from '@uniswap/uniswapx-sdk' import { Pair, Route as V2Route } from '@uniswap/v2-sdk' import { FeeAmount, Pool, Route as V3Route } from '@uniswap/v3-sdk' import { BigNumber } from 'ethers' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' +import { currencyId } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' import { MAX_AUTO_SLIPPAGE_TOLERANCE } from 'wallet/src/constants/transactions' import { @@ -28,12 +30,7 @@ import { Trade, UniswapXTrade, } from 'wallet/src/features/transactions/swap/trade/types' -import { - CurrencyField, - TradeProtocolPreference, -} from 'wallet/src/features/transactions/transactionState/types' -import { areAddressesEqual } from 'wallet/src/utils/addresses' -import { currencyId } from 'wallet/src/utils/currencyId' +import { CurrencyField, TradeProtocolPreference } from 'wallet/src/features/transactions/transactionState/types' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' const NATIVE_ADDRESS_FOR_TRADING_API = '0x0000000000000000000000000000000000000000' @@ -47,9 +44,7 @@ interface TradingApiResponseToTradeArgs { data: DiscriminatedQuoteResponse | undefined } -export function transformTradingApiResponseToTrade( - params: TradingApiResponseToTradeArgs -): Trade | null { +export function transformTradingApiResponseToTrade(params: TradingApiResponseToTradeArgs): Trade | null { const { currencyIn, currencyOut, tradeType, deadline, slippageTolerance, data } = params switch (data?.routing) { @@ -66,8 +61,7 @@ export function transformTradingApiResponseToTrade( slippageTolerance: slippageTolerance ?? MAX_AUTO_SLIPPAGE_TOLERANCE, v2Routes: routes?.flatMap((r) => (r?.routev2 ? { ...r, routev2: r.routev2 } : [])) ?? [], v3Routes: routes?.flatMap((r) => (r?.routev3 ? { ...r, routev3: r.routev3 } : [])) ?? [], - mixedRoutes: - routes?.flatMap((r) => (r?.mixedRoute ? { ...r, mixedRoute: r.mixedRoute } : [])) ?? [], + mixedRoutes: routes?.flatMap((r) => (r?.mixedRoute ? { ...r, mixedRoute: r.mixedRoute } : [])) ?? [], tradeType, }) } @@ -108,7 +102,7 @@ export function getSwapFee(quoteResponse?: DiscriminatedQuoteResponse): SwapFee export function computeRoutesTradingApi( tokenInIsNative: boolean, tokenOutIsNative: boolean, - quoteResponse?: QuoteResponse + quoteResponse?: QuoteResponse, ): | { routev3: V3Route | null @@ -146,13 +140,9 @@ export function computeRoutesTradingApi( throw new Error('Expected all token properties to be present') } - const parsedCurrencyIn = tokenInIsNative - ? NativeCurrency.onChain(tokenIn.chainId) - : parseTokenApi(tokenIn) + const parsedCurrencyIn = tokenInIsNative ? NativeCurrency.onChain(tokenIn.chainId) : parseTokenApi(tokenIn) - const parsedCurrencyOut = tokenOutIsNative - ? NativeCurrency.onChain(tokenOut.chainId) - : parseTokenApi(tokenOut) + const parsedCurrencyOut = tokenOutIsNative ? NativeCurrency.onChain(tokenOut.chainId) : parseTokenApi(tokenOut) try { return quote.route.map((route) => { @@ -179,12 +169,8 @@ export function computeRoutesTradingApi( const isOnlyV3 = isV3OnlyRouteApi(route) return { - routev3: isOnlyV3 - ? new V3Route(route.map(parseV3PoolApi), parsedCurrencyIn, parsedCurrencyOut) - : null, - routev2: isOnlyV2 - ? new V2Route(route.map(parseV2PairApi), parsedCurrencyIn, parsedCurrencyOut) - : null, + routev3: isOnlyV3 ? new V3Route(route.map(parseV3PoolApi), parsedCurrencyIn, parsedCurrencyOut) : null, + routev2: isOnlyV2 ? new V2Route(route.map(parseV2PairApi), parsedCurrencyIn, parsedCurrencyOut) : null, mixedRoute: !isOnlyV3 && !isOnlyV2 ? new MixedRouteSDK(route.map(parseMixedRouteApi), parsedCurrencyIn, parsedCurrencyOut) @@ -232,7 +218,7 @@ function parseTokenApi(token: TradingApiTokenInRoute): Token { /**name=*/ undefined, false, buyFeeBps ? BigNumber.from(buyFeeBps) : undefined, - sellFeeBps ? BigNumber.from(sellFeeBps) : undefined + sellFeeBps ? BigNumber.from(sellFeeBps) : undefined, ) } @@ -253,7 +239,7 @@ function parseV3PoolApi({ parseInt(fee, 10) as FeeAmount, sqrtRatioX96, liquidity, - parseInt(tickCurrent, 10) + parseInt(tickCurrent, 10), ) } @@ -263,7 +249,7 @@ function parseV2PairApi({ reserve0, reserve1 }: TradingApiV2PoolInRoute): Pair { } return new Pair( CurrencyAmount.fromRawAmount(parseTokenApi(reserve0.token), reserve0.quotient), - CurrencyAmount.fromRawAmount(parseTokenApi(reserve1.token), reserve1.quotient) + CurrencyAmount.fromRawAmount(parseTokenApi(reserve1.token), reserve1.quotient), ) } @@ -287,7 +273,7 @@ export function getTokenAddressForApi(currency: Maybe): string | undef } const SUPPORTED_TRADING_API_CHAIN_IDS: number[] = Object.values(TradingApiChainId).filter( - (value): value is number => typeof value === 'number' + (value): value is number => typeof value === 'number', ) // Parse any chain id to check if its supported by the API ChainId type @@ -298,9 +284,7 @@ function isTradingApiSupportedChainId(chainId?: number): chainId is TradingApiCh return Object.values(SUPPORTED_TRADING_API_CHAIN_IDS).includes(chainId) } -export function toTradingApiSupportedChainId( - chainId: Maybe -): TradingApiChainId | undefined { +export function toTradingApiSupportedChainId(chainId: Maybe): TradingApiChainId | undefined { if (!chainId || !isTradingApiSupportedChainId(chainId)) { return undefined } @@ -346,14 +330,8 @@ export function validateTrade({ return null } - const inputsMatch = areAddressesEqual( - currencyIn.wrapped.address, - trade?.inputAmount.currency.wrapped.address - ) - const outputsMatch = areAddressesEqual( - currencyOut.wrapped.address, - trade.outputAmount.currency.wrapped.address - ) + const inputsMatch = areAddressesEqual(currencyIn.wrapped.address, trade?.inputAmount.currency.wrapped.address) + const outputsMatch = areAddressesEqual(currencyOut.wrapped.address, trade.outputAmount.currency.wrapped.address) // TODO(MOB-3028): check if this logic needs any adjustments once we add UniswapX support. // Verify the amount specified in the quote response matches the exact amount from input state @@ -367,21 +345,18 @@ export function validateTrade({ const exactAmountsMatch = exactAmount?.toExact() !== exactAmountFromQuote if (!(tokenAddressesMatch && exactAmountsMatch)) { - logger.error( - new Error(`Mismatched ${!tokenAddressesMatch ? 'address' : 'exact amount'} in swap trade`), - { - tags: { file: 'tradingApi/utils', function: 'validateTrade' }, - extra: { - formState: { - currencyIdIn: currencyId(currencyIn), - currencyIdOut: currencyId(currencyOut), - exactAmount: exactAmount.toExact(), - exactCurrencyField, - }, - tradeProperties: getBaseTradeAnalyticsProperties({ trade, formatter }), + logger.error(new Error(`Mismatched ${!tokenAddressesMatch ? 'address' : 'exact amount'} in swap trade`), { + tags: { file: 'tradingApi/utils', function: 'validateTrade' }, + extra: { + formState: { + currencyIdIn: currencyId(currencyIn), + currencyIdOut: currencyId(currencyOut), + exactAmount: exactAmount.toExact(), + exactCurrencyField, }, - } - ) + tradeProperties: getBaseTradeAnalyticsProperties({ trade, formatter }), + }, + }) return null } @@ -393,7 +368,7 @@ export function validateTrade({ export function getRoutingPreferenceForSwapRequest( protocolPreference: TradeProtocolPreference | undefined, uniswapXEnabled: boolean, - isUSDQuote?: boolean + isUSDQuote?: boolean, ): RoutingPreference { if (isUSDQuote) { return RoutingPreference.CLASSIC diff --git a/packages/wallet/src/features/transactions/swap/trade/types.ts b/packages/wallet/src/features/transactions/swap/trade/types.ts index 3f31c765f6c..c949ca13250 100644 --- a/packages/wallet/src/features/transactions/swap/trade/types.ts +++ b/packages/wallet/src/features/transactions/swap/trade/types.ts @@ -6,17 +6,9 @@ import { V2DutchOrderTrade } from '@uniswap/uniswapx-sdk' import { Route as V2RouteSDK } from '@uniswap/v2-sdk' import { Route as V3RouteSDK } from '@uniswap/v3-sdk' import { providers } from 'ethers' -import { PollingInterval } from 'wallet/src/constants/misc' -import { - ClassicQuote, - DutchQuoteV2, - QuoteResponse, - Routing, -} from 'wallet/src/data/tradingApi/__generated__/index' -import { - getSwapFee, - transformToDutchOrderInfo, -} from 'wallet/src/features/transactions/swap/trade/tradingApi/utils' +import { PollingInterval } from 'uniswap/src/constants/misc' +import { ClassicQuote, DutchQuoteV2, QuoteResponse, Routing } from 'wallet/src/data/tradingApi/__generated__/index' +import { getSwapFee, transformToDutchOrderInfo } from 'wallet/src/features/transactions/swap/trade/tradingApi/utils' import { TradeProtocolPreference } from 'wallet/src/features/transactions/transactionState/types' // TradingAPI team is looking into updating type generation to produce the following types for it's current QuoteResponse type: @@ -80,7 +72,7 @@ export class UniswapXTrade extends V2DutchOrderTrade extends RouterSDKTrade { readonly quote?: ClassicQuoteResponse readonly routing = Routing.CLASSIC @@ -125,7 +117,7 @@ export class ClassicTrade< export type Trade< TInput extends Currency = Currency, TOutput extends Currency = Currency, - TTradeType extends TradeType = TradeType + TTradeType extends TradeType = TradeType, > = ClassicTrade | UniswapXTrade export interface TradeWithStatus { diff --git a/packages/wallet/src/features/transactions/swap/trade/utils.ts b/packages/wallet/src/features/transactions/swap/trade/utils.ts index 58e996a5b63..a566e17d657 100644 --- a/packages/wallet/src/features/transactions/swap/trade/utils.ts +++ b/packages/wallet/src/features/transactions/swap/trade/utils.ts @@ -1,13 +1,11 @@ import { Routing } from 'wallet/src/data/tradingApi/__generated__/index' export function isUniswapX( - obj: T -): obj is T & { routing: Routing.DUTCH_V2 } { - return obj.routing === Routing.DUTCH_V2 + obj: T, +): obj is T & { routing: Routing.DUTCH_V2 | Routing.DUTCH_LIMIT } { + return obj.routing === Routing.DUTCH_V2 || obj.routing === Routing.DUTCH_LIMIT } -export function isClassic( - obj: T -): obj is T & { routing: Routing.CLASSIC } { +export function isClassic(obj: T): obj is T & { routing: Routing.CLASSIC } { return obj.routing === Routing.CLASSIC } diff --git a/packages/wallet/src/features/transactions/swap/types.ts b/packages/wallet/src/features/transactions/swap/types.ts index ea2db1924a4..c894d64bf21 100644 --- a/packages/wallet/src/features/transactions/swap/types.ts +++ b/packages/wallet/src/features/transactions/swap/types.ts @@ -8,7 +8,7 @@ import { WrapType } from 'wallet/src/features/transactions/types' export type DerivedSwapInfo< TInput = CurrencyInfo, - TOutput extends CurrencyInfo = CurrencyInfo + TOutput extends CurrencyInfo = CurrencyInfo, > = BaseDerivedInfo & { chainId: WalletChainId currencies: BaseDerivedInfo['currencies'] & { diff --git a/packages/wallet/src/features/transactions/swap/usePermit2Signature.ts b/packages/wallet/src/features/transactions/swap/usePermit2Signature.ts index 22cd4d36a2d..4f4aaed3f89 100644 --- a/packages/wallet/src/features/transactions/swap/usePermit2Signature.ts +++ b/packages/wallet/src/features/transactions/swap/usePermit2Signature.ts @@ -1,10 +1,4 @@ -import { - AllowanceProvider, - AllowanceTransfer, - MaxUint160, - permit2Address, - PermitSingle, -} from '@uniswap/permit2-sdk' +import { AllowanceProvider, AllowanceTransfer, MaxUint160, permit2Address, PermitSingle } from '@uniswap/permit2-sdk' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { UNIVERSAL_ROUTER_ADDRESS } from '@uniswap/universal-router-sdk' import dayjs from 'dayjs' @@ -22,11 +16,7 @@ import { SignerManager } from 'wallet/src/features/wallet/signing/SignerManager' import { signTypedData } from 'wallet/src/features/wallet/signing/signing' const PERMIT2_SIG_VALIDITY_TIME = 30 // minutes -function getPermitStruct( - tokenAddress: string, - nonce: number, - universalRouterAddress: string -): PermitSingle { +function getPermitStruct(tokenAddress: string, nonce: number, universalRouterAddress: string): PermitSingle { return { details: { token: tokenAddress, @@ -56,15 +46,11 @@ async function getPermit2PermitSignature( account: Account, tokenAddress: string, chainId: WalletChainId, - tokenInAmount: string + tokenInAmount: string, ): Promise { try { if (account.type === AccountType.Readonly) { - logger.debug( - 'usePermit2PermitSignature', - 'getPermit2Signature', - 'Cannot sign with a view-only wallet' - ) + logger.debug('usePermit2PermitSignature', 'getPermit2Signature', 'Cannot sign with a view-only wallet') return } @@ -82,11 +68,7 @@ async function getPermit2PermitSignature( } const permitMessage = getPermitStruct(tokenAddress, nonce, universalRouterAddress) - const { domain, types, values } = AllowanceTransfer.getPermitData( - permitMessage, - permit2Address(chainId), - chainId - ) + const { domain, types, values } = AllowanceTransfer.getPermitData(permitMessage, permit2Address(chainId), chainId) const signature = await signTypedData( domain, @@ -95,7 +77,7 @@ async function getPermit2PermitSignature( // alternative would be to modify the sdk to use type aliases over interfaces { ...values }, account, - signerManager + signerManager, ) return { signature, @@ -111,7 +93,7 @@ async function getPermit2PermitSignature( export function usePermit2Signature( currencyInAmount: Maybe>, - skip?: boolean + skip?: boolean, ): { isLoading: boolean data: PermitSignatureInfo | undefined @@ -132,7 +114,7 @@ export function usePermit2Signature( account, currencyIn.address, currencyIn.chainId, - currencyInAmount.quotient.toString() + currencyInAmount.quotient.toString(), ) }, [account, currencyIn, currencyInAmount?.quotient, provider, signerManager, skip]) @@ -143,7 +125,7 @@ export function usePermit2Signature( export function usePermit2SignatureWithData( currencyInAmount: Maybe>, permitData: Maybe, - skip?: boolean + skip?: boolean, ): { isLoading: boolean signature: string | undefined @@ -156,7 +138,7 @@ export function usePermit2SignatureWithData( const { domain, types, values } = permitData || {} const permitSignatureFetcher = useCallback(async () => { - if (!provider || !currencyIn || currencyIn.isNative || skip || !domain || !types || !values) { + if (!provider || !currencyIn || skip || !domain || !types || !values) { return } @@ -165,7 +147,7 @@ export function usePermit2SignatureWithData( types as Record, values as Record, account, - signerManager + signerManager, ) }, [account, currencyIn, domain, provider, signerManager, skip, types, values]) diff --git a/packages/wallet/src/features/transactions/swap/utils.ts b/packages/wallet/src/features/transactions/swap/utils.ts index f079b669af5..67e3a893d93 100644 --- a/packages/wallet/src/features/transactions/swap/utils.ts +++ b/packages/wallet/src/features/transactions/swap/utils.ts @@ -12,6 +12,13 @@ import { ElementName, ElementNameType } from 'uniswap/src/features/telemetry/con import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' import { CurrencyId } from 'uniswap/src/types/currency' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' +import { + areCurrencyIdsEqual, + buildWrappedNativeCurrencyId, + currencyId, + currencyIdToAddress, + currencyIdToChain, +} from 'uniswap/src/utils/currencyId' import { NumberType } from 'utilities/src/format/types' import { AssetType } from 'wallet/src/entities/assets' import { LocalizationContextState } from 'wallet/src/features/language/LocalizationContext' @@ -19,27 +26,15 @@ import { getClassicQuoteFromResponse } from 'wallet/src/features/transactions/sw import { ClassicTrade, Trade } from 'wallet/src/features/transactions/swap/trade/types' import { isClassic } from 'wallet/src/features/transactions/swap/trade/utils' import { PermitSignatureInfo } from 'wallet/src/features/transactions/swap/usePermit2Signature' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { ExactInputSwapTransactionInfo, ExactOutputSwapTransactionInfo, TransactionType, WrapType, } from 'wallet/src/features/transactions/types' -import { - areCurrencyIdsEqual, - buildWrappedNativeCurrencyId, - currencyId, - currencyIdToAddress, - currencyIdToChain, -} from 'wallet/src/utils/currencyId' -export function serializeQueryParams( - params: Record[0]> -): string { +export function serializeQueryParams(params: Record[0]>): string { const queryString = [] for (const [param, value] of Object.entries(params)) { queryString.push(`${encodeURIComponent(param)}=${encodeURIComponent(value)}`) @@ -49,7 +44,7 @@ export function serializeQueryParams( export function getWrapType( inputCurrency: Currency | null | undefined, - outputCurrency: Currency | null | undefined + outputCurrency: Currency | null | undefined, ): WrapType { if (!inputCurrency || !outputCurrency || inputCurrency.chainId !== outputCurrency.chainId) { return WrapType.NotApplicable @@ -58,15 +53,9 @@ export function getWrapType( const inputChainId = inputCurrency.chainId as WalletChainId const wrappedCurrencyId = buildWrappedNativeCurrencyId(inputChainId) - if ( - inputCurrency.isNative && - areCurrencyIdsEqual(currencyId(outputCurrency), wrappedCurrencyId) - ) { + if (inputCurrency.isNative && areCurrencyIdsEqual(currencyId(outputCurrency), wrappedCurrencyId)) { return WrapType.Wrap - } else if ( - outputCurrency.isNative && - areCurrencyIdsEqual(currencyId(inputCurrency), wrappedCurrencyId) - ) { + } else if (outputCurrency.isNative && areCurrencyIdsEqual(currencyId(inputCurrency), wrappedCurrencyId)) { return WrapType.Unwrap } @@ -77,9 +66,7 @@ export function isWrapAction(wrapType: WrapType): wrapType is WrapType.Unwrap | return wrapType === WrapType.Unwrap || wrapType === WrapType.Wrap } -export function tradeToTransactionInfo( - trade: Trade -): ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo { +export function tradeToTransactionInfo(trade: Trade): ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo { const slippageTolerancePercent = slippageToleranceToPercent(trade.slippageTolerance) const { quote, slippageTolerance } = trade @@ -102,9 +89,7 @@ export function tradeToTransactionInfo( tradeType: TradeType.EXACT_INPUT, inputCurrencyAmountRaw: trade.inputAmount.quotient.toString(), expectedOutputCurrencyAmountRaw: trade.outputAmount.quotient.toString(), - minimumOutputCurrencyAmountRaw: trade - .minimumAmountOut(slippageTolerancePercent) - .quotient.toString(), + minimumOutputCurrencyAmountRaw: trade.minimumAmountOut(slippageTolerancePercent).quotient.toString(), } : { ...baseTransactionInfo, @@ -112,9 +97,7 @@ export function tradeToTransactionInfo( tradeType: TradeType.EXACT_OUTPUT, outputCurrencyAmountRaw: trade.outputAmount.quotient.toString(), expectedInputCurrencyAmountRaw: trade.inputAmount.quotient.toString(), - maximumInputCurrencyAmountRaw: trade - .maximumAmountIn(slippageTolerancePercent) - .quotient.toString(), + maximumInputCurrencyAmountRaw: trade.maximumAmountIn(slippageTolerancePercent).quotient.toString(), } } @@ -136,7 +119,7 @@ export function requireAcceptNewTrade(oldTrade: Maybe, newTrade: Maybe { const price = showInverseRate ? trade.executionPrice.invert() : trade.executionPrice diff --git a/packages/wallet/src/features/transactions/swap/wrapSaga.ts b/packages/wallet/src/features/transactions/swap/wrapSaga.ts index 67d6e8cb90c..6f9bad83159 100644 --- a/packages/wallet/src/features/transactions/swap/wrapSaga.ts +++ b/packages/wallet/src/features/transactions/swap/wrapSaga.ts @@ -3,15 +3,11 @@ import { Contract, providers } from 'ethers' import { call } from 'typed-redux-saga' import { Weth } from 'uniswap/src/abis/types' import WETH_ABI from 'uniswap/src/abis/weth.json' +import { getWrappedNativeAddress } from 'uniswap/src/constants/addresses' import { WalletChainId } from 'uniswap/src/types/chains' import { logger } from 'utilities/src/logger/logger' -import { getWrappedNativeAddress } from 'wallet/src/constants/addresses' import { sendTransaction } from 'wallet/src/features/transactions/sendTransactionSaga' -import { - TransactionOptions, - TransactionType, - TransactionTypeInfo, -} from 'wallet/src/features/transactions/types' +import { TransactionOptions, TransactionType, TransactionTypeInfo } from 'wallet/src/features/transactions/types' import { Account } from 'wallet/src/features/wallet/accounts/types' import { createMonitoredSaga } from 'wallet/src/utils/saga' @@ -22,10 +18,7 @@ export type WrapParams = { inputCurrencyAmount: CurrencyAmount } -export async function getWethContract( - chainId: WalletChainId, - provider: providers.Provider -): Promise { +export async function getWethContract(chainId: WalletChainId, provider: providers.Provider): Promise { return new Contract(getWrappedNativeAddress(chainId), WETH_ABI, provider) as Weth } diff --git a/packages/wallet/src/features/transactions/transactionState/transactionState.test.ts b/packages/wallet/src/features/transactions/transactionState/transactionState.test.ts index 799c9da64f7..ecb81299e62 100644 --- a/packages/wallet/src/features/transactions/transactionState/transactionState.test.ts +++ b/packages/wallet/src/features/transactions/transactionState/transactionState.test.ts @@ -1,6 +1,6 @@ import { AnyAction } from '@reduxjs/toolkit' +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' import { AssetType, CurrencyAsset } from 'wallet/src/entities/assets' import { INITIAL_TRANSACTION_STATE, diff --git a/packages/wallet/src/features/transactions/transactionState/transactionState.ts b/packages/wallet/src/features/transactions/transactionState/transactionState.ts index fbbfe0e3908..bbfa503c2a4 100644 --- a/packages/wallet/src/features/transactions/transactionState/transactionState.ts +++ b/packages/wallet/src/features/transactions/transactionState/transactionState.ts @@ -1,12 +1,9 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { shallowEqual } from 'react-redux' +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' import { AssetType, TradeableAsset } from 'wallet/src/entities/assets' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' const ETH_TRADEABLE_ASSET: TradeableAsset = { address: getNativeAddress(UniverseChainId.Mainnet), @@ -37,13 +34,9 @@ const slice = createSlice({ * If input/output currencies would be the same, it swaps the order * If network would change, unsets the dependent field */ - selectCurrency: ( - state, - action: PayloadAction<{ field: CurrencyField; tradeableAsset: TradeableAsset }> - ) => { + selectCurrency: (state, action: PayloadAction<{ field: CurrencyField; tradeableAsset: TradeableAsset }>) => { const { field, tradeableAsset } = action.payload - const nonExactField = - field === CurrencyField.INPUT ? CurrencyField.OUTPUT : CurrencyField.INPUT + const nonExactField = field === CurrencyField.INPUT ? CurrencyField.OUTPUT : CurrencyField.INPUT // swap order if tokens are the same if (shallowEqual(tradeableAsset, state[nonExactField])) { @@ -62,9 +55,7 @@ const slice = createSlice({ /** Switches input and output currencies */ switchCurrencySides: (state) => { state.exactCurrencyField = - state.exactCurrencyField === CurrencyField.INPUT - ? CurrencyField.OUTPUT - : CurrencyField.INPUT + state.exactCurrencyField === CurrencyField.INPUT ? CurrencyField.OUTPUT : CurrencyField.INPUT state.focusOnCurrencyField = state.exactCurrencyField ;[state[CurrencyField.INPUT], state[CurrencyField.OUTPUT]] = [ state[CurrencyField.OUTPUT], @@ -77,7 +68,7 @@ const slice = createSlice({ action: PayloadAction<{ field?: CurrencyField amount: string - }> + }>, ) => { const { field, amount } = action.payload if (field) { @@ -91,7 +82,7 @@ const slice = createSlice({ action: PayloadAction<{ field?: CurrencyField amount: string - }> + }>, ) => { const { field, amount } = action.payload if (field) { diff --git a/packages/wallet/src/features/transactions/transactionWatcherSaga.test.ts b/packages/wallet/src/features/transactions/transactionWatcherSaga.test.ts index cbb36c68bb5..2a2b291f8d0 100644 --- a/packages/wallet/src/features/transactions/transactionWatcherSaga.test.ts +++ b/packages/wallet/src/features/transactions/transactionWatcherSaga.test.ts @@ -2,10 +2,10 @@ import { faker } from '@faker-js/faker' import { expectSaga } from 'redux-saga-test-plan' import * as matchers from 'redux-saga-test-plan/matchers' import { call, delay } from 'redux-saga/effects' +import { PollingInterval } from 'uniswap/src/constants/misc' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' import { UniverseChainId } from 'uniswap/src/types/chains' import { sleep } from 'utilities/src/time/timing' -import { PollingInterval } from 'wallet/src/constants/misc' import { fetchMoonpayTransaction } from 'wallet/src/features/fiatOnRamp/api' import { attemptCancelTransaction } from 'wallet/src/features/transactions/cancelTransactionSaga' import { diff --git a/packages/wallet/src/features/transactions/transactionWatcherSaga.ts b/packages/wallet/src/features/transactions/transactionWatcherSaga.ts index 4ded414c145..a6eb8698e89 100644 --- a/packages/wallet/src/features/transactions/transactionWatcherSaga.ts +++ b/packages/wallet/src/features/transactions/transactionWatcherSaga.ts @@ -5,6 +5,7 @@ import { TradeType } from '@uniswap/sdk-core' import { BigNumberish, providers } from 'ethers' import { call, delay, fork, put, race, select, take, takeEvery } from 'typed-redux-saga' import { isWeb } from 'ui/src' +import { PollingInterval } from 'uniswap/src/constants/misc' import { FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/gating/flags' import { Statsig } from 'uniswap/src/features/gating/sdk/statsig' import { @@ -17,26 +18,16 @@ import { sendAnalyticsEvent, sendAppsFlyerEvent } from 'uniswap/src/features/tel import i18n from 'uniswap/src/i18n/i18n' import { WalletChainId } from 'uniswap/src/types/chains' import { logger } from 'utilities/src/logger/logger' -import { PollingInterval } from 'wallet/src/constants/misc' import { selectExtensionBetaFeedbackState } from 'wallet/src/features/behaviorHistory/selectors' -import { - ExtensionBetaFeedbackState, - setExtensionBetaFeedbackState, -} from 'wallet/src/features/behaviorHistory/slice' -import { - fetchFiatOnRampTransaction, - fetchMoonpayTransaction, -} from 'wallet/src/features/fiatOnRamp/api' +import { ExtensionBetaFeedbackState, setExtensionBetaFeedbackState } from 'wallet/src/features/behaviorHistory/slice' +import { fetchFiatOnRampTransaction, fetchMoonpayTransaction } from 'wallet/src/features/fiatOnRamp/api' import { FiatOnRampTransactionDetails } from 'wallet/src/features/fiatOnRamp/types' import { pushNotification, setNotificationStatus } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' import { attemptCancelTransaction } from 'wallet/src/features/transactions/cancelTransactionSaga' import { refetchGQLQueries } from 'wallet/src/features/transactions/refetchGQLQueriesSaga' import { attemptReplaceTransaction } from 'wallet/src/features/transactions/replaceTransactionSaga' -import { - selectIncompleteTransactions, - selectSwapTransactionsCount, -} from 'wallet/src/features/transactions/selectors' +import { selectIncompleteTransactions, selectSwapTransactionsCount } from 'wallet/src/features/transactions/selectors' import { addTransaction, cancelTransaction, @@ -61,11 +52,7 @@ import { getProvider, getSignerManager } from 'wallet/src/features/wallet/contex import { selectActiveAccount } from 'wallet/src/features/wallet/selectors' import { appSelect } from 'wallet/src/state' -export function* transactionWatcher({ - apolloClient, -}: { - apolloClient: ApolloClient -}) { +export function* transactionWatcher({ apolloClient }: { apolloClient: ApolloClient }) { logger.debug('transactionWatcherSaga', 'transactionWatcher', 'Starting tx watcher') yield* fork(watchForFinalizedTransactions) @@ -110,16 +97,13 @@ export function* transactionWatcher({ type: AppNotificationType.Error, address: transaction.from, errorMessage: i18n.t('transaction.watcher.error.status'), - }) + }), ) } } } -export function* fetchUpdatedFiatOnRampTransaction( - transaction: FiatOnRampTransactionDetails, - forceFetch: boolean -) { +export function* fetchUpdatedFiatOnRampTransaction(transaction: FiatOnRampTransactionDetails, forceFetch: boolean) { const activeAccount = yield* select(selectActiveAccount) if (!activeAccount) { return @@ -131,7 +115,7 @@ export function* fetchUpdatedFiatOnRampTransaction( /** previousTransactionDetails= */ transaction, forceFetch, activeAccount, - signerManager + signerManager, ) } @@ -142,9 +126,7 @@ export function* fetchUpdatedMoonpayTransaction(transaction: FiatOnRampTransacti export function* watchFiatOnRampTransaction(transaction: FiatOnRampTransactionDetails) { // we want to re-fetch this every time we've added a transaction, // as this feature flag could be changed after the app has started - const useOldMoonpayIntegration = !Statsig.checkGate( - getFeatureFlagName(FeatureFlags.ForAggregator) - ) + const useOldMoonpayIntegration = !Statsig.checkGate(getFeatureFlagName(FeatureFlags.ForAggregator)) const { id } = transaction @@ -154,7 +136,7 @@ export function* watchFiatOnRampTransaction(transaction: FiatOnRampTransactionDe 'Watching for updates for fiat onramp tx:', id, 'useOldMoonpayIntegration:', - useOldMoonpayIntegration + useOldMoonpayIntegration, ) let latestStatus = transaction.status @@ -180,11 +162,10 @@ export function* watchFiatOnRampTransaction(transaction: FiatOnRampTransactionDe logger.debug( 'transactionWatcherSaga', 'watchFiatOnRampTransaction', - `Updating transaction with id ${id} from status ${transaction.status} to ${updatedTransaction.status}` + `Updating transaction with id ${id} from status ${transaction.status} to ${updatedTransaction.status}`, ) - const isTransfer = - updatedTransaction.typeInfo.inputSymbol === updatedTransaction.typeInfo.outputSymbol + const isTransfer = updatedTransaction.typeInfo.inputSymbol === updatedTransaction.typeInfo.outputSymbol if (updatedTransaction.typeInfo.serviceProvider) { yield* call( sendAnalyticsEvent, @@ -195,7 +176,7 @@ export function* watchFiatOnRampTransaction(transaction: FiatOnRampTransactionDe externalTransactionId: updatedTransaction.id, status: updatedTransaction.status, serviceProvider: updatedTransaction.typeInfo.serviceProvider, - } + }, ) } } @@ -295,7 +276,7 @@ export function* watchTransaction({ type: AppNotificationType.Error, address: transaction.from, errorMessage: i18n.t('transaction.watcher.error.cancel'), - }) + }), ) } return @@ -307,7 +288,7 @@ export function* watchTransaction({ export async function waitForReceipt( hash: string, - provider: providers.Provider + provider: providers.Provider, ): Promise { const txReceipt = await provider.waitForTransaction(hash) if (txReceipt) { @@ -337,14 +318,10 @@ function* waitForReplacement(chainId: WalletChainId, id: string) { * Monitor for transactions with the same nonce as the current transaction. If any duplicate is finalized, it means * the current transaction has been invalidated and wont be picked up on chain. */ -export function* waitForTxnInvalidated( - chainId: WalletChainId, - id: string, - nonce: BigNumberish | undefined -) { +export function* waitForTxnInvalidated(chainId: WalletChainId, id: string, nonce: BigNumberish | undefined) { while (true) { const { payload } = yield* take>( - transactionActions.finalizeTransaction.type + transactionActions.finalizeTransaction.type, ) if ( @@ -361,9 +338,7 @@ export function* waitForTxnInvalidated( /** * Send analytics events for finalized transactions */ -export function logTransactionEvent( - actionData: ReturnType -): void { +export function logTransactionEvent(actionData: ReturnType): void { const { payload } = actionData const { hash, chainId, addedTime, from, typeInfo, receipt, status } = payload const { gasUsed, effectiveGasPrice, confirmedTime } = receipt ?? {} @@ -420,20 +395,26 @@ export function logTransactionEvent( function* watchForFinalizedTransactions() { const state = yield* appSelect(selectExtensionBetaFeedbackState) - if (isWeb && state === undefined) { + const extensionBetaFeedbackPromptEnabled = Statsig.checkGate( + getFeatureFlagName(FeatureFlags.ExtensionBetaFeedbackPrompt), + ) + + if (isWeb && extensionBetaFeedbackPromptEnabled && state === undefined) { yield* takeEvery(transactionActions.finalizeTransaction.type, maybeLaunchFeedbackModal) } } -function* maybeLaunchFeedbackModal( - actionData: ReturnType -) { +function* maybeLaunchFeedbackModal(actionData: ReturnType) { const { payload } = actionData const { typeInfo, status } = payload const { type } = typeInfo const state = yield* appSelect(selectExtensionBetaFeedbackState) + const extensionBetaFeedbackPromptEnabled = Statsig.checkGate( + getFeatureFlagName(FeatureFlags.ExtensionBetaFeedbackPrompt), + ) if ( + extensionBetaFeedbackPromptEnabled && status === TransactionStatus.Success && [TransactionType.Swap, TransactionType.Send].includes(type) && state === undefined @@ -443,10 +424,7 @@ function* maybeLaunchFeedbackModal( } } -type StatusOverride = - | TransactionStatus.Success - | TransactionStatus.Failed - | TransactionStatus.Canceled +type StatusOverride = TransactionStatus.Success | TransactionStatus.Failed | TransactionStatus.Canceled function* finalizeTransaction({ apolloClient, @@ -459,8 +437,7 @@ function* finalizeTransaction({ statusOverride?: StatusOverride transaction: TransactionDetails }) { - const status = - statusOverride ?? getFinalizedTransactionStatus(transaction.status, ethersReceipt?.status) + const status = statusOverride ?? getFinalizedTransactionStatus(transaction.status, ethersReceipt?.status) const receipt: TransactionReceipt | undefined = ethersReceipt ? { @@ -493,7 +470,7 @@ function* finalizeTransaction({ hash, status, receipt, - }) + }), ) // Flip status to true so we can render Notification badge on home @@ -522,6 +499,6 @@ export function* deleteTransaction(transaction: TransactionDetails) { address: transaction.from, id: transaction.id, chainId: transaction.chainId, - }) + }), ) } diff --git a/packages/wallet/src/features/transactions/transfer/TokenSelectorPanel.tsx b/packages/wallet/src/features/transactions/transfer/TokenSelectorPanel.tsx index 5820bfedde8..6394bdf1610 100644 --- a/packages/wallet/src/features/transactions/transfer/TokenSelectorPanel.tsx +++ b/packages/wallet/src/features/transactions/transfer/TokenSelectorPanel.tsx @@ -6,10 +6,7 @@ import { iconSizes } from 'ui/src/theme' import { CurrencyLogo } from 'uniswap/src/components/CurrencyLogo/CurrencyLogo' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { NumberType } from 'utilities/src/format/types' -import { - TokenSelector, - TokenSelectorVariation, -} from 'wallet/src/components/TokenSelector/TokenSelector' +import { TokenSelector, TokenSelectorVariation } from 'wallet/src/components/TokenSelector/TokenSelector' import { MaxAmountButton } from 'wallet/src/components/input/MaxAmountButton' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { SearchContext } from 'wallet/src/features/search/SearchContext' @@ -89,12 +86,7 @@ export function TokenSelectorPanel({ onSetMax={onSetMax} /> )} - + diff --git a/packages/wallet/src/features/transactions/transfer/TransferAmountInput.tsx b/packages/wallet/src/features/transactions/transfer/TransferAmountInput.tsx index 9ec8deeabdf..7aacd659a42 100644 --- a/packages/wallet/src/features/transactions/transfer/TransferAmountInput.tsx +++ b/packages/wallet/src/features/transactions/transfer/TransferAmountInput.tsx @@ -51,14 +51,14 @@ export function TransferAmountInput({ selection: { start, end }, }, }: NativeSyntheticEvent) => selectionChange?.(start, end), - [selectionChange] + [selectionChange], ) const onChangeText = useCallback( (newValue: string) => { onSetExactAmount(CurrencyField.INPUT, newValue, isFiatInput) }, - [onSetExactAmount, isFiatInput] + [onSetExactAmount, isFiatInput], ) // Display the fiat equivalent amount if the input is in fiat mode, otherwise display the token amount if fiat mode @@ -77,7 +77,7 @@ export function TransferAmountInput({ const { onLayout, fontSize, onSetFontSize } = useDynamicFontSizing( MAX_CHAR_PIXEL_WIDTH, MAX_INPUT_FONT_SIZE, - MIN_INPUT_FONT_SIZE + MIN_INPUT_FONT_SIZE, ) const [containerWidth, setContainerWidth] = useState(0) @@ -102,11 +102,11 @@ export function TransferAmountInput({ const subTextValue = warning ? warning.warning.title : !tokenOrFiatEquivalentAmount - ? // Override empty string from useTokenAndFiatDisplayAmounts to keep UI placeholder text consistent - isFiatInput - ? '0' - : '$0' - : tokenOrFiatEquivalentAmount + ? // Override empty string from useTokenAndFiatDisplayAmounts to keep UI placeholder text consistent + isFiatInput + ? '0' + : '$0' + : tokenOrFiatEquivalentAmount const subTextValueColor = warning ? '$statusCritical' : '$neutral2' const inputColor = !value ? '$neutral3' : '$neutral1' @@ -121,20 +121,11 @@ export function TransferAmountInput({ // Avoid case where onSetFontSize is called before onLayout, resulting in incorrect sizing if view is re-mounted onSetFontSize(value || '0') }} - {...rest}> - + {...rest} + > + {isFiatInput && ( - + {symbol} )} diff --git a/packages/wallet/src/features/transactions/transfer/TransferFormWarnings.tsx b/packages/wallet/src/features/transactions/transfer/TransferFormWarnings.tsx index 2abf9e76cf4..a84eed94076 100644 --- a/packages/wallet/src/features/transactions/transfer/TransferFormWarnings.tsx +++ b/packages/wallet/src/features/transactions/transfer/TransferFormWarnings.tsx @@ -10,11 +10,7 @@ import { WarningSeverity } from 'wallet/src/features/transactions/WarningModal/t import { useAllTransactionsBetweenAddresses } from 'wallet/src/features/transactions/hooks/useAllTransactionsBetweenAddresses' import { useIsSmartContractAddress } from 'wallet/src/features/transactions/transfer/hooks/useIsSmartContractAddress' import { TransferSpeedbump } from 'wallet/src/features/transactions/transfer/types' -import { - useActiveAccountAddressWithThrow, - useDisplayName, - useSignerAccounts, -} from 'wallet/src/features/wallet/hooks' +import { useActiveAccountAddressWithThrow, useDisplayName, useSignerAccounts } from 'wallet/src/features/wallet/hooks' import { DisplayNameType } from 'wallet/src/features/wallet/types' interface TransferFormWarningProps { @@ -42,12 +38,12 @@ export function TransferFormSpeedbumps({ const currentSignerAccounts = useSignerAccounts() const isSignerRecipient = useMemo( () => currentSignerAccounts.some((a) => a.address === recipient), - [currentSignerAccounts, recipient] + [currentSignerAccounts, recipient], ) const { isSmartContractAddress, loading: addressLoading } = useIsSmartContractAddress( recipient, - chainId ?? UniverseChainId.Mainnet + chainId ?? UniverseChainId.Mainnet, ) const shouldWarnSelfSend = isSameAddress(activeAddress, recipient) @@ -57,8 +53,7 @@ export function TransferFormSpeedbumps({ useEffect(() => { setTransferSpeedbump({ - hasWarning: - shouldWarnSmartContract || shouldWarnNewAddress || shouldWarnErc20 || shouldWarnSelfSend, + hasWarning: shouldWarnSmartContract || shouldWarnNewAddress || shouldWarnErc20 || shouldWarnSelfSend, loading: addressLoading, }) }, [ @@ -131,12 +126,9 @@ export function TransferFormSpeedbumps({ severity={WarningSeverity.Medium} title={t('send.warning.newAddress.title')} onClose={onCloseWarning} - onConfirm={onNext}> - + onConfirm={onNext} + > + ) } @@ -162,7 +154,8 @@ const TransferRecipient = ({ gap="$spacing8" px="$spacing16" py="$spacing12" - width="100%"> + width="100%" + > {type === DisplayNameType.ENS ? displayName : address} diff --git a/packages/wallet/src/features/transactions/transfer/TransferReview.tsx b/packages/wallet/src/features/transactions/transfer/TransferReview.tsx index 477f3c1a19f..42eec6ff17d 100644 --- a/packages/wallet/src/features/transactions/transfer/TransferReview.tsx +++ b/packages/wallet/src/features/transactions/transfer/TransferReview.tsx @@ -17,10 +17,7 @@ import { useUSDCValue } from 'wallet/src/features/transactions/swap/trade/hooks/ import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { DerivedTransferInfo } from 'wallet/src/features/transactions/transfer/types' import { AccountType } from 'wallet/src/features/wallet/accounts/types' -import { - useActiveAccountAddressWithThrow, - useActiveAccountWithThrow, -} from 'wallet/src/features/wallet/hooks' +import { useActiveAccountAddressWithThrow, useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' interface TransferFormProps { derivedTransferInfo: DerivedTransferInfo @@ -70,11 +67,7 @@ export function TransferReview({ const { blockingWarning } = warnings const actionButtonDisabled = - !!blockingWarning || - !gasFee.value || - !!gasFee.error || - !txRequest || - account.type === AccountType.Readonly + !!blockingWarning || !gasFee.value || !!gasFee.error || !txRequest || account.type === AccountType.Readonly const actionButtonProps = { disabled: actionButtonDisabled, @@ -83,9 +76,7 @@ export function TransferReview({ onPress: onReviewSubmit, } - const transferWarning = warnings.warnings.find( - (warning) => warning.severity >= WarningSeverity.Medium - ) + const transferWarning = warnings.warnings.find((warning) => warning.severity >= WarningSeverity.Medium) const formattedCurrencyAmount = formatCurrencyAmount({ value: currencyAmounts[CurrencyField.INPUT], diff --git a/packages/wallet/src/features/transactions/transfer/TransferTokenForm.tsx b/packages/wallet/src/features/transactions/transfer/TransferTokenForm.tsx index 0b89b5ebdba..2668ba71e4b 100644 --- a/packages/wallet/src/features/transactions/transfer/TransferTokenForm.tsx +++ b/packages/wallet/src/features/transactions/transfer/TransferTokenForm.tsx @@ -89,13 +89,7 @@ export function TransferTokenForm({ } = derivedTransferInfo const currencyIn = currencyInInfo?.currency - useUSDTokenUpdater( - dispatch, - isFiatInput, - exactAmountToken, - exactAmountFiat, - currencyIn ?? undefined - ) + useUSDTokenUpdater(dispatch, isFiatInput, exactAmountToken, exactAmountFiat, currencyIn ?? undefined) useShowSendNetworkNotification({ chainId: currencyIn?.chainId }) @@ -109,17 +103,12 @@ export function TransferTokenForm({ hasWarning: false, }) - const { onShowTokenSelector } = useTokenSelectorActionHandlers( - dispatch, - TokenSelectorFlow.Transfer - ) + const { onShowTokenSelector } = useTokenSelectorActionHandlers(dispatch, TokenSelectorFlow.Transfer) const { onSetExactAmount, onSetMax } = useTokenFormActionHandlers(dispatch) const onToggleShowRecipientSelector = useOnToggleShowRecipientSelector(dispatch) - const { isBlocked: isActiveBlocked, isBlockedLoading: isActiveBlockedLoading } = - useIsBlockedActiveAddress() - const { isBlocked: isRecipientBlocked, isBlockedLoading: isRecipientBlockedLoading } = - useIsBlocked(recipient) + const { isBlocked: isActiveBlocked, isBlockedLoading: isActiveBlockedLoading } = useIsBlockedActiveAddress() + const { isBlocked: isRecipientBlocked, isBlockedLoading: isRecipientBlockedLoading } = useIsBlocked(recipient) const isBlocked = isActiveBlocked || isRecipientBlocked const isBlockedLoading = isActiveBlockedLoading || isRecipientBlockedLoading @@ -169,7 +158,7 @@ export function TransferTokenForm({ (start: number, end?: number) => { setInputSelection({ start, end: end ?? start }) }, - [setInputSelection] + [setInputSelection], ) const previsFiatInput = usePrevious(isFiatInput) @@ -203,14 +192,7 @@ export function TransferTokenForm({ start: newPositionFromStartWithPrefix, end: newPositionFromStartWithPrefix, }) - }, [ - isFiatInput, - previsFiatInput, - inputSelection, - setInputSelection, - exactAmountToken, - exactAmountFiat, - ]) + }, [isFiatInput, previsFiatInput, inputSelection, setInputSelection, exactAmountToken, exactAmountFiat]) const onTransferWarningClick = (): void => { Keyboard.dismiss() @@ -219,9 +201,7 @@ export function TransferTokenForm({ const { onToggleFiatInput } = useTokenFormActionHandlers(dispatch) - const transferWarning = warnings.warnings.find( - (warning) => warning.severity >= WarningSeverity.Low - ) + const transferWarning = warnings.warnings.find((warning) => warning.severity >= WarningSeverity.Low) const transferWarningColor = getAlertColor(transferWarning?.severity) const TRANSFER_DIRECTION_BUTTON_SIZE = iconSizes.icon20 @@ -236,11 +216,7 @@ export function TransferTokenForm({ caption={transferWarning.message} confirmText={t('common.button.close')} icon={ - + } modalName={ModalName.SendWarning} severity={transferWarning.severity} @@ -263,7 +239,8 @@ export function TransferTokenForm({ // TODO(EXT-526): re-enable `exiting` animation when it's fixed. exiting={isWeb ? undefined : FadeOut} gap="$spacing2" - onLayout={onInputPanelLayout}> + onLayout={onInputPanelLayout} + > {nftIn ? ( ) : ( @@ -281,13 +258,9 @@ export function TransferTokenForm({ warnings={warnings.warnings} onPressIn={(): void => setCurrencyFieldFocused(true)} onSelectionChange={ - showNativeKeyboard - ? undefined - : (start, end): void => setInputSelection({ start, end }) - } - onSetExactAmount={(value): void => - onSetExactAmount(CurrencyField.INPUT, value, isFiatInput) + showNativeKeyboard ? undefined : (start, end): void => setInputSelection({ start, end }) } + onSetExactAmount={(value): void => onSetExactAmount(CurrencyField.INPUT, value, isFiatInput)} onSetMax={(amount): void => { onSetMax(amount) setCurrencyFieldFocused(false) @@ -306,11 +279,9 @@ export function TransferTokenForm({ TRANSFER_DIRECTION_BUTTON_INNER_PADDING + TRANSFER_DIRECTION_BUTTON_BORDER_WIDTH } - style={StyleSheet.absoluteFill}> - + style={StyleSheet.absoluteFill} + > + @@ -323,7 +294,8 @@ export function TransferTokenForm({ borderBottomRightRadius={transferWarning || isBlocked ? '$none' : '$rounded20'} borderTopLeftRadius="$rounded20" borderTopRightRadius="$rounded20" - justifyContent="center"> + justifyContent="center" + > {recipient && ( + py="$spacing12" + > + py="$spacing12" + > + onLayout={onDecimalPadLayout} + > {!isWeb && !nftIn && !showNativeKeyboard && ( + onPress={onPressReview} + > {t('send.button.review')} diff --git a/packages/wallet/src/features/transactions/transfer/getSendPrefilledState.ts b/packages/wallet/src/features/transactions/transfer/getSendPrefilledState.ts index 3f4e303c5d3..4ed18e3fd92 100644 --- a/packages/wallet/src/features/transactions/transfer/getSendPrefilledState.ts +++ b/packages/wallet/src/features/transactions/transfer/getSendPrefilledState.ts @@ -1,10 +1,7 @@ +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { WalletChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' import { AssetType, CurrencyAsset } from 'wallet/src/entities/assets' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' export function getSendPrefilledState({ chainId, diff --git a/packages/wallet/src/features/transactions/transfer/hooks/useDerivedTransferInfo.ts b/packages/wallet/src/features/transactions/transfer/hooks/useDerivedTransferInfo.ts index 3c829b8fc33..6731624d58b 100644 --- a/packages/wallet/src/features/transactions/transfer/hooks/useDerivedTransferInfo.ts +++ b/packages/wallet/src/features/transactions/transfer/hooks/useDerivedTransferInfo.ts @@ -1,19 +1,13 @@ import { useMemo } from 'react' import { UniverseChainId } from 'uniswap/src/types/chains' +import { buildCurrencyId } from 'uniswap/src/utils/currencyId' import { AssetType } from 'wallet/src/entities/assets' import { useNFT } from 'wallet/src/features/nfts/hooks' -import { - useOnChainCurrencyBalance, - useOnChainNativeCurrencyBalance, -} from 'wallet/src/features/portfolio/api' +import { useOnChainCurrencyBalance, useOnChainNativeCurrencyBalance } from 'wallet/src/features/portfolio/api' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' +import { CurrencyField, TransactionState } from 'wallet/src/features/transactions/transactionState/types' import { DerivedTransferInfo } from 'wallet/src/features/transactions/transfer/types' import { useActiveAccount } from 'wallet/src/features/wallet/hooks' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' export function useDerivedTransferInfo(state: TransactionState): DerivedTransferInfo { @@ -32,7 +26,7 @@ export function useDerivedTransferInfo(state: TransactionState): DerivedTransfer const currencyInInfo = useCurrencyInfo( tradeableAsset?.type === AssetType.Currency ? buildCurrencyId(tradeableAsset?.chainId, tradeableAsset?.address) - : undefined + : undefined, ) const currencyIn = currencyInInfo?.currency @@ -41,24 +35,24 @@ export function useDerivedTransferInfo(state: TransactionState): DerivedTransfer tradeableAsset?.address, tradeableAsset?.type === AssetType.ERC1155 || tradeableAsset?.type === AssetType.ERC721 ? tradeableAsset.tokenId - : undefined + : undefined, ) const currencies = useMemo( () => ({ [CurrencyField.INPUT]: currencyInInfo ?? nftIn, }), - [currencyInInfo, nftIn] + [currencyInInfo, nftIn], ) const { balance: tokenInBalance } = useOnChainCurrencyBalance( currencyIn?.isToken ? currencyIn : undefined, - activeAccount?.address + activeAccount?.address, ) const { balance: nativeInBalance } = useOnChainNativeCurrencyBalance( chainId ?? UniverseChainId.Mainnet, - activeAccount?.address + activeAccount?.address, ) const amountSpecified = useMemo( @@ -68,20 +62,20 @@ export function useDerivedTransferInfo(state: TransactionState): DerivedTransfer valueType: ValueType.Exact, currency: currencyIn, }), - [currencyIn, exactAmountToken] + [currencyIn, exactAmountToken], ) const currencyAmounts = useMemo( () => ({ [CurrencyField.INPUT]: amountSpecified, }), - [amountSpecified] + [amountSpecified], ) const currencyBalances = useMemo( () => ({ [CurrencyField.INPUT]: currencyIn?.isNative ? nativeInBalance : tokenInBalance, }), - [currencyIn, nativeInBalance, tokenInBalance] + [currencyIn, nativeInBalance, tokenInBalance], ) return useMemo( () => ({ @@ -112,6 +106,6 @@ export function useDerivedTransferInfo(state: TransactionState): DerivedTransfer recipient, tradeableAsset?.type, txId, - ] + ], ) } diff --git a/packages/wallet/src/features/transactions/transfer/hooks/useIsSmartContractAddress.ts b/packages/wallet/src/features/transactions/transfer/hooks/useIsSmartContractAddress.ts index 8678440fdf0..06c688757ee 100644 --- a/packages/wallet/src/features/transactions/transfer/hooks/useIsSmartContractAddress.ts +++ b/packages/wallet/src/features/transactions/transfer/hooks/useIsSmartContractAddress.ts @@ -5,7 +5,7 @@ import { useProvider } from 'wallet/src/features/wallet/context' export function useIsSmartContractAddress( address: string | undefined, - chainId: WalletChainId + chainId: WalletChainId, ): { loading: boolean isSmartContractAddress: boolean diff --git a/packages/wallet/src/features/transactions/transfer/hooks/useOnSelectRecipient.ts b/packages/wallet/src/features/transactions/transfer/hooks/useOnSelectRecipient.ts index 53bad441f87..32335351e60 100644 --- a/packages/wallet/src/features/transactions/transfer/hooks/useOnSelectRecipient.ts +++ b/packages/wallet/src/features/transactions/transfer/hooks/useOnSelectRecipient.ts @@ -3,15 +3,13 @@ import { useCallback } from 'react' import { selectRecipient } from 'wallet/src/features/transactions/transactionState/transactionState' import { useOnToggleShowRecipientSelector } from 'wallet/src/features/transactions/transfer/hooks/useOnToggleShowRecipientSelector' -export function useOnSelectRecipient( - dispatch: React.Dispatch -): (recipient: Address) => void { +export function useOnSelectRecipient(dispatch: React.Dispatch): (recipient: Address) => void { const onToggleShowRecipientSelector = useOnToggleShowRecipientSelector(dispatch) return useCallback( (recipient: Address) => { onToggleShowRecipientSelector() dispatch(selectRecipient({ recipient })) }, - [dispatch, onToggleShowRecipientSelector] + [dispatch, onToggleShowRecipientSelector], ) } diff --git a/packages/wallet/src/features/transactions/transfer/hooks/useOnToggleShowRecipientSelector.ts b/packages/wallet/src/features/transactions/transfer/hooks/useOnToggleShowRecipientSelector.ts index 67de95f0d9b..46d78e79839 100644 --- a/packages/wallet/src/features/transactions/transfer/hooks/useOnToggleShowRecipientSelector.ts +++ b/packages/wallet/src/features/transactions/transfer/hooks/useOnToggleShowRecipientSelector.ts @@ -11,13 +11,11 @@ export function useOnToggleShowRecipientSelector(dispatch: React.Dispatch -): (show: boolean) => void { +export function useSetShowRecipientSelector(dispatch: React.Dispatch): (show: boolean) => void { return useCallback( (show: boolean) => { dispatch(setShowRecipientSelector(show)) }, - [dispatch] + [dispatch], ) } diff --git a/packages/wallet/src/features/transactions/transfer/hooks/useShowSendNetworkNotification.ts b/packages/wallet/src/features/transactions/transfer/hooks/useShowSendNetworkNotification.ts index 1113c2acbeb..40a90a2e6d9 100644 --- a/packages/wallet/src/features/transactions/transfer/hooks/useShowSendNetworkNotification.ts +++ b/packages/wallet/src/features/transactions/transfer/hooks/useShowSendNetworkNotification.ts @@ -22,7 +22,7 @@ export function useShowSendNetworkNotification({ chainId }: { chainId?: WalletCh type: AppNotificationType.NetworkChanged, chainId, flow: 'send', - }) + }), ) }, ONE_SECOND_MS / 2) }, [chainId, prevChainId, dispatch]) diff --git a/packages/wallet/src/features/transactions/transfer/hooks/useTransferCallback.ts b/packages/wallet/src/features/transactions/transfer/hooks/useTransferCallback.ts index 6e0cd1af77e..c15c3adb690 100644 --- a/packages/wallet/src/features/transactions/transfer/hooks/useTransferCallback.ts +++ b/packages/wallet/src/features/transactions/transfer/hooks/useTransferCallback.ts @@ -15,7 +15,7 @@ export function useTransferERC20Callback( tokenAddress?: Address, amountInWei?: string, transferTxWithGasSettings?: providers.TransactionRequest, - onSubmit?: () => void + onSubmit?: () => void, ): (() => void) | null { const account = useActiveAccount() @@ -32,7 +32,7 @@ export function useTransferERC20Callback( } : undefined, transferTxWithGasSettings, - onSubmit + onSubmit, ) } @@ -44,7 +44,7 @@ export function useTransferNFTCallback( tokenAddress?: Address, tokenId?: string, txRequest?: providers.TransactionRequest, - onSubmit?: () => void + onSubmit?: () => void, ): (() => void) | null { const account = useActiveAccount() @@ -61,7 +61,7 @@ export function useTransferNFTCallback( } : undefined, txRequest, - onSubmit + onSubmit, ) } @@ -69,7 +69,7 @@ export function useTransferNFTCallback( function useTransferCallback( transferTokenParams?: TransferTokenParams, txRequest?: providers.TransactionRequest, - onSubmit?: () => void + onSubmit?: () => void, ): null | (() => void) { const dispatch = useAppDispatch() diff --git a/packages/wallet/src/features/transactions/transfer/hooks/useTransferTransactionRequest.ts b/packages/wallet/src/features/transactions/transfer/hooks/useTransferTransactionRequest.ts index 8e44bbea5b6..dedbcc098fd 100644 --- a/packages/wallet/src/features/transactions/transfer/hooks/useTransferTransactionRequest.ts +++ b/packages/wallet/src/features/transactions/transfer/hooks/useTransferTransactionRequest.ts @@ -4,10 +4,11 @@ import ERC1155_ABI from 'uniswap/src/abis/erc1155.json' import ERC20_ABI from 'uniswap/src/abis/erc20.json' import ERC721_ABI from 'uniswap/src/abis/erc721.json' import { Erc1155, Erc20, Erc721 } from 'uniswap/src/abis/types' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { UniverseChainId } from 'uniswap/src/types/chains' +import { currencyAddress, isNativeCurrencyAddress } from 'uniswap/src/utils/currencyId' import { useAsyncData } from 'utilities/src/react/hooks' import { AssetType } from 'wallet/src/entities/assets' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' import { ContractManager } from 'wallet/src/features/contracts/ContractManager' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { @@ -19,10 +20,9 @@ import { import { Account } from 'wallet/src/features/wallet/accounts/types' import { useContractManager, useProvider } from 'wallet/src/features/wallet/context' import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks' -import { currencyAddress, isNativeCurrencyAddress } from 'wallet/src/utils/currencyId' export function useTransferTransactionRequest( - derivedTransferInfo: DerivedTransferInfo + derivedTransferInfo: DerivedTransferInfo, ): providers.TransactionRequest | undefined { const account = useActiveAccountWithThrow() const chainId = toSupportedChainId(derivedTransferInfo.chainId) @@ -44,7 +44,7 @@ async function getTransferTransaction( provider: providers.Provider, contractManager: ContractManager, account: Account, - derivedTransferInfo: DerivedTransferInfo + derivedTransferInfo: DerivedTransferInfo, ): Promise { const params = getTransferParams(account, derivedTransferInfo) if (!params) { @@ -66,13 +66,10 @@ async function getTransferTransaction( function getTransferParams( account: Account, - derivedTransferInfo: DerivedTransferInfo + derivedTransferInfo: DerivedTransferInfo, ): TransferTokenParams | undefined { - const { currencyAmounts, currencyTypes, chainId, recipient, currencyInInfo, nftIn } = - derivedTransferInfo - const tokenAddress = currencyInInfo - ? currencyAddress(currencyInInfo.currency) - : nftIn?.nftContract?.address + const { currencyAmounts, currencyTypes, chainId, recipient, currencyInInfo, nftIn } = derivedTransferInfo + const tokenAddress = currencyInInfo ? currencyAddress(currencyInInfo.currency) : nftIn?.nftContract?.address const amount = currencyAmounts[CurrencyField.INPUT]?.quotient.toString() const assetType = currencyTypes[CurrencyField.INPUT] @@ -117,20 +114,11 @@ function getTransferParams( async function getErc721TransferRequest( params: TransferNFTParams, provider: providers.Provider, - contractManager: ContractManager + contractManager: ContractManager, ): Promise { const { chainId, account, toAddress, tokenAddress, tokenId } = params - const erc721Contract = contractManager.getOrCreateContract( - chainId, - tokenAddress, - provider, - ERC721_ABI - ) - const baseRequest = await erc721Contract.populateTransaction.transferFrom( - account.address, - toAddress, - tokenId - ) + const erc721Contract = contractManager.getOrCreateContract(chainId, tokenAddress, provider, ERC721_ABI) + const baseRequest = await erc721Contract.populateTransaction.transferFrom(account.address, toAddress, tokenId) return { ...baseRequest, @@ -142,15 +130,10 @@ async function getErc721TransferRequest( async function getErc1155TransferRequest( params: TransferNFTParams, provider: providers.Provider, - contractManager: ContractManager + contractManager: ContractManager, ): Promise { const { chainId, account, toAddress, tokenAddress, tokenId } = params - const erc1155Contract = contractManager.getOrCreateContract( - chainId, - tokenAddress, - provider, - ERC1155_ABI - ) + const erc1155Contract = contractManager.getOrCreateContract(chainId, tokenAddress, provider, ERC1155_ABI) // TODO: [MOB-242] handle `non ERC1155 Receiver implement` error const baseRequest = await erc1155Contract.populateTransaction.safeTransferFrom( @@ -158,7 +141,7 @@ async function getErc1155TransferRequest( toAddress, tokenId, /*amount=*/ '1', - /*data=*/ '0x0' + /*data=*/ '0x0', ) return { @@ -181,19 +164,12 @@ function getNativeTransferRequest(params: TransferCurrencyParams): providers.Tra export async function getTokenTransferRequest( params: TransferCurrencyParams, provider: providers.Provider, - contractManager: ContractManager + contractManager: ContractManager, ): Promise { const { account, toAddress, chainId, tokenAddress, amountInWei } = params - const tokenContract = contractManager.getOrCreateContract( - chainId, - tokenAddress, - provider, - ERC20_ABI - ) - const transactionRequest = await tokenContract.populateTransaction.transfer( - toAddress, - amountInWei, - { from: account.address } - ) + const tokenContract = contractManager.getOrCreateContract(chainId, tokenAddress, provider, ERC20_ABI) + const transactionRequest = await tokenContract.populateTransaction.transfer(toAddress, amountInWei, { + from: account.address, + }) return { ...transactionRequest, chainId } } diff --git a/packages/wallet/src/features/transactions/transfer/hooks/useTransferWarnings.ts b/packages/wallet/src/features/transactions/transfer/hooks/useTransferWarnings.ts index c7517d4a2e0..6a3939bc124 100644 --- a/packages/wallet/src/features/transactions/transfer/hooks/useTransferWarnings.ts +++ b/packages/wallet/src/features/transactions/transfer/hooks/useTransferWarnings.ts @@ -3,6 +3,7 @@ import { TFunction } from 'i18next' import _ from 'lodash' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { WalletChainId } from 'uniswap/src/types/chains' +import { currencyAddress } from 'uniswap/src/utils/currencyId' import { useMemoCompare } from 'utilities/src/react/hooks' import { GQLNftAsset } from 'wallet/src/features/nfts/hooks' import { getNetworkWarning } from 'wallet/src/features/transactions/WarningModal/getNetworkWarning' @@ -15,12 +16,11 @@ import { import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { DerivedTransferInfo } from 'wallet/src/features/transactions/transfer/types' import { isOffline } from 'wallet/src/features/transactions/utils' -import { currencyAddress } from 'wallet/src/utils/currencyId' export function getTransferWarnings( t: TFunction, derivedTransferInfo: DerivedTransferInfo, - offline: boolean + offline: boolean, ): Warning[] { const warnings: Warning[] = [] @@ -28,8 +28,7 @@ export function getTransferWarnings( warnings.push(getNetworkWarning(t)) } - const { currencyBalances, currencyAmounts, recipient, currencyInInfo, nftIn, chainId } = - derivedTransferInfo + const { currencyBalances, currencyAmounts, recipient, currencyInInfo, nftIn, chainId } = derivedTransferInfo const currencyBalanceIn = currencyBalances[CurrencyField.INPUT] const currencyAmountIn = currencyAmounts[CurrencyField.INPUT] @@ -39,7 +38,7 @@ export function getTransferWarnings( chainId, recipient, !!currencyAmountIn, - !!currencyBalanceIn + !!currencyBalanceIn, ) // insufficient balance @@ -69,10 +68,7 @@ export function getTransferWarnings( return warnings } -export function useTransferWarnings( - t: TFunction, - derivedTransferInfo: DerivedTransferInfo -): Warning[] { +export function useTransferWarnings(t: TFunction, derivedTransferInfo: DerivedTransferInfo): Warning[] { const networkStatus = useNetInfo() // First `useNetInfo` call always results with unknown state, // which we want to ignore here until state is determined, @@ -90,11 +86,9 @@ const checkIsMissingRequiredParams = ( chainId: WalletChainId | undefined, recipient: Address | undefined, hasCurrencyAmount: boolean, - hasCurrencyBalance: boolean + hasCurrencyBalance: boolean, ): boolean => { - const tokenAddress = currencyInInfo - ? currencyAddress(currencyInInfo.currency) - : nftIn?.nftContract?.address + const tokenAddress = currencyInInfo ? currencyAddress(currencyInInfo.currency) : nftIn?.nftContract?.address if (!tokenAddress || !chainId || !recipient) { return true diff --git a/packages/wallet/src/features/transactions/transfer/transferTokenSaga.test.ts b/packages/wallet/src/features/transactions/transfer/transferTokenSaga.test.ts index 50b4073ce96..1edf6f5fb04 100644 --- a/packages/wallet/src/features/transactions/transfer/transferTokenSaga.test.ts +++ b/packages/wallet/src/features/transactions/transfer/transferTokenSaga.test.ts @@ -2,8 +2,8 @@ import { call } from '@redux-saga/core/effects' import { BigNumber } from 'ethers' import { expectSaga } from 'redux-saga-test-plan' import * as matchers from 'redux-saga-test-plan/matchers' +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' import { DAI } from 'wallet/src/constants/tokens' import { AssetType } from 'wallet/src/entities/assets' import { sendTransaction } from 'wallet/src/features/transactions/sendTransactionSaga' diff --git a/packages/wallet/src/features/transactions/transfer/transferTokenSaga.ts b/packages/wallet/src/features/transactions/transfer/transferTokenSaga.ts index 46684c9097b..e833c60dec9 100644 --- a/packages/wallet/src/features/transactions/transfer/transferTokenSaga.ts +++ b/packages/wallet/src/features/transactions/transfer/transferTokenSaga.ts @@ -6,13 +6,13 @@ import ERC721_ABI from 'uniswap/src/abis/erc721.json' import { Erc1155, Erc20, Erc721 } from 'uniswap/src/abis/types' import { WalletEventName } from 'uniswap/src/features/telemetry/constants' import { sendAnalyticsEvent } from 'uniswap/src/features/telemetry/send' +import { isNativeCurrencyAddress } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' import { AssetType } from 'wallet/src/entities/assets' import { sendTransaction } from 'wallet/src/features/transactions/sendTransactionSaga' import { TransferTokenParams } from 'wallet/src/features/transactions/transfer/types' import { SendTokenTransactionInfo, TransactionType } from 'wallet/src/features/transactions/types' import { getContractManager, getProvider } from 'wallet/src/features/wallet/context' -import { isNativeCurrencyAddress } from 'wallet/src/utils/currencyId' import { createMonitoredSaga } from 'wallet/src/utils/saga' type Params = { @@ -63,29 +63,15 @@ function* validateTransfer(transferTokenParams: TransferTokenParams) { switch (type) { case AssetType.ERC1155: { - const erc1155Contract = contractManager.getOrCreateContract( - chainId, - tokenAddress, - provider, - ERC1155_ABI - ) + const erc1155Contract = contractManager.getOrCreateContract(chainId, tokenAddress, provider, ERC1155_ABI) - const balance = yield* call( - erc1155Contract.balanceOf, - account.address, - transferTokenParams.tokenId - ) + const balance = yield* call(erc1155Contract.balanceOf, account.address, transferTokenParams.tokenId) validateTransferAmount('1', balance) return } case AssetType.ERC721: { - const erc721Contract = contractManager.getOrCreateContract( - chainId, - tokenAddress, - provider, - ERC721_ABI - ) + const erc721Contract = contractManager.getOrCreateContract(chainId, tokenAddress, provider, ERC721_ABI) const balance = yield* call(erc721Contract.balanceOf, account.address) validateTransferAmount('1', balance) return @@ -97,12 +83,7 @@ function* validateTransfer(transferTokenParams: TransferTokenParams) { return } - const tokenContract = contractManager.getOrCreateContract( - chainId, - tokenAddress, - provider, - ERC20_ABI - ) + const tokenContract = contractManager.getOrCreateContract(chainId, tokenAddress, provider, ERC20_ABI) const currentBalance = yield* call(tokenContract.balanceOf, account.address) validateTransferAmount(transferTokenParams.amountInWei, currentBalance) } diff --git a/packages/wallet/src/features/transactions/types.ts b/packages/wallet/src/features/transactions/types.ts index 69906d2cc9f..a569c492bda 100644 --- a/packages/wallet/src/features/transactions/types.ts +++ b/packages/wallet/src/features/transactions/types.ts @@ -20,9 +20,7 @@ export enum WrapType { Unwrap, } -export type ChainIdToTxIdToDetails = Partial< - Record -> +export type ChainIdToTxIdToDetails = Partial> // Basic identifying info for a transaction export interface TransactionId { @@ -56,10 +54,19 @@ interface BaseTransactionDetails extends TransactionId { // it should contain all the appropriate gas details in order // to get submitted first cancelRequest?: providers.TransactionRequest + + networkFee?: TransactionNetworkFee +} + +export type TransactionNetworkFee = { + quantity: string + tokenSymbol: string + tokenAddress: string + chainId: WalletChainId } export interface UniswapXOrderDetails extends BaseTransactionDetails { - routing: Routing.DUTCH_V2 + routing: Routing.DUTCH_V2 | Routing.DUTCH_LIMIT // Note: `orderHash` is an off-chain value used to track orders before they're filled on-chain. // UniswapX orders will also have a transaction `hash` if they become filled. @@ -84,6 +91,8 @@ export enum TransactionStatus { Failed = 'failed', Pending = 'pending', Replacing = 'replacing', + Expired = 'expired', + InsufficientFunds = 'insufficientFunds', Unknown = 'unknown', // May want more granular options here later like InMemPool } @@ -121,6 +130,7 @@ export interface NFTSummaryInfo { name: string collectionName: string imageURL: string + address: string } export enum NFTTradeType { @@ -169,6 +179,7 @@ export interface ApproveTransactionInfo extends BaseTransactionInfo { tokenAddress: string spender: string approvalAmount?: string + dappInfo?: DappInfoTransactionDetails } export interface BaseSwapTransactionInfo extends BaseTransactionInfo { @@ -289,6 +300,7 @@ export interface NFTMintTransactionInfo extends BaseTransactionInfo { nftSummaryInfo: NFTSummaryInfo purchaseCurrencyId?: string purchaseCurrencyAmountRaw?: string + dappInfo?: DappInfoTransactionDetails } export interface NFTTradeTransactionInfo extends BaseTransactionInfo { @@ -303,6 +315,7 @@ export interface NFTApproveTransactionInfo extends BaseTransactionInfo { type: TransactionType.NFTApprove nftSummaryInfo: NFTSummaryInfo spender: string + dappInfo?: DappInfoTransactionDetails } export interface WCConfirmInfo extends BaseTransactionInfo { @@ -310,9 +323,16 @@ export interface WCConfirmInfo extends BaseTransactionInfo { dapp: DappInfo } +export interface DappInfoTransactionDetails { + name?: string + address: string + icon?: string +} + export interface UnknownTransactionInfo extends BaseTransactionInfo { type: TransactionType.Unknown tokenAddress?: string + dappInfo?: DappInfoTransactionDetails } export type TransactionTypeInfo = @@ -332,18 +352,14 @@ export type TransactionTypeInfo = | OnRampPurchaseInfo | OnRampTransferInfo -export function isConfirmedSwapTypeInfo( - typeInfo: TransactionTypeInfo -): typeInfo is ConfirmedSwapTransactionInfo { +export function isConfirmedSwapTypeInfo(typeInfo: TransactionTypeInfo): typeInfo is ConfirmedSwapTransactionInfo { return Boolean( (typeInfo as ConfirmedSwapTransactionInfo).inputCurrencyAmountRaw && - (typeInfo as ConfirmedSwapTransactionInfo).outputCurrencyAmountRaw + (typeInfo as ConfirmedSwapTransactionInfo).outputCurrencyAmountRaw, ) } -export function isFinalizedTx( - tx: TransactionDetails | FinalizedTransactionDetails -): tx is FinalizedTransactionDetails { +export function isFinalizedTx(tx: TransactionDetails | FinalizedTransactionDetails): tx is FinalizedTransactionDetails { return ( tx.status === TransactionStatus.Success || tx.status === TransactionStatus.Failed || @@ -374,3 +390,9 @@ export interface TransferFlowProps { isFiatInput?: boolean showFiatToggle?: boolean } + +export enum TransactionDetailsType { + Transaction = 'TransactionDetails', + OnRamp = 'OnRampTransactionDetails', + UniswapXOrder = 'SwapOrderDetails', +} diff --git a/packages/wallet/src/features/transactions/utils.ts b/packages/wallet/src/features/transactions/utils.ts index 301283feee8..97981dfea09 100644 --- a/packages/wallet/src/features/transactions/utils.ts +++ b/packages/wallet/src/features/transactions/utils.ts @@ -15,7 +15,7 @@ export const MAX_FIAT_INPUT_DECIMALS = 2 export function getSerializableTransactionRequest( request: providers.TransactionRequest, - chainId?: WalletChainId + chainId?: WalletChainId, ): providers.TransactionRequest { // prettier-ignore const { to, from, nonce, gasLimit, gasPrice, data, value, maxPriorityFeePerGas, maxFeePerGas, type } = request @@ -38,7 +38,7 @@ export function getSerializableTransactionRequest( function getNativeCurrencyTotalSpend( value?: CurrencyAmount, gasFee?: string, - nativeCurrency?: NativeCurrency + nativeCurrency?: NativeCurrency, ): Maybe> { if (!gasFee || !nativeCurrency) { return value @@ -59,11 +59,7 @@ export function hasSufficientFundsIncludingGas(params: { nativeCurrencyBalance?: CurrencyAmount }): boolean { const { transactionAmount, gasFee, nativeCurrencyBalance } = params - const totalSpend = getNativeCurrencyTotalSpend( - transactionAmount, - gasFee, - nativeCurrencyBalance?.currency - ) + const totalSpend = getNativeCurrencyTotalSpend(transactionAmount, gasFee, nativeCurrencyBalance?.currency) return !totalSpend || !nativeCurrencyBalance?.lessThan(totalSpend) } @@ -88,7 +84,7 @@ export function isOffline(networkStatus: NetInfoState): boolean { // Based on the current status of the transaction, we determine the new status. export function getFinalizedTransactionStatus( currentStatus: TransactionStatus, - receiptStatus?: number + receiptStatus?: number, ): FinalizedTransactionStatus { if (!receiptStatus) { return TransactionStatus.Failed @@ -100,10 +96,7 @@ export function getFinalizedTransactionStatus( } export function getIsCancelable(tx: TransactionDetails): boolean { - if ( - tx.status === TransactionStatus.Pending && - (isUniswapX(tx) || Object.keys(tx.options?.request).length > 0) - ) { + if (tx.status === TransactionStatus.Pending && (isUniswapX(tx) || Object.keys(tx.options?.request).length > 0)) { return true } return false diff --git a/packages/wallet/src/features/trm/BlockedAddressWarning.tsx b/packages/wallet/src/features/trm/BlockedAddressWarning.tsx index aa5d7d1f3a8..d4814ca0e7d 100644 --- a/packages/wallet/src/features/trm/BlockedAddressWarning.tsx +++ b/packages/wallet/src/features/trm/BlockedAddressWarning.tsx @@ -17,24 +17,17 @@ export function BlockedAddressWarning({ return ( <> - {showBlockedAddressModal && ( - setShowBlockedAddressModal(false)} /> - )} + {showBlockedAddressModal && setShowBlockedAddressModal(false)} />} { Keyboard.dismiss() setShowBlockedAddressModal(true) - }}> + }} + > - + - {isRecipientBlocked - ? t('send.warning.blocked.recipient') - : t('send.warning.blocked.default')} + {isRecipientBlocked ? t('send.warning.blocked.recipient') : t('send.warning.blocked.default')} diff --git a/packages/wallet/src/features/trm/hooks.ts b/packages/wallet/src/features/trm/hooks.ts index c47a4ade9e0..b519a9ef03d 100644 --- a/packages/wallet/src/features/trm/hooks.ts +++ b/packages/wallet/src/features/trm/hooks.ts @@ -21,7 +21,7 @@ export function useIsBlocked(address?: string, isViewOnly = false): IsBlockedRes { ttlMs: ONE_MINUTE_MS * 5, skip: !address || isViewOnly, - } + }, ) return { diff --git a/packages/wallet/src/features/unitags/api.ts b/packages/wallet/src/features/unitags/api.ts index d749693b12a..c4fffadc3e0 100644 --- a/packages/wallet/src/features/unitags/api.ts +++ b/packages/wallet/src/features/unitags/api.ts @@ -32,12 +32,14 @@ const BASE_HEADERS = { const generateAxiosHeaders = async ( signature: string, - firebaseAppCheckToken?: string + firebaseAppCheckToken?: string, ): Promise> => { return { ...BASE_HEADERS, 'x-uni-sig': signature, - ...(firebaseAppCheckToken && { 'x-firebase-app-check': firebaseAppCheckToken }), + ...(firebaseAppCheckToken && { + 'x-firebase-app-check': firebaseAppCheckToken, + }), } } @@ -46,16 +48,18 @@ export function useUnitagClaimEligibilityQuery({ deviceId, isUsernameChange, skip, -}: UnitagClaimEligibilityParams & { skip?: boolean }): ReturnType< - typeof useRestQuery -> { +}: UnitagClaimEligibilityParams & { skip?: boolean }): ReturnType> { return useRestQuery>( - addQueryParamsToEndpoint('/claim/eligibility', { address, deviceId, isUsernameChange }), + addQueryParamsToEndpoint('/claim/eligibility', { + address, + deviceId, + isUsernameChange, + }), { address, deviceId, isUsernameChange }, // dummy body so that cache key is unique per query params ['canClaim', 'errorCode', 'message'], // return all fields { skip, ttlMs: ONE_MINUTE_MS * 2 }, 'GET', - unitagsApolloClient + unitagsApolloClient, ) } @@ -71,11 +75,9 @@ export async function getUnitagAvatarUploadUrl({ signerManager: SignerManager }): ReturnType> { const avatarUploadUrl = `${uniswapUrls.unitagsApiUrl}/username/avatar-upload-url` - const { requestParams, signature } = await createSignedRequestParams<{ username: string }>( - { username }, - account, - signerManager - ) + const { requestParams, signature } = await createSignedRequestParams<{ + username: string + }>({ username }, account, signerManager) const headers = await generateAxiosHeaders(signature) return await axios.get(avatarUploadUrl, { params: requestParams, @@ -96,7 +98,7 @@ export async function deleteUnitag({ const { requestBody, signature } = await createSignedRequestBody( { username }, account, - signerManager + signerManager, ) const headers = await generateAxiosHeaders(signature) return await axios.delete(avatarUploadUrl, { @@ -125,7 +127,7 @@ export async function updateUnitagMetadata({ clearAvatar, }, account, - signerManager + signerManager, ) const headers = await generateAxiosHeaders(signature) return await axios.put(updateMetadataUrl, requestBody, { @@ -156,7 +158,7 @@ export async function claimUnitag({ metadata, }, account, - signerManager + signerManager, ) const headers = await generateAxiosHeaders(signature, firebaseAppCheckToken) return await axios.post(claimUnitagUrl, requestBody, { @@ -182,7 +184,7 @@ export async function changeUnitag({ deviceId, }, account, - signerManager + signerManager, ) const headers = await generateAxiosHeaders(signature) return await axios.post(changeUnitagUrl, requestBody, { @@ -197,7 +199,7 @@ export async function fetchUnitagByAddresses(addresses: Address[]): Promise<{ error?: unknown }> { const unitagAddressesUrl = `${uniswapUrls.unitagsApiUrl}/addresses?addresses=${encodeURIComponent( - addresses.join(',') + addresses.join(','), )}` try { @@ -216,29 +218,9 @@ export async function fetchExtensionWaitlistEligibity(username: string): Promise data?: UnitagWaitlistPositionResponse error?: unknown }> { - const unitagWaitlistPositionUrl = `${ - uniswapUrls.unitagsApiUrl - }/waitlist/position?username=${encodeURIComponent(username)}` - - try { - const response = await axios.get(unitagWaitlistPositionUrl, { - headers: BASE_HEADERS, - }) - return { - data: response.data, - } - } catch (error) { - return { error } - } -} - -export async function fetchExtensionEligibityByAddresses(addresses: Address[]): Promise<{ - data?: UnitagWaitlistPositionResponse - error?: unknown -}> { - const unitagWaitlistPositionUrl = `${ - uniswapUrls.unitagsApiUrl - }/waitlist/position?addresses=${encodeURIComponent(addresses.join(','))}` + const unitagWaitlistPositionUrl = `${uniswapUrls.unitagsApiUrl}/waitlist/position?username=${encodeURIComponent( + username, + )}` try { const response = await axios.get(unitagWaitlistPositionUrl, { diff --git a/packages/wallet/src/features/unitags/avatars.ts b/packages/wallet/src/features/unitags/avatars.ts index 28b68f726d8..534c5e30595 100644 --- a/packages/wallet/src/features/unitags/avatars.ts +++ b/packages/wallet/src/features/unitags/avatars.ts @@ -1,9 +1,6 @@ import axios from 'axios' import { Platform } from 'react-native' -import { - UnitagAvatarUploadCredentials, - UnitagGetAvatarUploadUrlResponse, -} from 'uniswap/src/features/unitags/types' +import { UnitagAvatarUploadCredentials, UnitagGetAvatarUploadUrlResponse } from 'uniswap/src/features/unitags/types' import { logger } from 'utilities/src/logger/logger' import { getUnitagAvatarUploadUrl, updateUnitagMetadata } from 'wallet/src/features/unitags/api' import { Account } from 'wallet/src/features/wallet/accounts/types' @@ -23,7 +20,7 @@ export function isLocalFileUri(imageUri: string): boolean { export async function uploadFileToS3( imageUri: string, - creds: UnitagAvatarUploadCredentials + creds: UnitagAvatarUploadCredentials, ): Promise<{ success: boolean }> { if (!creds.preSignedUrl || !creds.s3UploadFields) { return { success: false } @@ -139,9 +136,7 @@ export const tryUploadAvatar = async ({ }): Promise<{ success: boolean; skipped: boolean }> => { const needsAvatarUpload = !!avatarImageUri && isLocalFileUri(avatarImageUri) const isPreSignedUrlReady = - !avatarUploadUrlLoading && - !!avatarUploadUrlResponse?.preSignedUrl && - !!avatarUploadUrlResponse?.s3UploadFields + !avatarUploadUrlLoading && !!avatarUploadUrlResponse?.preSignedUrl && !!avatarUploadUrlResponse?.s3UploadFields const shouldTryAvatarUpload = needsAvatarUpload && isPreSignedUrlReady if (!shouldTryAvatarUpload) { diff --git a/packages/wallet/src/features/unitags/hooks.ts b/packages/wallet/src/features/unitags/hooks.ts index 8a597e97911..937e25410a1 100644 --- a/packages/wallet/src/features/unitags/hooks.ts +++ b/packages/wallet/src/features/unitags/hooks.ts @@ -15,43 +15,26 @@ import { UnitagGetAvatarUploadUrlResponse, } from 'uniswap/src/features/unitags/types' import { UniverseChainId } from 'uniswap/src/types/chains' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { logger } from 'utilities/src/logger/logger' import { useAsyncData } from 'utilities/src/react/hooks' import { ONE_SECOND_MS } from 'utilities/src/time/time' import { getFirebaseAppCheckToken } from 'wallet/src/features/appCheck' import { selectExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/selectors' -import { - ExtensionOnboardingState, - setExtensionOnboardingState, -} from 'wallet/src/features/behaviorHistory/slice' +import { ExtensionOnboardingState, setExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/slice' import { useENS } from 'wallet/src/features/ens/useENS' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext' -import { - claimUnitag, - getUnitagAvatarUploadUrl, - useUnitagClaimEligibilityQuery, -} from 'wallet/src/features/unitags/api' -import { - isLocalFileUri, - uploadAndUpdateAvatarAfterClaim, -} from 'wallet/src/features/unitags/avatars' -import { - AVATAR_UPLOAD_CREDS_EXPIRY_SECONDS, - UNITAG_VALID_REGEX, -} from 'wallet/src/features/unitags/constants' +import { claimUnitag, getUnitagAvatarUploadUrl, useUnitagClaimEligibilityQuery } from 'wallet/src/features/unitags/api' +import { isLocalFileUri, uploadAndUpdateAvatarAfterClaim } from 'wallet/src/features/unitags/avatars' +import { AVATAR_UPLOAD_CREDS_EXPIRY_SECONDS, UNITAG_VALID_REGEX } from 'wallet/src/features/unitags/constants' import { parseUnitagErrorCode } from 'wallet/src/features/unitags/utils' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { useWalletSigners } from 'wallet/src/features/wallet/context' -import { - useAccounts, - useActiveAccount, - useActiveAccountAddressWithThrow, -} from 'wallet/src/features/wallet/hooks' +import { useAccounts, useActiveAccount, useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' import { SignerManager } from 'wallet/src/features/wallet/signing/SignerManager' import { useAppDispatch, useAppSelector } from 'wallet/src/state' -import { areAddressesEqual } from 'wallet/src/utils/addresses' const MIN_UNITAG_LENGTH = 3 const MAX_UNITAG_LENGTH = 20 @@ -87,7 +70,7 @@ export const useCanActiveAddressClaimUnitag = (): { export const useCanAddressClaimUnitag = ( address?: Address, - isUsernameChange?: boolean + isUsernameChange?: boolean, ): { canClaimUnitag: boolean; errorCode?: UnitagErrorCodes } => { const { data: deviceId } = useAsyncData(getUniqueId) const { refetchUnitagsCounter } = useUnitagUpdater() @@ -136,7 +119,7 @@ export const getUnitagFormatError = (unitag: string, t: TFunction): string | und export const useCanClaimUnitagName = ( unitagAddress: Address | undefined, - unitag: string | undefined + unitag: string | undefined, ): { error: string | undefined; loading: boolean; requiresENSMatch: boolean } => { const { t } = useTranslation() @@ -146,11 +129,7 @@ export const useCanClaimUnitagName = ( // Skip the backend calls if we found an error const unitagToSearch = error ? undefined : unitag const { loading: unitagLoading, data } = useUnitagQuery(unitagToSearch) - const { loading: ensLoading, address: ensAddress } = useENS( - UniverseChainId.Mainnet, - unitagToSearch, - true - ) + const { loading: ensLoading, address: ensAddress } = useENS(UniverseChainId.Mainnet, unitagToSearch, true) const loading = unitagLoading || ensLoading // Check for availability and ENS match @@ -172,7 +151,7 @@ export const useCanClaimUnitagName = ( export const useClaimUnitag = (): (( claim: UnitagClaim, context: UnitagClaimContext, - account?: Account + account?: Account, ) => Promise<{ claimError?: string }>) => { const { t } = useTranslation() const dispatch = useAppDispatch() @@ -236,7 +215,7 @@ export const useClaimUnitag = (): (( pushNotification({ type: AppNotificationType.Error, errorMessage: t('unitags.claim.error.avatar'), - }) + }), ) } } @@ -266,8 +245,7 @@ export const useAvatarUploadCredsWithRefresh = ({ avatarUploadUrlResponse?: UnitagGetAvatarUploadUrlResponse } => { const [avatarUploadUrlLoading, setAvatarUploadUrlLoading] = useState(false) - const [avatarUploadUrlResponse, setAvatarUploadUrlResponse] = - useState() + const [avatarUploadUrlResponse, setAvatarUploadUrlResponse] = useState() // Re-fetch the avatar upload pre-signed URL every 110 seconds to ensure it's always fresh useEffect(() => { @@ -297,10 +275,7 @@ export const useAvatarUploadCredsWithRefresh = ({ }) // Set up the interval to refetch creds 10 seconds before expiry - const intervalId = setInterval( - fetchAvatarUploadUrl, - (AVATAR_UPLOAD_CREDS_EXPIRY_SECONDS - 10) * ONE_SECOND_MS - ) + const intervalId = setInterval(fetchAvatarUploadUrl, (AVATAR_UPLOAD_CREDS_EXPIRY_SECONDS - 10) * ONE_SECOND_MS) // Clear the interval on component unmount return () => clearInterval(intervalId) @@ -315,19 +290,44 @@ export const useShowExtensionPromoBanner = (): { showExtensionPromoBanner: boolean } => { const dispatch = useAppDispatch() - const extensionOnboardingEnabled = useFeatureFlag(FeatureFlags.ExtensionOnboarding) + const extensionOnboardingEnabledBeta = useFeatureFlag(FeatureFlags.ExtensionOnboarding) + const extensionPromotionGAEnabled = useFeatureFlag(FeatureFlags.ExtensionPromotionGA) const extensionOnboardingState = useAppSelector(selectExtensionOnboardingState) const activeAccount = useActiveAccount() - const skip = - !extensionOnboardingEnabled || + const skipBetaWaitlistQuery = + extensionPromotionGAEnabled || // ignore waitlist if GA enabled + !extensionOnboardingEnabledBeta || extensionOnboardingState === ExtensionOnboardingState.Completed || !activeAccount || activeAccount.type !== AccountType.SignerMnemonic - const { data, error, loading } = useWaitlistPositionQuery([activeAccount?.address || ''], skip) + const { data, error, loading } = useWaitlistPositionQuery([activeAccount?.address || ''], skipBetaWaitlistQuery) + + /** Onboarding completed, skip all checks **/ + if (extensionOnboardingState === ExtensionOnboardingState.Completed) { + return { + error: undefined, + loading: false, + showExtensionPromoBanner: false, + } + } + + /*** GA checks first ***/ + if (extensionPromotionGAEnabled) { + if (extensionOnboardingState === ExtensionOnboardingState.Undefined) { + dispatch(setExtensionOnboardingState(ExtensionOnboardingState.ReadyToOnboard)) + } - if (skip) { + return { + error: undefined, + loading: false, + showExtensionPromoBanner: true, + } + } + + /*** Skip beta checks if we didn't query for the data ***/ + if (skipBetaWaitlistQuery) { return { error: undefined, loading: false, @@ -335,12 +335,16 @@ export const useShowExtensionPromoBanner = (): { } } - const canOnboardToExtension = data?.isAccepted ?? false + const canOnboardToExtensionBeta = data?.isAccepted ?? false - if (canOnboardToExtension && extensionOnboardingState === ExtensionOnboardingState.Undefined) { + if (canOnboardToExtensionBeta && extensionOnboardingState === ExtensionOnboardingState.Undefined) { // Store the information locally so that we don't need to check again during onboarding dispatch(setExtensionOnboardingState(ExtensionOnboardingState.ReadyToOnboard)) } - return { error: error?.message, loading, showExtensionPromoBanner: canOnboardToExtension } + return { + error: error?.message, + loading, + showExtensionPromoBanner: canOnboardToExtensionBeta, + } } diff --git a/packages/wallet/src/features/unitags/utils.ts b/packages/wallet/src/features/unitags/utils.ts index b292e312fb1..96bb20d41c0 100644 --- a/packages/wallet/src/features/unitags/utils.ts +++ b/packages/wallet/src/features/unitags/utils.ts @@ -1,11 +1,7 @@ import { TFunction } from 'i18next' import { UnitagErrorCodes } from 'uniswap/src/features/unitags/types' -export function parseUnitagErrorCode( - t: TFunction, - unitag: string, - errorCode: UnitagErrorCodes -): string { +export function parseUnitagErrorCode(t: TFunction, unitag: string, errorCode: UnitagErrorCodes): string { switch (errorCode) { case UnitagErrorCodes.UnitagNotAvailable: return t('unitags.claim.error.unavailable') diff --git a/packages/wallet/src/features/wallet/Keyring/Keyring.native.ts b/packages/wallet/src/features/wallet/Keyring/Keyring.native.ts index 4442d843cf7..a1cfb57e6d8 100644 --- a/packages/wallet/src/features/wallet/Keyring/Keyring.native.ts +++ b/packages/wallet/src/features/wallet/Keyring/Keyring.native.ts @@ -14,6 +14,10 @@ const { RNEthersRS } = NativeModules * Simple wrapper around RNEthersRS */ class NativeKeyring implements IKeyring { + removeAllMnemonicsAndPrivateKeys(): Promise { + throw new NotImplementedError('removeAllMnemonicsAndPrivateKeys') + } + isUnlocked(): Promise { throw new NotImplementedError('isUnlocked') } diff --git a/packages/wallet/src/features/wallet/Keyring/Keyring.test.ts b/packages/wallet/src/features/wallet/Keyring/Keyring.test.ts index 9a263dcf1ad..839f8cc07cf 100644 --- a/packages/wallet/src/features/wallet/Keyring/Keyring.test.ts +++ b/packages/wallet/src/features/wallet/Keyring/Keyring.test.ts @@ -152,4 +152,17 @@ describe(WebKeyring, () => { await expect(action()).rejects.toThrow() }) }) + + describe('removeAllMnemonicsAndPrivateKeys', () => { + it('removes all mnemonics', async () => { + const keyring = new WebKeyring() + await keyring.importMnemonic(SAMPLE_SEED, SAMPLE_PASSWORD) + await keyring.importMnemonic(SAMPLE_SEED, SAMPLE_PASSWORD) + + await keyring.removeAllMnemonicsAndPrivateKeys() + + const allMnemonics = await keyring.getMnemonicIds() + expect(allMnemonics).toEqual([]) + }) + }) }) diff --git a/packages/wallet/src/features/wallet/Keyring/Keyring.ts b/packages/wallet/src/features/wallet/Keyring/Keyring.ts index 7ca3a18d6b1..88e224f0800 100644 --- a/packages/wallet/src/features/wallet/Keyring/Keyring.ts +++ b/packages/wallet/src/features/wallet/Keyring/Keyring.ts @@ -4,6 +4,8 @@ import { NotImplementedError } from 'utilities/src/errors' * Provides the generation, storage, and signing logic for mnemonics and private keys. */ export interface IKeyring { + removeAllMnemonicsAndPrivateKeys(): Promise + /** @returns true if the extension is unlocked (encryption key kept in session storage can unencrypt the mnemonic) */ isUnlocked(): Promise @@ -101,6 +103,10 @@ export interface IKeyring { /** Dummy Keyring implementation. */ class NullKeyring implements IKeyring { + removeAllMnemonicsAndPrivateKeys(): Promise { + throw new Error('Method not implemented.') + } + isUnlocked(): Promise { throw new NotImplementedError('isUnlocked') } @@ -130,11 +136,7 @@ class NullKeyring implements IKeyring { } // returns the mnemonicId (derived address at index 0) of the imported mnemonic - importMnemonic( - _mnemonic: string, - _password?: string, - _allowOverwrite?: boolean - ): Promise { + importMnemonic(_mnemonic: string, _password?: string, _allowOverwrite?: boolean): Promise { throw new NotImplementedError('importMnemonic') } @@ -169,11 +171,7 @@ class NullKeyring implements IKeyring { throw new NotImplementedError('removePrivateKey') } - signTransactionHashForAddress( - _address: string, - _hash: string, - _chainId: number - ): Promise { + signTransactionHashForAddress(_address: string, _hash: string, _chainId: number): Promise { throw new NotImplementedError('signTransactionHashForAddress') } diff --git a/packages/wallet/src/features/wallet/Keyring/__mocks__/Keyring.ts b/packages/wallet/src/features/wallet/Keyring/__mocks__/Keyring.ts index 78f2fb522f5..5233ebf7b06 100644 --- a/packages/wallet/src/features/wallet/Keyring/__mocks__/Keyring.ts +++ b/packages/wallet/src/features/wallet/Keyring/__mocks__/Keyring.ts @@ -9,6 +9,10 @@ const privateKeys: { [id: string]: string } = {} const password = faker.word.noun() class MockKeyring implements IKeyring { + removeAllMnemonicsAndPrivateKeys(): Promise { + return Promise.resolve(false) + } + isUnlocked(): Promise { return Promise.resolve(false) } @@ -86,10 +90,7 @@ class MockKeyring implements IKeyring { return Promise.resolve(true) } - async signTransactionForAddress( - address: string, - transaction: providers.TransactionRequest - ): Promise { + async signTransactionForAddress(address: string, transaction: providers.TransactionRequest): Promise { const privateKey = privateKeys[address] if (!privateKey) { return Promise.reject(`No private key found for ${address}`) diff --git a/packages/wallet/src/features/wallet/Keyring/crypto.ts b/packages/wallet/src/features/wallet/Keyring/crypto.ts index 6d9a975593f..8be7880b945 100644 --- a/packages/wallet/src/features/wallet/Keyring/crypto.ts +++ b/packages/wallet/src/features/wallet/Keyring/crypto.ts @@ -36,12 +36,7 @@ interface EncryptParams { additionalData?: string } // encrypts and returns the cipher text -export async function encrypt({ - plaintext, - encryptionKey, - iv, - additionalData, -}: EncryptParams): Promise { +export async function encrypt({ plaintext, encryptionKey, iv, additionalData }: EncryptParams): Promise { const encoder = new TextEncoder() const ciphertext = await crypto.subtle.encrypt( { @@ -50,7 +45,7 @@ export async function encrypt({ additionalData: encoder.encode(additionalData), }, encryptionKey, - encoder.encode(plaintext) + encoder.encode(plaintext), ) return new Uint8Array(ciphertext).toString() } @@ -80,7 +75,7 @@ export async function decrypt({ additionalData: encoder.encode(additionalData), }, encryptionKey, - ciphertext + ciphertext, ) return decoder.decode(result) } catch (error) { @@ -102,10 +97,7 @@ export async function convertBase64SeedToCryptoKey(keyBase64: string): Promise { +export async function getEncryptionKeyFromPassword(password: string, secretPayload: SecretPayload): Promise { const { name, iterations, hash } = secretPayload const salt = decodeFromStorage(secretPayload.salt) const pbkdf2Params = { salt, name, iterations, hash } @@ -114,13 +106,10 @@ export async function getEncryptionKeyFromPassword( new TextEncoder().encode(password), PBKDF2_PARAMS.name, false, - ['deriveKey'] + ['deriveKey'], ) // TODO: This should use Argon2 like ToB recommended for the mobile app // https://github.com/Uniswap/universe/blob/main/apps/mobile/ios/EncryptionHelper.swift - return crypto.subtle.deriveKey(pbkdf2Params, keyMaterial, AES_GCM_PARAMS, true, [ - 'encrypt', - 'decrypt', - ]) + return crypto.subtle.deriveKey(pbkdf2Params, keyMaterial, AES_GCM_PARAMS, true, ['encrypt', 'decrypt']) } diff --git a/packages/wallet/src/features/wallet/accounts/editAccountSaga.ts b/packages/wallet/src/features/wallet/accounts/editAccountSaga.ts index 4788b19586c..487663735e1 100644 --- a/packages/wallet/src/features/wallet/accounts/editAccountSaga.ts +++ b/packages/wallet/src/features/wallet/accounts/editAccountSaga.ts @@ -4,10 +4,7 @@ import { unique } from 'utilities/src/primitives/array' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' import { Account, AccountType, BackupType } from 'wallet/src/features/wallet/accounts/types' import { selectAccounts } from 'wallet/src/features/wallet/selectors' -import { - editAccount as editInStore, - removeAccounts as removeAccountsInStore, -} from 'wallet/src/features/wallet/slice' +import { editAccount as editInStore, removeAccounts as removeAccountsInStore } from 'wallet/src/features/wallet/slice' import { appSelect } from 'wallet/src/state' import { createMonitoredSaga } from 'wallet/src/utils/saga' @@ -107,14 +104,7 @@ function* editAccount(params: EditAccountParams) { function* renameAccount(params: RenameParams, account: Account) { const { newName } = params - logger.debug( - 'editAccountSaga', - 'renameAccount', - 'Renaming account', - account.address, - 'to ', - newName - ) + logger.debug('editAccountSaga', 'renameAccount', 'Renaming account', account.address, 'to ', newName) yield* put( editInStore({ address: account.address, @@ -122,7 +112,7 @@ function* renameAccount(params: RenameParams, account: Account) { ...account, name: newName, }, - }) + }), ) } @@ -144,7 +134,7 @@ function* addBackupMethod(params: AddBackupMethodParams, account: Account) { const accounts = yield* appSelect(selectAccounts) const mnemonicAccounts = Object.values(accounts).filter( - (a) => a.type === AccountType.SignerMnemonic && a.mnemonicId === account.mnemonicId + (a) => a.type === AccountType.SignerMnemonic && a.mnemonicId === account.mnemonicId, ) const updatedBackups: BackupType[] = unique([...(account.backups ?? []), backupMethod]) @@ -157,16 +147,16 @@ function* addBackupMethod(params: AddBackupMethodParams, account: Account) { ...mnemonicAccount, backups: updatedBackups, }, - }) + }), ) - }) + }), ) logger.debug( 'editAccountSaga', 'addBackupMethod', 'Adding backup method', - mnemonicAccounts.map((a) => a.address) + mnemonicAccounts.map((a) => a.address), ) } @@ -180,7 +170,7 @@ function* removeBackupMethod(params: RemoveBackupMethodParams, account: Account) const accounts = yield* appSelect(selectAccounts) const mnemonicAccounts = Object.values(accounts).filter( - (a) => a.type === AccountType.SignerMnemonic && a.mnemonicId === account.mnemonicId + (a) => a.type === AccountType.SignerMnemonic && a.mnemonicId === account.mnemonicId, ) const updatedBackups = account.backups?.filter((backup) => backup !== backupMethod) @@ -194,16 +184,16 @@ function* removeBackupMethod(params: RemoveBackupMethodParams, account: Account) ...mnemonicAccount, backups: updatedBackups, }, - }) + }), ) - }) + }), ) logger.debug( 'editAccountSaga', 'removeBackupMethod', 'Removing backup method', - mnemonicAccounts.map((a) => a.address) + mnemonicAccounts.map((a) => a.address), ) } diff --git a/packages/wallet/src/features/wallet/context.tsx b/packages/wallet/src/features/wallet/context.tsx index 30dc1c505bd..d5b33566a13 100644 --- a/packages/wallet/src/features/wallet/context.tsx +++ b/packages/wallet/src/features/wallet/context.tsx @@ -1,12 +1,5 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ -import { - createContext, - PropsWithChildren, - useCallback, - useContext, - useEffect, - useState, -} from 'react' +import { createContext, PropsWithChildren, useCallback, useContext, useEffect, useState } from 'react' import { call, getContext } from 'typed-redux-saga' import { RPCType, WalletChainId } from 'uniswap/src/types/chains' import { logger } from 'utilities/src/logger/logger' @@ -42,11 +35,7 @@ export function WalletContextProvider({ children }: PropsWithChildren): // Probably not strictly necessary but more robust than relying on 'organic' re-renders const [contextVersion, updateContextVersion] = useState(0) const incrementContextVersion = useCallback(() => { - logger.debug( - 'walletContext', - 'WalletContextProvider', - `Context update count: ${contextVersion + 1}` - ) + logger.debug('walletContext', 'WalletContextProvider', `Context update count: ${contextVersion + 1}`) updateContextVersion(contextVersion + 1) }, [contextVersion, updateContextVersion]) useEffect(() => { diff --git a/packages/wallet/src/features/wallet/create/createAccountsSaga.ts b/packages/wallet/src/features/wallet/create/createAccountsSaga.ts index eaabe5e7b18..b3533dd256b 100644 --- a/packages/wallet/src/features/wallet/create/createAccountsSaga.ts +++ b/packages/wallet/src/features/wallet/create/createAccountsSaga.ts @@ -20,7 +20,7 @@ export function* createAccounts({ accounts }: CreateAccountsParams) { 'createAccountsSaga', 'createAccount', 'New accounts created:', - accounts.map((acc) => acc.address).join(',') + accounts.map((acc) => acc.address).join(','), ) } diff --git a/packages/wallet/src/features/wallet/getAccountId.ts b/packages/wallet/src/features/wallet/getAccountId.ts index 124be3b83e1..f018dc52454 100644 --- a/packages/wallet/src/features/wallet/getAccountId.ts +++ b/packages/wallet/src/features/wallet/getAccountId.ts @@ -1,4 +1,4 @@ -import { AddressStringFormat, normalizeAddress } from 'wallet/src/utils/addresses' +import { AddressStringFormat, normalizeAddress } from 'uniswap/src/utils/addresses' export function getAccountId(address: Address): string { return normalizeAddress(address, AddressStringFormat.Lowercase) diff --git a/packages/wallet/src/features/wallet/hooks.ts b/packages/wallet/src/features/wallet/hooks.ts index 04c143bed77..5a2df8231af 100644 --- a/packages/wallet/src/features/wallet/hooks.ts +++ b/packages/wallet/src/features/wallet/hooks.ts @@ -1,15 +1,12 @@ import { useMemo, useRef } from 'react' import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks' +import { getValidAddress, sanitizeAddressText, shortenAddress } from 'uniswap/src/utils/addresses' import { trimToLength } from 'utilities/src/primitives/string' import { useENSAvatar, useENSName } from 'wallet/src/features/ens/api' import useIsFocused from 'wallet/src/features/focus/useIsFocused' import { useOnboardingContext } from 'wallet/src/features/onboarding/OnboardingContext' import { UNITAG_SUFFIX } from 'wallet/src/features/unitags/constants' -import { - Account, - AccountType, - SignerMnemonicAccount, -} from 'wallet/src/features/wallet/accounts/types' +import { Account, AccountType, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' import { makeSelectAccountNotificationSetting, selectAccounts, @@ -25,7 +22,6 @@ import { import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice' import { DisplayName, DisplayNameType } from 'wallet/src/features/wallet/types' import { useAppSelector } from 'wallet/src/state' -import { getValidAddress, sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' const ENS_TRIM_LENGTH = 8 @@ -46,6 +42,11 @@ export function useAccountIfExists(address: Address): Account | undefined { return account } +export function useSignerAccountIfExists(address: Address): SignerMnemonicAccount | undefined { + const signerAccounts = useAppSelector(selectSignerMnemonicAccounts) + return signerAccounts.find((account) => account.address === address) +} + export function useSignerAccounts(): SignerMnemonicAccount[] { return useAppSelector(selectSignerMnemonicAccounts) } @@ -132,10 +133,7 @@ type DisplayNameOptions = { * @param options.includeUnitagSuffix - Whether to include the unitag suffix (.uni.eth) in returned unitag name * @param options.showLocalName - Whether to show the local wallet name */ -export function useDisplayName( - address: Maybe, - options?: DisplayNameOptions -): DisplayName | undefined { +export function useDisplayName(address: Maybe, options?: DisplayNameOptions): DisplayName | undefined { const defaultOptions = { showShortenedEns: false, includeUnitagSuffix: false, @@ -160,9 +158,7 @@ export function useDisplayName( if (overrideDisplayName) { return { - name: showShortenedEns - ? trimToLength(overrideDisplayName, ENS_TRIM_LENGTH) - : overrideDisplayName, + name: showShortenedEns ? trimToLength(overrideDisplayName, ENS_TRIM_LENGTH) : overrideDisplayName, type: DisplayNameType.ENS, } } diff --git a/packages/wallet/src/features/wallet/selectors.ts b/packages/wallet/src/features/wallet/selectors.ts index 856dd6f6fda..31a4b0a76aa 100644 --- a/packages/wallet/src/features/wallet/selectors.ts +++ b/packages/wallet/src/features/wallet/selectors.ts @@ -1,11 +1,6 @@ import { createSelector, Selector } from '@reduxjs/toolkit' import { TokenSortableField } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { - Account, - AccountType, - ReadOnlyAccount, - SignerMnemonicAccount, -} from 'wallet/src/features/wallet/accounts/types' +import { Account, AccountType, ReadOnlyAccount, SignerMnemonicAccount } from 'wallet/src/features/wallet/accounts/types' import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice' import { TokensOrderBy } from 'wallet/src/features/wallet/types' import type { RootState } from 'wallet/src/state' @@ -15,32 +10,24 @@ const DEFAULT_TOKENS_ORDER_BY = TokenSortableField.Volume export const selectAccounts = (state: RootState): Record => state.wallet.accounts export const selectSignerMnemonicAccounts = createSelector(selectAccounts, (accounts) => - Object.values(accounts).filter( - (a): a is SignerMnemonicAccount => a.type === AccountType.SignerMnemonic - ) + Object.values(accounts).filter((a): a is SignerMnemonicAccount => a.type === AccountType.SignerMnemonic), ) -export const selectSortedSignerMnemonicAccounts = createSelector( - selectSignerMnemonicAccounts, - (accounts) => - accounts.sort( - (a, b) => - (a as SignerMnemonicAccount).derivationIndex - (b as SignerMnemonicAccount).derivationIndex - ) +export const selectSortedSignerMnemonicAccounts = createSelector(selectSignerMnemonicAccounts, (accounts) => + accounts.sort((a, b) => (a as SignerMnemonicAccount).derivationIndex - (b as SignerMnemonicAccount).derivationIndex), ) export const selectSignerMnemonicAccountExists = createSelector( selectAccounts, - (accounts) => - Object.values(accounts).findIndex((value) => value.type === AccountType.SignerMnemonic) >= 0 + (accounts) => Object.values(accounts).findIndex((value) => value.type === AccountType.SignerMnemonic) >= 0, ) export const selectViewOnlyAccounts = createSelector(selectAccounts, (accounts) => - Object.values(accounts).filter((a): a is ReadOnlyAccount => a.type === AccountType.Readonly) + Object.values(accounts).filter((a): a is ReadOnlyAccount => a.type === AccountType.Readonly), ) export const selectSortedViewOnlyAccounts = createSelector(selectViewOnlyAccounts, (accounts) => - accounts.sort((a, b) => a.timeImportedMs - b.timeImportedMs) + accounts.sort((a, b) => a.timeImportedMs - b.timeImportedMs), ) // Sorted signer accounts, then sorted view-only accounts @@ -49,20 +36,17 @@ export const selectAllAccountsSorted = createSelector( selectSortedViewOnlyAccounts, (signerMnemonicAccounts, viewOnlyAccounts) => { return [...signerMnemonicAccounts, ...viewOnlyAccounts] - } + }, ) -export const selectActiveAccountAddress = (state: RootState): string | null => - state.wallet.activeAccountAddress +export const selectActiveAccountAddress = (state: RootState): string | null => state.wallet.activeAccountAddress export const selectActiveAccount = createSelector( selectAccounts, selectActiveAccountAddress, - (accounts, activeAccountAddress) => - (activeAccountAddress ? accounts[activeAccountAddress] : null) ?? null + (accounts, activeAccountAddress) => (activeAccountAddress ? accounts[activeAccountAddress] : null) ?? null, ) -export const selectFinishedOnboarding = (state: RootState): boolean | undefined => - state.wallet.finishedOnboarding +export const selectFinishedOnboarding = (state: RootState): boolean | undefined => state.wallet.finishedOnboarding export const selectTokensOrderBy = (state: RootState): TokensOrderBy => state.wallet.settings.tokensOrderBy ?? DEFAULT_TOKENS_ORDER_BY @@ -70,22 +54,24 @@ export const selectTokensOrderBy = (state: RootState): TokensOrderBy => export const selectInactiveAccounts = createSelector( selectActiveAccountAddress, selectAccounts, - (activeAddress, accounts) => - Object.values(accounts).filter((account) => account.address !== activeAddress) + (activeAddress, accounts) => Object.values(accounts).filter((account) => account.address !== activeAddress), ) export const makeSelectAccountNotificationSetting = (): Selector => createSelector( selectAccounts, (_: RootState, address: Address) => address, - (accounts, address) => !!accounts[address]?.pushNotificationsEnabled + (accounts, address) => !!accounts[address]?.pushNotificationsEnabled, ) +export const selectAnyAddressHasNotificationsEnabled = createSelector(selectAccounts, (accounts) => + Object.values(accounts).some((account) => account.pushNotificationsEnabled), +) + export const selectWalletHideSmallBalancesSetting = (state: RootState): boolean => state.wallet.settings.hideSmallBalances -export const selectWalletHideSpamTokensSetting = (state: RootState): boolean => - state.wallet.settings.hideSpamTokens +export const selectWalletHideSpamTokensSetting = (state: RootState): boolean => state.wallet.settings.hideSpamTokens export const selectWalletSwapProtectionSetting = (state: RootState): SwapProtectionSetting => state.wallet.settings.swapProtection diff --git a/packages/wallet/src/features/wallet/signing/NativeSigner.native.ts b/packages/wallet/src/features/wallet/signing/NativeSigner.native.ts index 71d7868f046..84f6cfa1e06 100644 --- a/packages/wallet/src/features/wallet/signing/NativeSigner.native.ts +++ b/packages/wallet/src/features/wallet/signing/NativeSigner.native.ts @@ -2,10 +2,10 @@ import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer' import { _TypedDataEncoder } from '@ethersproject/hash' import { Bytes, Signer, UnsignedTransaction, providers, utils } from 'ethers' import { hexlify } from 'ethers/lib/utils' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { UniverseChainId } from 'uniswap/src/types/chains' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring.native' -import { areAddressesEqual } from 'wallet/src/utils/addresses' // A signer that uses native keystore to access keys export class NativeSigner extends Signer { @@ -33,23 +33,19 @@ export class NativeSigner extends Signer { } // chainID isn't available here, but is not needed for signing hashes so just default to Mainnet - return Keyring.signHashForAddress( - this.address, - hexlify(message).slice(2), - UniverseChainId.Mainnet - ) + return Keyring.signHashForAddress(this.address, hexlify(message).slice(2), UniverseChainId.Mainnet) } // reference: https://github.com/ethers-io/ethers.js/blob/ce8f1e4015c0f27bf178238770b1325136e3351a/packages/wallet/src.ts/index.ts#L135 async _signTypedData( domain: TypedDataDomain, types: Record>, - value: Record + value: Record, ): Promise { const signature = await Keyring.signHashForAddress( this.address, _TypedDataEncoder.hash(domain, types, value).slice(2), - toSupportedChainId(domain.chainId) || UniverseChainId.Mainnet + toSupportedChainId(domain.chainId) || UniverseChainId.Mainnet, ) return signature } @@ -68,11 +64,7 @@ export class NativeSigner extends Signer { const ut = tx const hashedTx = utils.keccak256(utils.serializeTransaction(ut)) - const signature = await Keyring.signTransactionHashForAddress( - this.address, - hashedTx.slice(2), - tx.chainId - ) + const signature = await Keyring.signTransactionHashForAddress(this.address, hashedTx.slice(2), tx.chainId) return utils.serializeTransaction(ut, `0x${signature}`) } diff --git a/packages/wallet/src/features/wallet/signing/NativeSigner.ts b/packages/wallet/src/features/wallet/signing/NativeSigner.ts index fa9612b844e..bf5019a5f08 100644 --- a/packages/wallet/src/features/wallet/signing/NativeSigner.ts +++ b/packages/wallet/src/features/wallet/signing/NativeSigner.ts @@ -1,10 +1,10 @@ import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer' import { _TypedDataEncoder } from '@ethersproject/hash' import { Signer, UnsignedTransaction, providers, utils } from 'ethers' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { UniverseChainId } from 'uniswap/src/types/chains' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring' -import { areAddressesEqual } from 'wallet/src/utils/addresses' /** * A signer that uses a native keyring to access keys @@ -12,7 +12,10 @@ import { areAddressesEqual } from 'wallet/src/utils/addresses' */ export class NativeSigner extends Signer { - constructor(private readonly address: string, provider?: providers.Provider) { + constructor( + private readonly address: string, + provider?: providers.Provider, + ) { super() if (provider && !providers.Provider.isProvider(provider)) { @@ -34,12 +37,12 @@ export class NativeSigner extends Signer { async _signTypedData( domain: TypedDataDomain, types: Record>, - value: Record + value: Record, ): Promise { const signature = await Keyring.signHashForAddress( this.address, _TypedDataEncoder.hash(domain, types, value), - toSupportedChainId(domain.chainId) || UniverseChainId.Mainnet + toSupportedChainId(domain.chainId) || UniverseChainId.Mainnet, ) return signature } @@ -63,7 +66,7 @@ export class NativeSigner extends Signer { const signature = await Keyring.signTransactionHashForAddress( this.address, hashedTx, - tx.chainId || UniverseChainId.Mainnet + tx.chainId || UniverseChainId.Mainnet, ) return utils.serializeTransaction(ut, signature) diff --git a/packages/wallet/src/features/wallet/signing/signing.native.ts b/packages/wallet/src/features/wallet/signing/signing.native.ts index f83e6d7fca3..4ad6aadc8a3 100644 --- a/packages/wallet/src/features/wallet/signing/signing.native.ts +++ b/packages/wallet/src/features/wallet/signing/signing.native.ts @@ -2,18 +2,14 @@ import { ethers, TypedDataDomain, TypedDataField, Wallet } from 'ethers' import { arrayify, isHexString } from 'ethers/lib/utils' +import { ensureLeading0x } from 'uniswap/src/utils/addresses' import { Account } from 'wallet/src/features/wallet/accounts/types' import { NativeSigner } from 'wallet/src/features/wallet/signing/NativeSigner' import { SignerManager } from 'wallet/src/features/wallet/signing/SignerManager' import { EthTypedMessage } from 'wallet/src/features/wallet/signing/types' -import { ensureLeading0x } from 'wallet/src/utils/addresses' // https://docs.ethers.io/v5/api/signer/#Signer--signing-methods -export async function signMessage( - message: string, - account: Account, - signerManager: SignerManager -): Promise { +export async function signMessage(message: string, account: Account, signerManager: SignerManager): Promise { const signer = await signerManager.getSignerForAccount(account) const formattedMessage = isHexString(message) ? arrayify(message) : message const signature = await signer.signMessage(formattedMessage) @@ -25,7 +21,7 @@ export async function signTypedData( types: Record, value: Record, account: Account, - signerManager: SignerManager + signerManager: SignerManager, ): Promise { const signer = await signerManager.getSignerForAccount(account) @@ -43,7 +39,7 @@ export async function signTypedDataMessage( message: string, account: Account, signerManager: SignerManager, - _provider?: ethers.providers.JsonRpcProvider + _provider?: ethers.providers.JsonRpcProvider, ): Promise { const parsedData: EthTypedMessage = JSON.parse(message) // ethers computes EIP712Domain type for you, so we should not pass it in directly @@ -51,11 +47,5 @@ export async function signTypedDataMessage( // https://github.com/ethers-io/ethers.js/issues/687#issuecomment-714069471 delete parsedData.types.EIP712Domain - return signTypedData( - parsedData.domain, - parsedData.types, - parsedData.message, - account, - signerManager - ) + return signTypedData(parsedData.domain, parsedData.types, parsedData.message, account, signerManager) } diff --git a/packages/wallet/src/features/wallet/signing/signing.ts b/packages/wallet/src/features/wallet/signing/signing.ts index 0296c5f7027..bf22a52120d 100644 --- a/packages/wallet/src/features/wallet/signing/signing.ts +++ b/packages/wallet/src/features/wallet/signing/signing.ts @@ -2,18 +2,18 @@ import { ethers, TypedDataDomain, TypedDataField, Wallet } from 'ethers' import { arrayify, isHexString } from 'ethers/lib/utils' +import { ensureLeading0x } from 'uniswap/src/utils/addresses' import { Account } from 'wallet/src/features/wallet/accounts/types' import { NativeSigner } from 'wallet/src/features/wallet/signing/NativeSigner' import { SignerManager } from 'wallet/src/features/wallet/signing/SignerManager' import { EthTypedMessage } from 'wallet/src/features/wallet/signing/types' -import { ensureLeading0x } from 'wallet/src/utils/addresses' // https://docs.ethers.io/v5/api/signer/#Signer--signing-methods export async function signMessage( message: string, account: Account, signerManager: SignerManager, - provider?: ethers.providers.JsonRpcProvider + provider?: ethers.providers.JsonRpcProvider, ): Promise { // Mobile code does not explicitly connect to provider, // Web needs to connect to provider to ensure correct chain @@ -32,7 +32,7 @@ export async function signTypedData( value: Record, account: Account, signerManager: SignerManager, - provider?: ethers.providers.JsonRpcProvider + provider?: ethers.providers.JsonRpcProvider, ): Promise { // Mobile code does not explicitly connect to provider, // Web needs to connect to provider to ensure correct chain @@ -53,7 +53,7 @@ export async function signTypedDataMessage( message: string, account: Account, signerManager: SignerManager, - provider?: ethers.providers.JsonRpcProvider + provider?: ethers.providers.JsonRpcProvider, ): Promise { const parsedData: EthTypedMessage = JSON.parse(message) // ethers computes EIP712Domain type for you, so we should not pass it in directly @@ -61,12 +61,5 @@ export async function signTypedDataMessage( // https://github.com/ethers-io/ethers.js/issues/687#issuecomment-714069471 delete parsedData.types.EIP712Domain - return signTypedData( - parsedData.domain, - parsedData.types, - parsedData.message, - account, - signerManager, - provider - ) + return signTypedData(parsedData.domain, parsedData.types, parsedData.message, account, signerManager, provider) } diff --git a/packages/wallet/src/features/wallet/slice.ts b/packages/wallet/src/features/wallet/slice.ts index ab34bd2b3d4..14f0dc9a72d 100644 --- a/packages/wallet/src/features/wallet/slice.ts +++ b/packages/wallet/src/features/wallet/slice.ts @@ -1,7 +1,7 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { areAddressesEqual, getValidAddress } from 'uniswap/src/utils/addresses' import { Account } from 'wallet/src/features/wallet/accounts/types' import { NFTViewType, TokensOrderBy } from 'wallet/src/features/wallet/types' -import { areAddressesEqual, getValidAddress } from 'wallet/src/utils/addresses' export const HIDE_SMALL_USD_BALANCES_THRESHOLD = 1 @@ -80,9 +80,7 @@ const slice = createSlice({ // Reset active account to first account if currently active account is deleted if ( state.activeAccountAddress && - addressesToRemove.some((addressToRemove) => - areAddressesEqual(addressToRemove, state.activeAccountAddress) - ) + addressesToRemove.some((addressToRemove) => areAddressesEqual(addressToRemove, state.activeAccountAddress)) ) { const firstAccountId = Object.keys(state.accounts)[0] state.activeAccountAddress = firstAccountId ?? null @@ -112,7 +110,7 @@ const slice = createSlice({ }, setFinishedOnboarding: ( state, - { payload: { finishedOnboarding } }: PayloadAction<{ finishedOnboarding: boolean }> + { payload: { finishedOnboarding } }: PayloadAction<{ finishedOnboarding: boolean }>, ) => { state.finishedOnboarding = finishedOnboarding }, @@ -121,15 +119,13 @@ const slice = createSlice({ }, setTokensOrderBy: ( state, - { payload: { newTokensOrderBy } }: PayloadAction<{ newTokensOrderBy: TokensOrderBy }> + { payload: { newTokensOrderBy } }: PayloadAction<{ newTokensOrderBy: TokensOrderBy }>, ) => { state.settings.tokensOrderBy = newTokensOrderBy }, setSwapProtectionSetting: ( state, - { - payload: { newSwapProtectionSetting }, - }: PayloadAction<{ newSwapProtectionSetting: SwapProtectionSetting }> + { payload: { newSwapProtectionSetting } }: PayloadAction<{ newSwapProtectionSetting: SwapProtectionSetting }>, ) => { state.settings.swapProtection = newSwapProtectionSetting }, @@ -143,7 +139,7 @@ const slice = createSlice({ state, { payload: { ratingProvided, feedbackProvided }, - }: PayloadAction<{ ratingProvided?: boolean; feedbackProvided?: boolean }> + }: PayloadAction<{ ratingProvided?: boolean; feedbackProvided?: boolean }>, ) => { state.appRatingPromptedMs = Date.now() diff --git a/packages/wallet/src/provider/tamagui-provider.tsx b/packages/wallet/src/provider/tamagui-provider.tsx index 0cc97de0183..0593b4fbf1b 100644 --- a/packages/wallet/src/provider/tamagui-provider.tsx +++ b/packages/wallet/src/provider/tamagui-provider.tsx @@ -1,18 +1,16 @@ import { TamaguiProvider as OGTamaguiProvider, TamaguiProviderProps } from 'ui/src' import { config } from 'ui/src/tamagui.config' +import { isTestEnv } from 'utilities/src/environment' import { useSelectedColorScheme } from 'wallet/src/features/appearance/hooks' // without // this exported Provider is useful for tests -export function TamaguiProvider({ - children, - ...rest -}: Omit): JSX.Element { +export function TamaguiProvider({ children, ...rest }: Omit): JSX.Element { // because we dont always want to wrap all of redux for visual tests, make it default to false if in test mode // this should be done better but release needs hotfix so for now it works const userSelectedColorScheme = useSelectedColorScheme() - const isDark = process.env.NODE_ENV === 'test' ? false : userSelectedColorScheme === 'dark' + const isDark = isTestEnv() ? false : userSelectedColorScheme === 'dark' return ( diff --git a/packages/wallet/src/state/createMigrate.ts b/packages/wallet/src/state/createMigrate.ts index 7ead403b32f..fda9e9eb840 100644 --- a/packages/wallet/src/state/createMigrate.ts +++ b/packages/wallet/src/state/createMigrate.ts @@ -4,7 +4,7 @@ import { DEFAULT_VERSION } from 'redux-persist/es/constants' import { logger } from 'utilities/src/logger/logger' export function createMigrate( - migrations: MigrationManifest + migrations: MigrationManifest, ): (state: PersistedState, currentVersion: number) => Promise { return function (state: PersistedState, currentVersion: number): Promise { try { @@ -16,11 +16,7 @@ export function createMigrate( const inboundVersion: number = state._persist?.version ?? DEFAULT_VERSION if (inboundVersion === currentVersion) { - logger.debug( - 'redux-persist', - 'createMigrate', - `versions match (${currentVersion}), noop migration` - ) + logger.debug('redux-persist', 'createMigrate', `versions match (${currentVersion}), noop migration`) return Promise.resolve(state) } @@ -36,19 +32,12 @@ export function createMigrate( logger.debug('redux-persist', 'createMigrate', `migrationKeys: ${migrationKeys}`) - const migratedState: PersistedState = migrationKeys.reduce( - (versionState: PersistedState, versionKey) => { - logger.debug( - 'redux-persist', - 'createMigrate', - `running migration for versionKey: ${versionKey}` - ) - // Safe non-null assertion because `versionKey` comes from `Object.keys(migrations)` - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return migrations[versionKey]!(versionState) - }, - state - ) + const migratedState: PersistedState = migrationKeys.reduce((versionState: PersistedState, versionKey) => { + logger.debug('redux-persist', 'createMigrate', `running migration for versionKey: ${versionKey}`) + // Safe non-null assertion because `versionKey` comes from `Object.keys(migrations)` + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return migrations[versionKey]!(versionState) + }, state) return Promise.resolve(migratedState) } catch (error) { diff --git a/packages/wallet/src/state/reducer.ts b/packages/wallet/src/state/reducer.ts index d5cd1fba561..856aeb3c079 100644 --- a/packages/wallet/src/state/reducer.ts +++ b/packages/wallet/src/state/reducer.ts @@ -8,6 +8,7 @@ import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiat import { languageSettingsReducer } from 'wallet/src/features/language/slice' import { notificationReducer } from 'wallet/src/features/notifications/slice' import { searchHistoryReducer } from 'wallet/src/features/search/searchHistorySlice' +import { telemetryReducer } from 'wallet/src/features/telemetry/slice' import { timingReducer } from 'wallet/src/features/timing/slice' import { tokensReducer } from 'wallet/src/features/tokens/tokensSlice' import { transactionReducer } from 'wallet/src/features/transactions/slice' @@ -24,6 +25,7 @@ export const sharedReducers = { languageSettings: languageSettingsReducer, notifications: notificationReducer, searchHistory: searchHistoryReducer, + telemetry: telemetryReducer, timing: timingReducer, tokens: tokensReducer, transactions: transactionReducer, @@ -39,6 +41,7 @@ export const sharedPersistedStateWhitelist: Array = [ 'favorites', 'notifications', 'searchHistory', + 'telemetry', 'tokens', 'transactions', 'wallet', diff --git a/packages/wallet/src/state/saga.ts b/packages/wallet/src/state/saga.ts index d1336e138ee..0b5881bcd51 100644 --- a/packages/wallet/src/state/saga.ts +++ b/packages/wallet/src/state/saga.ts @@ -21,19 +21,14 @@ export interface MonitoredSaga { export type MonitoredSagaReducer = Reducer> -export function getMonitoredSagaReducers( - monitoredSagas: Record -): MonitoredSagaReducer { +export function getMonitoredSagaReducers(monitoredSagas: Record): MonitoredSagaReducer { return combineReducers( - Object.keys(monitoredSagas).reduce( - (acc: { [name: string]: Reducer }, sagaName: string) => { - // Safe non-null assertion because key `sagaName` comes from `Object.keys(monitoredSagas)` - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - acc[sagaName] = monitoredSagas[sagaName]!.reducer - return acc - }, - {} - ) + Object.keys(monitoredSagas).reduce((acc: { [name: string]: Reducer }, sagaName: string) => { + // Safe non-null assertion because key `sagaName` comes from `Object.keys(monitoredSagas)` + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + acc[sagaName] = monitoredSagas[sagaName]!.reducer + return acc + }, {}), ) } diff --git a/packages/wallet/src/state/sharedMigrations.ts b/packages/wallet/src/state/sharedMigrations.ts index 70285cb87ae..ba98dd43aea 100644 --- a/packages/wallet/src/state/sharedMigrations.ts +++ b/packages/wallet/src/state/sharedMigrations.ts @@ -81,7 +81,7 @@ export function activatePendingAccounts(state: any): any { (account: any) => account.type === AccountType.SignerMnemonic && account.mnemonicId === activeAccount.mnemonicId && - account.pending === true + account.pending === true, ) if (activeSignerAccountPendingWallets.length > MAX_WALLET_IMPORT) { diff --git a/packages/wallet/src/state/sharedMigrationsTests.ts b/packages/wallet/src/state/sharedMigrationsTests.ts index cc6f3f9d8f8..912291930de 100644 --- a/packages/wallet/src/state/sharedMigrationsTests.ts +++ b/packages/wallet/src/state/sharedMigrationsTests.ts @@ -492,12 +492,10 @@ export function testActivatePendingAccounts(migration: (state: any) => any, prev } const Schema8PendingAccountsActiveAddressInTheMiddleMigrated = migration( - Schema8PendingAccountsActiveAddressInTheMiddle + Schema8PendingAccountsActiveAddressInTheMiddle, ) - expect( - Object.keys(Schema8PendingAccountsActiveAddressInTheMiddleMigrated.wallet.accounts) - ).toIncludeSameMembers([ + expect(Object.keys(Schema8PendingAccountsActiveAddressInTheMiddleMigrated.wallet.accounts)).toIncludeSameMembers([ '0xTest0', '0xTest1', '0xTest2', @@ -507,12 +505,8 @@ export function testActivatePendingAccounts(migration: (state: any) => any, prev '0xTest7', '0xTest8', ]) - Object.values(Schema8PendingAccountsActiveAddressInTheMiddleMigrated.wallet.accounts).forEach( - (acc: any) => { - expect(acc.pending).toBeUndefined() - } - ) - expect(Schema8PendingAccountsActiveAddressInTheMiddleMigrated.wallet.activeAccountAddress).toBe( - '0xTest5' - ) + Object.values(Schema8PendingAccountsActiveAddressInTheMiddleMigrated.wallet.accounts).forEach((acc: any) => { + expect(acc.pending).toBeUndefined() + }) + expect(Schema8PendingAccountsActiveAddressInTheMiddleMigrated.wallet.activeAccountAddress).toBe('0xTest5') } diff --git a/packages/wallet/src/state/testUtils.ts b/packages/wallet/src/state/testUtils.ts index 26fe674f175..8c136f22ea8 100644 --- a/packages/wallet/src/state/testUtils.ts +++ b/packages/wallet/src/state/testUtils.ts @@ -10,16 +10,10 @@ export const getAllKeysOfNestedObject = (obj: Record, prefix = return [...res] } - if ( - typeof (obj as Record)[el] === 'object' && - (obj as Record)[el] !== null - ) { + if (typeof (obj as Record)[el] === 'object' && (obj as Record)[el] !== null) { return [ ...res, - ...getAllKeysOfNestedObject( - (obj as Record)[el] as Record, - prefix + el + '.' - ), + ...getAllKeysOfNestedObject((obj as Record)[el] as Record, prefix + el + '.'), ] } diff --git a/packages/wallet/src/test/fixtures/gql/activities/index.ts b/packages/wallet/src/test/fixtures/gql/activities/index.ts index 8da5c0936ab..5ffceb4d63e 100644 --- a/packages/wallet/src/test/fixtures/gql/activities/index.ts +++ b/packages/wallet/src/test/fixtures/gql/activities/index.ts @@ -50,7 +50,7 @@ export const approveAssetActivity = createFixture()(() => transactionStatus: TransactionStatus.Confirmed, assetChanges: [erc20ApproveAssetChange()], }), - }) + }), ) export const erc20SwapAssetActivity = createFixture()(() => @@ -63,7 +63,7 @@ export const erc20SwapAssetActivity = createFixture()(() => transactionStatus: TransactionStatus.Confirmed, assetChanges: [erc20TransferIn(), erc20TokenTransferOut()], }), - }) + }), ) export const erc20ReceiveAssetActivity = createFixture()(() => @@ -76,5 +76,5 @@ export const erc20ReceiveAssetActivity = createFixture()(() => transactionStatus: TransactionStatus.Confirmed, assetChanges: [erc20TransferIn()], }), - }) + }), ) diff --git a/packages/wallet/src/test/fixtures/gql/activities/tokens.ts b/packages/wallet/src/test/fixtures/gql/activities/tokens.ts index ea3dea0208e..654a3e93026 100644 --- a/packages/wallet/src/test/fixtures/gql/activities/tokens.ts +++ b/packages/wallet/src/test/fixtures/gql/activities/tokens.ts @@ -39,7 +39,7 @@ export const tokenTransfer = createFixture()(() => ({ */ export const erc20ApproveAssetChange = createFixture()(() => - tokenApproval({ asset: daiToken(), tokenStandard: TokenStandard.Erc20 }) + tokenApproval({ asset: daiToken(), tokenStandard: TokenStandard.Erc20 }), ) export const erc20TokenTransferOut = createFixture()(() => @@ -48,9 +48,9 @@ export const erc20TokenTransferOut = createFixture()(() => tokenStandard: TokenStandard.Erc20, direction: TransactionDirection.Out, transactedValue: amount({ value: 1, currency: Currency.Usd }), - }) + }), ) export const erc20TransferIn = createFixture()(() => - erc20TokenTransferOut({ direction: TransactionDirection.In }) + erc20TokenTransferOut({ direction: TransactionDirection.In }), ) diff --git a/packages/wallet/src/test/fixtures/gql/assets/tokens.ts b/packages/wallet/src/test/fixtures/gql/assets/tokens.ts index a2895ec9b8c..9ab5406123e 100644 --- a/packages/wallet/src/test/fixtures/gql/assets/tokens.ts +++ b/packages/wallet/src/test/fixtures/gql/assets/tokens.ts @@ -13,11 +13,7 @@ import { } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { toGraphQLChain } from 'uniswap/src/features/chains/utils' import { amounts } from 'wallet/src/test/fixtures/gql/amounts' -import { - get24hPriceChange, - getLatestPrice, - priceHistory, -} from 'wallet/src/test/fixtures/gql/history' +import { get24hPriceChange, getLatestPrice, priceHistory } from 'wallet/src/test/fixtures/gql/history' import { GQL_CHAINS, image } from 'wallet/src/test/fixtures/gql/misc' import { DAI, @@ -88,11 +84,9 @@ type TokenProjectMarketOptions = { priceHistory: (TimestampedAmount | undefined)[] } -export const tokenProjectMarket = createFixture( - () => ({ - priceHistory: priceHistory({ duration: HistoryDuration.Week, size: 7 }), - }) -)(({ priceHistory: history }) => ({ +export const tokenProjectMarket = createFixture(() => ({ + priceHistory: priceHistory({ duration: HistoryDuration.Week, size: 7 }), +}))(({ priceHistory: history }) => ({ __typename: 'TokenProjectMarket', id: faker.datatype.uuid(), priceHistory: history, @@ -141,7 +135,7 @@ export const usdcTokenProject = createFixture token({ sdkToken: USDBC_BASE, market: tokenMarket() }), token({ sdkToken: USDC_OPTIMISM }), ], - }) + }), ) /** @@ -155,9 +149,7 @@ const ethProject = tokenProject({ }) export const ethToken = createFixture()(() => token({ sdkToken: ETH, project: ethProject })) -export const wethToken = createFixture()(() => - token({ sdkToken: WETH, project: ethProject }) -) +export const wethToken = createFixture()(() => token({ sdkToken: WETH, project: ethProject })) const daiProject = tokenProject({ name: 'Dai Stablecoin', @@ -173,12 +165,6 @@ const usdcProject = tokenProject({ isSpam: false, }) -export const usdcToken = createFixture()(() => - token({ sdkToken: USDC, project: usdcProject }) -) -export const usdcBaseToken = createFixture()(() => - token({ sdkToken: USDBC_BASE, project: usdcProject }) -) -export const usdcArbitrumToken = createFixture()(() => - token({ sdkToken: USDC_ARBITRUM, project: usdcProject }) -) +export const usdcToken = createFixture()(() => token({ sdkToken: USDC, project: usdcProject })) +export const usdcBaseToken = createFixture()(() => token({ sdkToken: USDBC_BASE, project: usdcProject })) +export const usdcArbitrumToken = createFixture()(() => token({ sdkToken: USDC_ARBITRUM, project: usdcProject })) diff --git a/packages/wallet/src/test/fixtures/gql/history.ts b/packages/wallet/src/test/fixtures/gql/history.ts index 5105d8d72a2..56bb6927dce 100644 --- a/packages/wallet/src/test/fixtures/gql/history.ts +++ b/packages/wallet/src/test/fixtures/gql/history.ts @@ -46,7 +46,7 @@ export const priceHistory = createFixture({ +export const gqlTransactionDetails = createFixture({ transactionStatus: randomEnumValue(TransactionStatus), })(({ transactionStatus }) => ({ __typename: 'TransactionDetails', diff --git a/packages/wallet/src/test/fixtures/lib/ethers.ts b/packages/wallet/src/test/fixtures/lib/ethers.ts index 337731a33d3..73c8e0fc310 100644 --- a/packages/wallet/src/test/fixtures/lib/ethers.ts +++ b/packages/wallet/src/test/fixtures/lib/ethers.ts @@ -1,8 +1,4 @@ -import { - TransactionReceipt, - TransactionRequest, - TransactionResponse, -} from '@ethersproject/providers' +import { TransactionReceipt, TransactionRequest, TransactionResponse } from '@ethersproject/providers' import { BigNumber, Transaction } from 'ethers' import { faker } from 'wallet/src/test/shared' import { createFixture } from 'wallet/src/test/utils' diff --git a/packages/wallet/src/test/fixtures/lib/sdk.ts b/packages/wallet/src/test/fixtures/lib/sdk.ts index 4b0a93b4bb5..aa68c7eeadf 100644 --- a/packages/wallet/src/test/fixtures/lib/sdk.ts +++ b/packages/wallet/src/test/fixtures/lib/sdk.ts @@ -1,13 +1,13 @@ import { Token } from '@uniswap/sdk-core' +import { getWrappedNativeAddress } from 'uniswap/src/constants/addresses' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getWrappedNativeAddress } from 'wallet/src/constants/addresses' export const ETH = new Token( UniverseChainId.Mainnet, '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', 18, 'ETH', - 'Ethereum' + 'Ethereum', ) export const WETH = new Token( @@ -15,7 +15,7 @@ export const WETH = new Token( getWrappedNativeAddress(UniverseChainId.Mainnet), 18, 'WETH', - 'Wrapped Ether' + 'Wrapped Ether', ) export const DAI = new Token( @@ -23,7 +23,7 @@ export const DAI = new Token( '0x6b175474e89094c44da98b954eedeac495271d0f', 18, 'DAI', - 'Dai Stablecoin' + 'Dai Stablecoin', ) export const DAI_ARBITRUM_ONE = new Token( @@ -31,7 +31,7 @@ export const DAI_ARBITRUM_ONE = new Token( '0xda10009cbd5d07dd0cecc66161fc93d7c9000da1', 18, 'DAI', - 'Dai stable coin' + 'Dai stable coin', ) export const USDC = new Token( @@ -39,7 +39,7 @@ export const USDC = new Token( '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_ARBITRUM = new Token( @@ -47,7 +47,7 @@ export const USDC_ARBITRUM = new Token( '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDBC_BASE = new Token( @@ -55,7 +55,7 @@ export const USDBC_BASE = new Token( '0xd9aaec86b65d86f6a7b5b1b0c42ffa531710b6ca', 6, 'USDbC', - 'USD Base Coin' + 'USD Base Coin', ) export const USDC_OPTIMISM = new Token( @@ -63,7 +63,7 @@ export const USDC_OPTIMISM = new Token( '0x7f5c764cbc14f9669b88837ca1490cca17c31607', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_POLYGON = new Token( @@ -71,7 +71,7 @@ export const USDC_POLYGON = new Token( '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDC_GOERLI = new Token( @@ -79,7 +79,7 @@ export const USDC_GOERLI = new Token( '0x07865c6e87b9f70255377e024ace6630c1eaa37f', 6, 'USDC', - 'USD//C' + 'USD//C', ) export const USDT = new Token( @@ -87,7 +87,7 @@ export const USDT = new Token( '0xdac17f958d2ee523a2206206994597c13d831ec7', 6, 'USDT', - 'Tether USD' + 'Tether USD', ) export const USDT_BNB = new Token( @@ -95,7 +95,7 @@ export const USDT_BNB = new Token( '0x55d398326f99059ff775485246999027b3197955', 18, 'USDT', - 'TetherUSD' + 'TetherUSD', ) export const WBTC = new Token( @@ -103,7 +103,7 @@ export const WBTC = new Token( '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', 8, 'WBTC', - 'Wrapped BTC' + 'Wrapped BTC', ) export const SDK_TOKENS = [ diff --git a/packages/wallet/src/test/fixtures/wallet/accounts.ts b/packages/wallet/src/test/fixtures/wallet/accounts.ts index 634e60ef6af..56991a5981f 100644 --- a/packages/wallet/src/test/fixtures/wallet/accounts.ts +++ b/packages/wallet/src/test/fixtures/wallet/accounts.ts @@ -5,11 +5,7 @@ import { ReadOnlyAccount, SignerMnemonicAccount, } from 'wallet/src/features/wallet/accounts/types' -import { - SAMPLE_SEED_ADDRESS_1, - SAMPLE_SEED_ADDRESS_2, - SAMPLE_SEED_ADDRESS_3, -} from 'wallet/src/test/fixtures/constants' +import { SAMPLE_SEED_ADDRESS_1, SAMPLE_SEED_ADDRESS_2, SAMPLE_SEED_ADDRESS_3 } from 'wallet/src/test/fixtures/constants' import { faker } from 'wallet/src/test/shared' import { createFixture, randomEnumValue } from 'wallet/src/test/utils' diff --git a/packages/wallet/src/test/fixtures/wallet/balances.ts b/packages/wallet/src/test/fixtures/wallet/balances.ts index b1a25707053..51e3f12ee35 100644 --- a/packages/wallet/src/test/fixtures/wallet/balances.ts +++ b/packages/wallet/src/test/fixtures/wallet/balances.ts @@ -1,17 +1,13 @@ -import { - Portfolio, - Token, - TokenBalance, -} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { Portfolio, Token, TokenBalance } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { fromGraphQLChain } from 'uniswap/src/features/chains/utils' import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' -import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { currencyId } from 'uniswap/src/utils/currencyId' import { buildCurrency } from 'wallet/src/features/dataApi/utils' import { portfolio } from 'wallet/src/test/fixtures/gql' import { tokenBalance } from 'wallet/src/test/fixtures/gql/assets' import { currencyInfo } from 'wallet/src/test/fixtures/wallet/currencies' import { faker } from 'wallet/src/test/shared' import { createFixture } from 'wallet/src/test/utils' -import { currencyId } from 'wallet/src/utils/currencyId' const portfolioBalanceBase = createFixture()(() => ({ cacheId: faker.datatype.uuid(), @@ -71,9 +67,9 @@ type PortfolioBalancesOptions = { portfolio: Portfolio } -export const portfolioBalances = createFixture( - () => ({ portfolio: portfolio() }) -)( +export const portfolioBalances = createFixture(() => ({ + portfolio: portfolio(), +}))( ({ portfolio: { tokenBalances } }) => (tokenBalances ?.map((balance) => { @@ -83,5 +79,5 @@ export const portfolioBalances = createFixture()(() => currencyInfo({ nativeCurrency: MAINNET_CURRENCY, logoUrl: 'https://token-icons.s3.amazonaws.com/eth.png', - }) + }), ) export const uniCurrencyInfo = createFixture()(() => @@ -41,7 +41,7 @@ export const uniCurrencyInfo = createFixture()(() => nativeCurrency: MAINNET_CURRENCY, logoUrl: 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png', - }) + }), ) export const daiCurrencyInfo = createFixture()(() => @@ -49,7 +49,7 @@ export const daiCurrencyInfo = createFixture()(() => nativeCurrency: MAINNET_CURRENCY, logoUrl: 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png', - }) + }), ) export const arbitrumDaiCurrencyInfo = createFixture()(() => @@ -57,14 +57,14 @@ export const arbitrumDaiCurrencyInfo = createFixture()(() => nativeCurrency: ARBITRUM_CURRENCY, logoUrl: 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png', - }) + }), ) export const usdcCurrencyInfo = createFixture()(() => currencyInfo({ nativeCurrency: BASE_CURRENCY, logoUrl: null, - }) + }), ) export const ETH_CURRENCY_INFO = ethCurrencyInfo() diff --git a/packages/wallet/src/test/fixtures/wallet/notifications.ts b/packages/wallet/src/test/fixtures/wallet/notifications.ts index 0027e43470c..e63d2f858d8 100644 --- a/packages/wallet/src/test/fixtures/wallet/notifications.ts +++ b/packages/wallet/src/test/fixtures/wallet/notifications.ts @@ -101,15 +101,13 @@ export const wrapTxNotification = createFixture()(() => ({ unwrapped: faker.datatype.boolean(), })) -const transferCurrencyTxNotificationBase = createFixture()( - () => ({ - ...transactionNotificationBase(), - txType: randomChoice([TransactionType.Send, TransactionType.Receive]), - assetType: AssetType.Currency, - tokenAddress: faker.finance.ethereumAddress(), - currencyAmountRaw: faker.datatype.number().toString(), - }) -) +const transferCurrencyTxNotificationBase = createFixture()(() => ({ + ...transactionNotificationBase(), + txType: randomChoice([TransactionType.Send, TransactionType.Receive]), + assetType: AssetType.Currency, + tokenAddress: faker.finance.ethereumAddress(), + currencyAmountRaw: faker.datatype.number().toString(), +})) export const sendCurrencyTxNotification = createFixture()(() => ({ ...transferCurrencyTxNotificationBase(), @@ -174,14 +172,12 @@ export const chooseCountryNotification = createFixture()( - () => ({ - ...appNotificationBase(), - type: AppNotificationType.AssetVisibility, - visible: faker.datatype.boolean(), - assetName: faker.lorem.words(), - }) -) +export const changeAssetVisibilityNotifiation = createFixture()(() => ({ + ...appNotificationBase(), + type: AppNotificationType.AssetVisibility, + visible: faker.datatype.boolean(), + assetName: faker.lorem.words(), +})) export const swapPendingNotification = createFixture()(() => ({ ...appNotificationBase(), @@ -189,16 +185,13 @@ export const swapPendingNotification = createFixture()( wrapType: randomEnumValue(WrapType), })) -export const transferCurrencyPendingNotification = - createFixture()(() => ({ - ...appNotificationBase(), - type: AppNotificationType.TransferCurrencyPending, - currencyInfo: currencyInfo(), - })) +export const transferCurrencyPendingNotification = createFixture()(() => ({ + ...appNotificationBase(), + type: AppNotificationType.TransferCurrencyPending, + currencyInfo: currencyInfo(), +})) -export const scantasticCompleteNotification = createFixture()( - () => ({ - ...appNotificationBase(), - type: AppNotificationType.ScantasticComplete, - }) -) +export const scantasticCompleteNotification = createFixture()(() => ({ + ...appNotificationBase(), + type: AppNotificationType.ScantasticComplete, +})) diff --git a/packages/wallet/src/test/fixtures/wallet/recipients.ts b/packages/wallet/src/test/fixtures/wallet/recipients.ts index 10adcdf56c8..5050ef16ac6 100644 --- a/packages/wallet/src/test/fixtures/wallet/recipients.ts +++ b/packages/wallet/src/test/fixtures/wallet/recipients.ts @@ -12,10 +12,7 @@ type RecipientSectionOptions = { addresses: string[] } -export const recipientSection = createFixture< - SectionListData, - RecipientSectionOptions ->(() => ({ +export const recipientSection = createFixture, RecipientSectionOptions>(() => ({ addresses: [faker.finance.ethereumAddress(), faker.finance.ethereumAddress()], }))(({ addresses }) => ({ title: faker.lorem.words(), diff --git a/packages/wallet/src/test/fixtures/wallet/redux.ts b/packages/wallet/src/test/fixtures/wallet/redux.ts index b79c1a00bd8..84bd7b0abff 100644 --- a/packages/wallet/src/test/fixtures/wallet/redux.ts +++ b/packages/wallet/src/test/fixtures/wallet/redux.ts @@ -21,9 +21,8 @@ type PreloadedSharedStateOptions = { account: Account | undefined } -export const preloadedSharedState = createFixture< - PreloadedState, - PreloadedSharedStateOptions ->({ account: undefined })(({ account }) => ({ +export const preloadedSharedState = createFixture, PreloadedSharedStateOptions>({ + account: undefined, +})(({ account }) => ({ wallet: preloadedWalletState({ account }), })) diff --git a/packages/wallet/src/test/fixtures/wallet/transactions/fixtures.ts b/packages/wallet/src/test/fixtures/wallet/transactions/fixtures.ts index 34dc362f588..9bea3dbcfdb 100644 --- a/packages/wallet/src/test/fixtures/wallet/transactions/fixtures.ts +++ b/packages/wallet/src/test/fixtures/wallet/transactions/fixtures.ts @@ -43,6 +43,7 @@ export const nftSummaryInfo = createFixture()(() => ({ name: faker.lorem.words(), collectionName: faker.lorem.words(), imageURL: faker.image.imageUrl(), + address: faker.finance.ethereumAddress(), })) export const approveTransactionInfo = createFixture()(() => ({ @@ -57,25 +58,21 @@ export const baseSwapTransactionInfo = createFixture()( outputCurrencyId: faker.datatype.uuid(), })) -export const extractInputSwapTransactionInfo = createFixture()( - () => ({ - ...baseSwapTransactionInfo(), - tradeType: TradeType.EXACT_INPUT, - inputCurrencyAmountRaw: faker.datatype.number().toString(), - expectedOutputCurrencyAmountRaw: faker.datatype.number().toString(), - minimumOutputCurrencyAmountRaw: faker.datatype.number().toString(), - }) -) - -export const extractOutputSwapTransactionInfo = createFixture()( - () => ({ - ...baseSwapTransactionInfo(), - tradeType: TradeType.EXACT_OUTPUT, - outputCurrencyAmountRaw: faker.datatype.number().toString(), - expectedInputCurrencyAmountRaw: faker.datatype.number().toString(), - maximumInputCurrencyAmountRaw: faker.datatype.number().toString(), - }) -) +export const extractInputSwapTransactionInfo = createFixture()(() => ({ + ...baseSwapTransactionInfo(), + tradeType: TradeType.EXACT_INPUT, + inputCurrencyAmountRaw: faker.datatype.number().toString(), + expectedOutputCurrencyAmountRaw: faker.datatype.number().toString(), + minimumOutputCurrencyAmountRaw: faker.datatype.number().toString(), +})) + +export const extractOutputSwapTransactionInfo = createFixture()(() => ({ + ...baseSwapTransactionInfo(), + tradeType: TradeType.EXACT_OUTPUT, + outputCurrencyAmountRaw: faker.datatype.number().toString(), + expectedInputCurrencyAmountRaw: faker.datatype.number().toString(), + maximumInputCurrencyAmountRaw: faker.datatype.number().toString(), +})) export const confirmedSwapTransactionInfo = createFixture()(() => ({ ...baseSwapTransactionInfo(), @@ -169,9 +166,7 @@ export const transactionReceipt = createFixture()(() => ({ effectiveGasPrice: faker.datatype.number(), })) -export const finalizedTransactionAction = createFixture>()( - () => ({ - payload: finalizedTransactionDetails(), - type: 'transactions/finalizeTransaction', - }) -) +export const finalizedTransactionAction = createFixture>()(() => ({ + payload: finalizedTransactionDetails(), + type: 'transactions/finalizeTransaction', +})) diff --git a/packages/wallet/src/test/fixtures/wallet/transactions/helpers.ts b/packages/wallet/src/test/fixtures/wallet/transactions/helpers.ts index 6abaa1034aa..54b9e1b364f 100644 --- a/packages/wallet/src/test/fixtures/wallet/transactions/helpers.ts +++ b/packages/wallet/src/test/fixtures/wallet/transactions/helpers.ts @@ -32,9 +32,7 @@ type TxFixtures = { finalizedTxAction: ReturnType } -export const getTxFixtures = ( - transaction?: T -): TxFixtures => { +export const getTxFixtures = (transaction?: T): TxFixtures => { const txBase = merge( {}, transactionDetails({ @@ -43,7 +41,7 @@ export const getTxFixtures = ( request: ethersTransactionRequest(), }, }), - transaction + transaction, ) // Transaction flow diff --git a/packages/wallet/src/test/mocks/gql/provider.tsx b/packages/wallet/src/test/mocks/gql/provider.tsx index c41d938cc8f..f0f228f6124 100644 --- a/packages/wallet/src/test/mocks/gql/provider.tsx +++ b/packages/wallet/src/test/mocks/gql/provider.tsx @@ -12,10 +12,7 @@ import { getErrorLink, getRestLink } from 'wallet/src/data/links' import { mocks as defaultMocks } from 'wallet/src/test/mocks/gql/mocks' import { defaultResolvers } from 'wallet/src/test/mocks/gql/resolvers' -const GQL_SCHEMA_PATH = path.join( - __dirname, - '../../../../../uniswap/src/data/graphql/uniswap-data-api/schema.graphql' -) +const GQL_SCHEMA_PATH = path.join(__dirname, '../../../../../uniswap/src/data/graphql/uniswap-data-api/schema.graphql') const baseSchema = loadSchemaSync(GQL_SCHEMA_PATH, { loaders: [new GraphQLFileLoader()] }) diff --git a/packages/wallet/src/test/mocks/gql/resolvers.ts b/packages/wallet/src/test/mocks/gql/resolvers.ts index 53b4f814e29..f5dbbbcd580 100644 --- a/packages/wallet/src/test/mocks/gql/resolvers.ts +++ b/packages/wallet/src/test/mocks/gql/resolvers.ts @@ -1,8 +1,5 @@ import { GraphQLJSON } from 'graphql-scalars' -import { - HistoryDuration, - Resolvers, -} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { HistoryDuration, Resolvers } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { priceHistory, tokenProject } from 'wallet/src/test/fixtures' export const defaultResolvers: Resolvers = { diff --git a/packages/wallet/src/test/mocks/providers.ts b/packages/wallet/src/test/mocks/providers.ts index 836a08bb1c5..efda1df14e1 100644 --- a/packages/wallet/src/test/mocks/providers.ts +++ b/packages/wallet/src/test/mocks/providers.ts @@ -3,8 +3,8 @@ import { BigNumber, providers } from 'ethers' import ERC20_ABI from 'uniswap/src/abis/erc20.json' import { Erc20 } from 'uniswap/src/abis/types' import WETH_ABI from 'uniswap/src/abis/weth.json' +import { getWrappedNativeAddress } from 'uniswap/src/constants/addresses' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' -import { getWrappedNativeAddress } from 'wallet/src/constants/addresses' import { DAI } from 'wallet/src/constants/tokens' import { ContractManager } from 'wallet/src/features/contracts/ContractManager' import { SignerManager } from 'wallet/src/features/wallet/signing/SignerManager' @@ -62,12 +62,9 @@ contractManager.getOrCreateContract( UniverseChainId.Mainnet, getWrappedNativeAddress(UniverseChainId.Mainnet), provider, - WETH_ABI + WETH_ABI, ) -export const tokenContract = contractManager.getContract( - UniverseChainId.Mainnet, - DAI.address -) as Erc20 +export const tokenContract = contractManager.getContract(UniverseChainId.Mainnet, DAI.address) as Erc20 export const mockTokenContract = { balanceOf: (): BigNumber => BigNumber.from('1000000000000000000'), diff --git a/packages/wallet/src/test/mocks/sdk.ts b/packages/wallet/src/test/mocks/sdk.ts index c72b94b9dae..15d4cb1c6df 100644 --- a/packages/wallet/src/test/mocks/sdk.ts +++ b/packages/wallet/src/test/mocks/sdk.ts @@ -8,5 +8,5 @@ export const mockPool = new Pool( FeeAmount.HIGH, '2437312313659959819381354528', '10272714736694327408', - -69633 + -69633, ) diff --git a/packages/wallet/src/test/mocks/utils.ts b/packages/wallet/src/test/mocks/utils.ts index e2105c381c8..36643e7910f 100644 --- a/packages/wallet/src/test/mocks/utils.ts +++ b/packages/wallet/src/test/mocks/utils.ts @@ -73,11 +73,7 @@ export const mockFiatConverter: LocalizationContextState = { formatPercent(_: Maybe): string { throw new Error('Function not implemented.') }, - addFiatSymbolToNumber(_: { - value: Maybe - currencyCode: string - currencySymbol: string - }): string { + addFiatSymbolToNumber(_: { value: Maybe; currencyCode: string; currencySymbol: string }): string { throw new Error('Function not implemented.') }, } diff --git a/packages/wallet/src/test/render.tsx b/packages/wallet/src/test/render.tsx index 046e64e4e02..4eca5747905 100644 --- a/packages/wallet/src/test/render.tsx +++ b/packages/wallet/src/test/render.tsx @@ -13,6 +13,7 @@ import { import React, { PropsWithChildren } from 'react' import { Resolvers } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { UnitagUpdaterContextProvider } from 'uniswap/src/features/unitags/context' +import { WalletNavigationContextState, WalletNavigationProvider } from 'wallet/src/contexts/WalletNavigationContext' import { SharedProvider } from 'wallet/src/provider' import { sharedRootReducer, type SharedState } from 'wallet/src/state/reducer' import { AutoMockedApolloProvider } from 'wallet/src/test/mocks' @@ -26,6 +27,20 @@ type ExtendedRenderOptions = RenderOptions & { store?: EnhancedStore } +const mockNavigationFunctions: WalletNavigationContextState = { + navigateToAccountActivityList: jest.fn(), + navigateToAccountTokenList: jest.fn(), + navigateToBuyOrReceiveWithEmptyWallet: jest.fn(), + navigateToNftDetails: jest.fn(), + navigateToNftCollection: jest.fn(), + navigateToSwapFlow: jest.fn(), + navigateToTokenDetails: jest.fn(), + navigateToReceive: jest.fn(), + navigateToSend: jest.fn(), + handleShareNft: jest.fn(), + handleShareToken: jest.fn(), +} + /** * * @param ui Component to render @@ -46,7 +61,7 @@ export function renderWithProviders( middleware: (getDefaultMiddleware) => getDefaultMiddleware(), }), ...renderOptions - }: ExtendedRenderOptions = {} + }: ExtendedRenderOptions = {}, ): RenderResult & { store: EnhancedStore } { @@ -54,7 +69,9 @@ export function renderWithProviders( return ( - {children} + + {children} + ) @@ -84,13 +101,13 @@ type RenderHookWithProvidersResult = // Don't require hookOptions if hook doesn't take any arguments export function renderHookWithProviders( hook: () => R, - hookOptions?: ExtendedRenderHookOptions + hookOptions?: ExtendedRenderHookOptions, ): RenderHookWithProvidersResult // Require hookOptions if hook takes arguments export function renderHookWithProviders( hook: (...args: P) => R, - hookOptions: ExtendedRenderHookOptions

+ hookOptions: ExtendedRenderHookOptions

, ): RenderHookWithProvidersResult /** @@ -102,7 +119,7 @@ export function renderHookWithProviders( */ export function renderHookWithProviders

( hook: (...args: P) => R, - hookOptions?: ExtendedRenderHookOptions

+ hookOptions?: ExtendedRenderHookOptions

, ): RenderHookWithProvidersResult { const { cache, diff --git a/packages/wallet/src/test/utils/array.ts b/packages/wallet/src/test/utils/array.ts index d5667a30773..42c417223ba 100644 --- a/packages/wallet/src/test/utils/array.ts +++ b/packages/wallet/src/test/utils/array.ts @@ -22,10 +22,7 @@ * console.log(strings); // ["hello", "hello"] * ``` */ -export const createArray = ( - length: L, - factory: (index: number) => T -): ArrayOfLength => { +export const createArray = (length: L, factory: (index: number) => T): ArrayOfLength => { const result = [] for (let i = 0; i < length; i++) { result.push(factory(i)) diff --git a/packages/wallet/src/test/utils/factory.ts b/packages/wallet/src/test/utils/factory.ts index 72f171707b9..5e44e7812c1 100644 --- a/packages/wallet/src/test/utils/factory.ts +++ b/packages/wallet/src/test/utils/factory.ts @@ -61,9 +61,13 @@ import { omit, pick } from 'lodash' */ // If there are no custom options export function createFixture(): { - (getValues: () => V): { + ( + getValues: () => V, + ): { // If some fields returned by getValues are overridden - >(overrides: O): V extends (infer I)[] + >( + overrides: O, + ): V extends (infer I)[] ? (Omit & O)[] // update type of each array element : Omit & O // update type of the object // If no fields are overridden @@ -73,11 +77,15 @@ export function createFixture(): { // If there are custom options with default values object export function createFixture( - defaultOptions: Required

// defaultOptions is an object with default options + defaultOptions: Required

, // defaultOptions is an object with default options ): { - (getValues: (options: P) => V): { + ( + getValues: (options: P) => V, + ): { // If some fields returned by getValues are overridden - >(overrides: O): V extends (infer I)[] + >( + overrides: O, + ): V extends (infer I)[] ? (Omit> & Omit)[] // update type of each array element : Omit> & Omit // update type of the object // If no fields are overridden @@ -87,11 +95,15 @@ export function createFixture( // If there are custom options with default values getter function export function createFixture( - getDefaultOptions: () => Required

// getDefaultOptions is a function that returns an object with default options + getDefaultOptions: () => Required

, // getDefaultOptions is a function that returns an object with default options ): { - (getValues: (options: P) => V): { + ( + getValues: (options: P) => V, + ): { // If some fields returned by getValues are overridden - >(overrides: O): V extends (infer I)[] + >( + overrides: O, + ): V extends (infer I)[] ? (Omit> & Omit)[] // update type of each array element : Omit> & Omit // update type of the object // If no fields are overridden @@ -100,21 +112,19 @@ export function createFixture( } export function createFixture( - defaultOptionsOrGetter?: Required

| (() => Required

) + defaultOptionsOrGetter?: Required

| (() => Required

), ) { return (getValues: (options?: P) => V) => { // eslint-disable-next-line @typescript-eslint/explicit-function-return-type return | Partial>(overrides?: O) => { // Get default options (if they exist) const defaultOptions = - typeof defaultOptionsOrGetter === 'function' - ? defaultOptionsOrGetter() - : defaultOptionsOrGetter + typeof defaultOptionsOrGetter === 'function' ? defaultOptionsOrGetter() : defaultOptionsOrGetter // Get overrides for options (filter out undefined values) const optionOverrides = Object.fromEntries( Object.entries(defaultOptions ? pick(overrides, Object.keys(defaultOptions)) : {}).filter( - ([, value]) => value !== undefined - ) + ([, value]) => value !== undefined, + ), ) // Get values with getValues function const mergedOptions = defaultOptions ? { ...defaultOptions, ...optionOverrides } : undefined diff --git a/packages/wallet/src/test/utils/random.ts b/packages/wallet/src/test/utils/random.ts index 9f9bdeea742..92f8908a940 100644 --- a/packages/wallet/src/test/utils/random.ts +++ b/packages/wallet/src/test/utils/random.ts @@ -19,9 +19,7 @@ * * @typeparam T Type of the enum object (will be automatically inferred from the provided argument). */ -export const randomEnumValue = >( - enumObj: T -): T[keyof T] => { +export const randomEnumValue = >(enumObj: T): T[keyof T] => { // If enum has different types for keys and values (keys are always strings, // values can be strings or numbers), we need to filter out the keys const keys = Object.keys(enumObj).filter((key) => isNaN(Number(key))) diff --git a/packages/wallet/src/test/utils/resolvers.ts b/packages/wallet/src/test/utils/resolvers.ts index 8cf62b3bfe6..a2e275b0847 100644 --- a/packages/wallet/src/test/utils/resolvers.ts +++ b/packages/wallet/src/test/utils/resolvers.ts @@ -14,38 +14,39 @@ type UndefinedToNull = T extends undefined ? null : T type ResolverReturnType = T extends (...args: any[]) => infer TResult ? TResult : T extends { resolve: (...args: any[]) => infer TResult } - ? TResult - : never - -type ResolverParameters> = T extends ResolverWithResolve< - infer TResult, // only result type is needed to filter selected fields - any, - any, - any -> - ? Parameters> - : T extends ResolverFn - ? Parameters> - : never + ? TResult + : never + +type ResolverParameters> = + T extends ResolverWithResolve< + infer TResult, // only result type is needed to filter selected fields + any, + any, + any + > + ? Parameters> + : T extends ResolverFn + ? Parameters> + : never type ResolverResponses = { [K in keyof T]: Promise> } function isResolverWithResolve>( - resolver: T + resolver: T, ): resolver is Extract> { return typeof resolver === 'object' && resolver !== null && 'resolve' in resolver } function isResolverFunction>( - resolver: T + resolver: T, ): resolver is Extract> { return typeof resolver === 'function' } export function queryResolvers( - resolvers: T + resolvers: T, ): { resolved: ResolverResponses resolvers: { Query: T } @@ -53,10 +54,7 @@ export function queryResolvers( // Create a response object with functions to create and resolve promises const promiseResolvers = {} as Record void> const resolved = Object.fromEntries( - Object.keys(resolvers).map((key) => [ - key, - new Promise((resolve) => (promiseResolvers[key as keyof T] = resolve)), - ]) + Object.keys(resolvers).map((key) => [key, new Promise((resolve) => (promiseResolvers[key as keyof T] = resolve))]), ) as ResolverResponses return { @@ -67,21 +65,16 @@ export function queryResolvers( const key = k as keyof T type R = typeof resolver - const resolve = async ( - ...params: ResolverParameters - ): Promise> => { + const resolve = async (...params: ResolverParameters): Promise> => { const [parent, args, context, info] = params const resolvedValue = isResolverWithResolve(resolver) ? resolver.resolve(parent, args, context, info) : isResolverFunction(resolver) - ? resolver(parent, args, context, info) - : null + ? resolver(parent, args, context, info) + : null - const updatedValue = await filterObjectFields( - info.fieldNodes[0]?.selectionSet, - resolvedValue - ) + const updatedValue = await filterObjectFields(info.fieldNodes[0]?.selectionSet, resolvedValue) // cloneDeepWith returns any type so we need to cast it manually const resultObj = cloneDeepWith(updatedValue, undefinedToNull) as ResolverReturnType @@ -94,7 +87,7 @@ export function queryResolvers( } return [key, resolve] - }) + }), ) as unknown as T, }, } @@ -108,7 +101,7 @@ function isObject(value: T): value is Exclude( selectionSet: SelectionSetNode | undefined, - sourceValue: T | Promise>> | null + sourceValue: T | Promise>> | null, ): Promise { // resolved source value can be a Promise or a plain value const source = await sourceValue diff --git a/packages/wallet/src/test/utils/wallet/balances.ts b/packages/wallet/src/test/utils/wallet/balances.ts index a37bd47b848..2cad1316590 100644 --- a/packages/wallet/src/test/utils/wallet/balances.ts +++ b/packages/wallet/src/test/utils/wallet/balances.ts @@ -1,13 +1,14 @@ import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { portfolioBalances } from 'wallet/src/test/fixtures' -export function portfolioBalancesById( - inputBalances?: PortfolioBalance[] -): Record { +export function portfolioBalancesById(inputBalances?: PortfolioBalance[]): Record { const balances = inputBalances ?? portfolioBalances() - return balances.reduce((acc, balance) => { - acc[balance.currencyInfo.currencyId] = balance - return acc - }, {} as Record) + return balances.reduce( + (acc, balance) => { + acc[balance.currencyInfo.currencyId] = balance + return acc + }, + {} as Record, + ) } diff --git a/packages/wallet/src/utils/animations.ts b/packages/wallet/src/utils/animations.ts index 93ace698daa..7797d287038 100644 --- a/packages/wallet/src/utils/animations.ts +++ b/packages/wallet/src/utils/animations.ts @@ -1,12 +1,7 @@ import { Easing, SharedValue, withRepeat, withTiming } from 'react-native-reanimated' export function errorShakeAnimation(input: SharedValue): number { - return withRepeat( - withTiming(5, { duration: 50, easing: Easing.inOut(Easing.ease) }), - 3, - true, - () => { - input.value = 0 - } - ) + return withRepeat(withTiming(5, { duration: 50, easing: Easing.inOut(Easing.ease) }), 3, true, () => { + input.value = 0 + }) } diff --git a/packages/wallet/src/utils/balance.ts b/packages/wallet/src/utils/balance.ts index 183ffc6cbd8..c40796a5ab1 100644 --- a/packages/wallet/src/utils/balance.ts +++ b/packages/wallet/src/utils/balance.ts @@ -8,17 +8,17 @@ const NATIVE_CURRENCY_DECIMALS = 18 // TODO(MOB-181): calculate this in a more scientific way export const MIN_ETH_FOR_GAS: JSBI = JSBI.multiply( JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(NATIVE_CURRENCY_DECIMALS - 3)), - JSBI.BigInt(15) + JSBI.BigInt(15), ) // .015 ETH export const MIN_POLYGON_FOR_GAS: JSBI = JSBI.multiply( JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(NATIVE_CURRENCY_DECIMALS - 2)), - JSBI.BigInt(6) + JSBI.BigInt(6), ) // .06 MATIC export const MIN_ARBITRUM_FOR_GAS: JSBI = JSBI.multiply( JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(NATIVE_CURRENCY_DECIMALS - 4)), - JSBI.BigInt(8) + JSBI.BigInt(8), ) // .0008 ETH export const MIN_OPTIMISM_FOR_GAS: JSBI = MIN_ARBITRUM_FOR_GAS @@ -42,9 +42,7 @@ export const MIN_ZKSYNC_FOR_GAS: JSBI = MIN_ARBITRUM_FOR_GAS * https://github.com/Uniswap/interface/blob/main/src/utils/maxAmountSpend.ts * @param currencyAmount to return max of */ -export function maxAmountSpend( - currencyAmount: Maybe> -): Maybe> { +export function maxAmountSpend(currencyAmount: Maybe>): Maybe> { if (!currencyAmount) { return undefined } diff --git a/packages/wallet/src/utils/clipboard.native.ts b/packages/wallet/src/utils/clipboard.native.ts index 3080cd395e4..0840dfdc306 100644 --- a/packages/wallet/src/utils/clipboard.native.ts +++ b/packages/wallet/src/utils/clipboard.native.ts @@ -31,8 +31,7 @@ const Clipboard: IClipboard = { const base64Encoding = await blobToBase64(blob) // extract base64 encoding from result string - const formattedEncoding = - typeof base64Encoding === 'string' ? base64Encoding.split(',')[1] : null + const formattedEncoding = typeof base64Encoding === 'string' ? base64Encoding.split(',')[1] : null // if valid result, copy to clipboard if (formattedEncoding) { diff --git a/packages/wallet/src/utils/colors.tsx b/packages/wallet/src/utils/colors.tsx index 7de80525536..e789d0f24bf 100644 --- a/packages/wallet/src/utils/colors.tsx +++ b/packages/wallet/src/utils/colors.tsx @@ -18,9 +18,7 @@ export function opacify(amount: number, hexColor: string): string { } if (hexColor.length !== 7) { - throw new Error( - `opacify: provided color ${hexColor} was not in hexadecimal format (e.g. #000000)` - ) + throw new Error(`opacify: provided color ${hexColor} was not in hexadecimal format (e.g. #000000)`) } if (amount < 0 || amount > 100) { @@ -60,9 +58,7 @@ export function useNetworkColors(chainId: WalletChainId): { * @param backgroundColor The hex value of the background color to check contrast against * @returns either 'sporeWhite' or 'sporeBlack' */ -export function getContrastPassingTextColor( - backgroundColor: string -): '$sporeWhite' | '$sporeBlack' { +export function getContrastPassingTextColor(backgroundColor: string): '$sporeWhite' | '$sporeBlack' { const lightText = colorsLight.sporeWhite if (hex(lightText, backgroundColor) >= MIN_COLOR_CONTRAST_THRESHOLD) { return '$sporeWhite' @@ -115,7 +111,7 @@ const ColorVariant = { */ export function adjustColorVariant( colorName: string | undefined, - adjustmentType: AdjustmentType + adjustmentType: AdjustmentType, ): keyof GlobalPalette | undefined { if (!colorName) { return undefined @@ -161,17 +157,14 @@ export function findNearestThemeColor(hexString: string): keyof GlobalPalette | } as { colorDiff: number | undefined colorName: keyof GlobalPalette | undefined - } + }, ).colorName } /** * Returns a number representing the difference between two colors. Lower means more similar. */ -export function getColorDiffScore( - colorA: string | null, - colorB: string | null -): number | undefined { +export function getColorDiffScore(colorA: string | null, colorB: string | null): number | undefined { if (!colorA || !colorB) { return undefined } diff --git a/packages/wallet/src/utils/currency.ts b/packages/wallet/src/utils/currency.ts index c89fcbcf779..847413255bd 100644 --- a/packages/wallet/src/utils/currency.ts +++ b/packages/wallet/src/utils/currency.ts @@ -1,7 +1,7 @@ import { Currency } from '@uniswap/sdk-core' +import { getValidAddress, shortenAddress } from 'uniswap/src/utils/addresses' import { getSymbolDisplayText } from 'uniswap/src/utils/currency' import { LocalizationContextState } from 'wallet/src/features/language/LocalizationContext' -import { getValidAddress, shortenAddress } from 'wallet/src/utils/addresses' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' export function getFormattedCurrencyAmount( @@ -9,7 +9,7 @@ export function getFormattedCurrencyAmount( currencyAmountRaw: string, formatter: LocalizationContextState, isApproximateAmount = false, - valueType = ValueType.Raw + valueType = ValueType.Raw, ): string { const currencyAmount = getCurrencyAmount({ value: currencyAmountRaw, @@ -27,7 +27,7 @@ export function getFormattedCurrencyAmount( export function getCurrencyDisplayText( currency: Maybe, - tokenAddressString: Address | undefined + tokenAddressString: Address | undefined, ): string | undefined { const symbolDisplayText = getSymbolDisplayText(currency?.symbol) diff --git a/packages/wallet/src/utils/currencyId.test.ts b/packages/wallet/src/utils/currencyId.test.ts index 1a04fe672d0..16710bd77b8 100644 --- a/packages/wallet/src/utils/currencyId.test.ts +++ b/packages/wallet/src/utils/currencyId.test.ts @@ -1,7 +1,5 @@ +import { getNativeAddress } from 'uniswap/src/constants/addresses' import { UniverseChainId } from 'uniswap/src/types/chains' -import { getNativeAddress } from 'wallet/src/constants/addresses' -import { DAI } from 'wallet/src/constants/tokens' -import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { NATIVE_ANALYTICS_ADDRESS_VALUE, areCurrencyIdsEqual, @@ -14,7 +12,9 @@ import { currencyIdToGraphQLAddress, getCurrencyAddressForAnalytics, isNativeCurrencyAddress, -} from 'wallet/src/utils/currencyId' +} from 'uniswap/src/utils/currencyId' +import { DAI } from 'wallet/src/constants/tokens' +import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' const ETH = NativeCurrency.onChain(UniverseChainId.Mainnet) const DAI_ADDRESS = '0x6B175474E89094C44Da98b954EedeAC495271d0F' diff --git a/packages/wallet/src/utils/currencyId.ts b/packages/wallet/src/utils/currencyId.ts index 2aad79f67d6..ac060f784e7 100644 --- a/packages/wallet/src/utils/currencyId.ts +++ b/packages/wallet/src/utils/currencyId.ts @@ -1,10 +1,10 @@ import { Currency } from '@uniswap/sdk-core' +import { getNativeAddress, getWrappedNativeAddress } from 'uniswap/src/constants/addresses' import { DEFAULT_NATIVE_ADDRESS } from 'uniswap/src/constants/chains' +import { toSupportedChainId } from 'uniswap/src/features/chains/utils' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' import { CurrencyId } from 'uniswap/src/types/currency' -import { getNativeAddress, getWrappedNativeAddress } from 'wallet/src/constants/addresses' -import { toSupportedChainId } from 'wallet/src/features/chains/utils' -import { areAddressesEqual } from 'wallet/src/utils/addresses' +import { areAddressesEqual } from 'uniswap/src/utils/addresses' export function currencyId(currency: Currency): CurrencyId { return buildCurrencyId(currency.chainId, currencyAddress(currency)) @@ -44,10 +44,7 @@ export function getCurrencyAddressForAnalytics(currency: Currency): string { return currency.address } -export const isNativeCurrencyAddress = ( - chainId: WalletChainId, - address: Maybe

-): boolean => { +export const isNativeCurrencyAddress = (chainId: WalletChainId, address: Maybe
): boolean => { if (!address) { return true } @@ -64,9 +61,7 @@ export function currencyIdToAddress(_currencyId: string): Address { return currencyIdParts[1] } -function isPolygonChain( - chainId: number -): chainId is UniverseChainId.Polygon | UniverseChainId.PolygonMumbai { +function isPolygonChain(chainId: number): chainId is UniverseChainId.Polygon | UniverseChainId.PolygonMumbai { return chainId === UniverseChainId.PolygonMumbai || chainId === UniverseChainId.Polygon } @@ -88,11 +83,7 @@ export function currencyIdToGraphQLAddress(_currencyId?: string): Address | null } // backend only expects `null` for the native asset, except Polygon & Celo - if ( - isNativeCurrencyAddress(chainId, address) && - !isPolygonChain(chainId) && - !isCeloChain(chainId) - ) { + if (isNativeCurrencyAddress(chainId, address) && !isPolygonChain(chainId) && !isCeloChain(chainId)) { return null } diff --git a/packages/wallet/src/utils/getCurrencyAmount.ts b/packages/wallet/src/utils/getCurrencyAmount.ts index 680d30e277d..3f952a8cff1 100644 --- a/packages/wallet/src/utils/getCurrencyAmount.ts +++ b/packages/wallet/src/utils/getCurrencyAmount.ts @@ -67,13 +67,7 @@ export function getCurrencyAmount({ } } -const sanitizeTokenAmount = ({ - value, - valueType, -}: { - value: string - valueType: ValueType -}): string => { +const sanitizeTokenAmount = ({ value, valueType }: { value: string; valueType: ValueType }): string => { let sanitizedValue = convertScientificNotationToNumber(value) if (sanitizedValue === '.') { diff --git a/packages/wallet/src/utils/linking.ts b/packages/wallet/src/utils/linking.ts index fccb1b5b87f..f4363360cc6 100644 --- a/packages/wallet/src/utils/linking.ts +++ b/packages/wallet/src/utils/linking.ts @@ -3,14 +3,11 @@ import { Linking } from 'react-native' import { colorsLight } from 'ui/src/theme' import { UNIVERSE_CHAIN_INFO } from 'uniswap/src/constants/chains' import { uniswapUrls } from 'uniswap/src/constants/urls' +import { toUniswapWebAppLink } from 'uniswap/src/features/chains/utils' import { UniverseChainId, WalletChainId } from 'uniswap/src/types/chains' +import { currencyIdToChain, currencyIdToGraphQLAddress } from 'uniswap/src/utils/currencyId' import { logger } from 'utilities/src/logger/logger' -import { toUniswapWebAppLink } from 'wallet/src/features/chains/utils' -import { - FiatPurchaseTransactionInfo, - ServiceProviderInfo, -} from 'wallet/src/features/transactions/types' -import { currencyIdToChain, currencyIdToGraphQLAddress } from 'wallet/src/utils/currencyId' +import { FiatPurchaseTransactionInfo, ServiceProviderInfo } from 'wallet/src/features/transactions/types' export const UNISWAP_APP_NATIVE_TOKEN = 'NATIVE' const ALLOWED_EXTERNAL_URI_SCHEMES = ['http://', 'https://'] @@ -29,7 +26,7 @@ export async function openUri( openExternalBrowser = false, isSafeUri = false, // NOTE: okay to use colors object directly as we want the same color for light/dark modes - controlsColor = colorsLight.accent1 + controlsColor = colorsLight.accent1, ): Promise { const trimmedURI = uri.trim() if (!isSafeUri && !ALLOWED_EXTERNAL_URI_SCHEMES.some((scheme) => trimmedURI.startsWith(scheme))) { @@ -73,10 +70,7 @@ export function dismissInAppBrowser(): void { WebBrowser.dismissBrowser() } -export async function openTransactionLink( - hash: string | undefined, - chainId: WalletChainId -): Promise { +export async function openTransactionLink(hash: string | undefined, chainId: WalletChainId): Promise { if (!hash) { return } @@ -101,11 +95,8 @@ const SERVICE_PROVIDER_SUPPORT_URLS: Record = { STRIPE: 'https://support.stripe.com/', } -export async function openLegacyFiatOnRampServiceProviderLink( - serviceProvider: string -): Promise { - const helpUrl = - SERVICE_PROVIDER_SUPPORT_URLS[serviceProvider] ?? 'https://www.moonpay.com/contact-us' +export async function openLegacyFiatOnRampServiceProviderLink(serviceProvider: string): Promise { + const helpUrl = SERVICE_PROVIDER_SUPPORT_URLS[serviceProvider] ?? 'https://www.moonpay.com/contact-us' return openUri(helpUrl) } @@ -130,11 +121,7 @@ export enum ExplorerDataType { * @param data the data to return a link for * @param type the type of the data */ -export function getExplorerLink( - chainId: WalletChainId, - data: string, - type: ExplorerDataType -): string { +export function getExplorerLink(chainId: WalletChainId, data: string, type: ExplorerDataType): string { const prefix = UNIVERSE_CHAIN_INFO[chainId].explorer.url switch (type) { @@ -180,10 +167,7 @@ export function getProfileUrl(walletAddress: string): string { const UTM_TAGS_MOBILE = 'utm_medium=mobile&utm_source=share-tdp' -export function getTokenUrl( - currencyId: string, - addMobileUTMTags: boolean = false -): string | undefined { +export function getTokenUrl(currencyId: string, addMobileUTMTags: boolean = false): string | undefined { const chainId = currencyIdToChain(currencyId) if (!chainId) { return diff --git a/packages/wallet/src/utils/mnemonics.ts b/packages/wallet/src/utils/mnemonics.ts index a380d3b8c0f..857daa56220 100644 --- a/packages/wallet/src/utils/mnemonics.ts +++ b/packages/wallet/src/utils/mnemonics.ts @@ -13,7 +13,7 @@ export enum MnemonicValidationError { export function translateMnemonicErrorMessage( error: MnemonicValidationError, invalidWord: string | undefined, - t: AppTFunction + t: AppTFunction, ): string { switch (error) { case MnemonicValidationError.InvalidPhrase: diff --git a/packages/wallet/src/utils/password.ts b/packages/wallet/src/utils/password.ts index 96e80cefb35..bc14e78231d 100644 --- a/packages/wallet/src/utils/password.ts +++ b/packages/wallet/src/utils/password.ts @@ -98,21 +98,18 @@ export function usePasswordForm(): { currentStrength: passwordStrength, minStrength: PasswordStrength.MEDIUM, }), - [passwordStrength, password] + [passwordStrength, password], ) const debouncedPassword = useDebounce(password, PASSWORD_VALIDATION_DEBOUNCE_MS) const debouncedConfirmPassword = useDebounce(confirmPassword, PASSWORD_VALIDATION_DEBOUNCE_MS) // Used to disable the continue button right away - const passwordsDiffer = useMemo( - () => doPasswordsDiffer(password, confirmPassword), - [password, confirmPassword] - ) + const passwordsDiffer = useMemo(() => doPasswordsDiffer(password, confirmPassword), [password, confirmPassword]) // Used to show the error message after debounce time const debouncedPasswordsDiffer = useMemo( () => doPasswordsDiffer(debouncedPassword, debouncedConfirmPassword), - [debouncedPassword, debouncedConfirmPassword] + [debouncedPassword, debouncedConfirmPassword], ) const enableNext = Boolean(password && confirmPassword) && !isWeakPassword && !passwordsDiffer diff --git a/packages/wallet/src/utils/persistedStorage.ts b/packages/wallet/src/utils/persistedStorage.ts index b57f786f0b7..5d64f09beff 100644 --- a/packages/wallet/src/utils/persistedStorage.ts +++ b/packages/wallet/src/utils/persistedStorage.ts @@ -27,7 +27,7 @@ export class PersistedStorage { return chrome.storage[this.area].set({ [key]: value }) } - removeItem(key: string): Promise { + removeItem(key: string | string[]): Promise { return chrome.storage[this.area].remove(key) } diff --git a/packages/wallet/src/utils/saga.ts b/packages/wallet/src/utils/saga.ts index b17e30656c0..22cab9883a9 100644 --- a/packages/wallet/src/utils/saga.ts +++ b/packages/wallet/src/utils/saga.ts @@ -12,7 +12,7 @@ import { AppNotificationType } from 'wallet/src/features/notifications/types' */ export function createSaga( saga: (params: SagaParams) => unknown, - name: string + name: string, ): { wrappedSaga: () => Generator actions: { @@ -24,9 +24,7 @@ export function createSaga( const wrappedSaga = function* () { while (true) { try { - const trigger = yield* take<{ type: typeof triggerAction.type; payload: SagaParams }>( - triggerAction.type - ) + const trigger = yield* take<{ type: typeof triggerAction.type; payload: SagaParams }>(triggerAction.type) logger.debug('saga', 'wrappedSaga', `${name} triggered`) yield* call(saga, trigger.payload) } catch (error) { @@ -75,7 +73,7 @@ interface MonitoredSagaOptions { export function createMonitoredSaga( saga: (params: SagaParams) => unknown, name: string, - options?: MonitoredSagaOptions + options?: MonitoredSagaOptions, ): { name: string // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -104,16 +102,14 @@ export function createMonitoredSaga( .addCase(resetAction, (state) => { state.status = null state.error = null - }) + }), ) // eslint-disable-next-line @typescript-eslint/no-explicit-any const wrappedSaga = function* (): any { while (true) { try { - const trigger = yield* take<{ type: typeof triggerAction.type; payload: SagaParams }>( - triggerAction.type - ) + const trigger = yield* take<{ type: typeof triggerAction.type; payload: SagaParams }>(triggerAction.type) logger.debug('saga', 'monitoredSaga', `${name} triggered`) yield* put(statusAction(SagaStatus.Started)) const { result, cancel, timeout } = yield* race({ @@ -154,7 +150,7 @@ export function createMonitoredSaga( pushNotification({ type: AppNotificationType.Error, errorMessage, - }) + }), ) } } diff --git a/packages/wallet/src/utils/transaction.ts b/packages/wallet/src/utils/transaction.ts index 6e44b2e4c4a..a59c3895070 100644 --- a/packages/wallet/src/utils/transaction.ts +++ b/packages/wallet/src/utils/transaction.ts @@ -4,9 +4,7 @@ const formatAsHexString = (input?: BigNumberish): string | undefined => input !== undefined ? BigNumber.from(input).toHexString() : input // hexlifyTransaction is idempotent so it's safe to call more than once on a singular transaction request -export function hexlifyTransaction( - transferTxRequest: providers.TransactionRequest -): providers.TransactionRequest { +export function hexlifyTransaction(transferTxRequest: providers.TransactionRequest): providers.TransactionRequest { const { value, nonce, gasLimit, gasPrice, maxPriorityFeePerGas, maxFeePerGas } = transferTxRequest return { ...transferTxRequest, diff --git a/packages/wallet/src/utils/useDynamicFontSizing.ts b/packages/wallet/src/utils/useDynamicFontSizing.ts index a0807981ac4..e770cc75fc8 100644 --- a/packages/wallet/src/utils/useDynamicFontSizing.ts +++ b/packages/wallet/src/utils/useDynamicFontSizing.ts @@ -4,7 +4,7 @@ import { LayoutChangeEvent } from 'react-native' export function useDynamicFontSizing( maxCharWidthAtMaxFontSize: number, maxFontSize: number, - minFontSize: number + minFontSize: number, ): { onLayout: (event: LayoutChangeEvent) => void fontSize: number @@ -31,7 +31,7 @@ export function useDynamicFontSizing( const newFontSize = Math.round(Math.min(maxFontSize, scaledSizeWithMin)) setFontSize(newFontSize) }, - [fontSize, maxFontSize, minFontSize, maxCharWidthAtMaxFontSize] + [fontSize, maxFontSize, minFontSize, maxCharWidthAtMaxFontSize], ) return { onLayout, fontSize, onSetFontSize } @@ -41,7 +41,7 @@ const getStringWidth = ( value: string, maxCharWidthAtMaxFontSize: number, currentFontSize: number, - maxFontSize: number + maxFontSize: number, ): number => { const widthAtMaxFontSize = value.length * maxCharWidthAtMaxFontSize return widthAtMaxFontSize * (currentFontSize / maxFontSize) diff --git a/scripts/prettier.sh b/scripts/prettier.sh new file mode 100755 index 00000000000..3876434726d --- /dev/null +++ b/scripts/prettier.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Default check +mode="-c" + +# Can change to write with "--write" +if [[ "$1" == "--write" ]]; then + mode="-w" +fi + +# Store the directory from which the script was called +CALLER_DIR="$(pwd)" + +# Get the directory of the script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Change to the script directory +cd "$SCRIPT_DIR/.." + +./node_modules/.bin/prettier $mode $CALLER_DIR --ignore-path .prettierignore diff --git a/scripts/turbo-changed.sh b/scripts/turbo-changed.sh index a8f5fe4c9ca..9e67c5ceea4 100755 --- a/scripts/turbo-changed.sh +++ b/scripts/turbo-changed.sh @@ -4,8 +4,8 @@ command=$1 # remove first argument so we pass along options to turbo shift # Validate command input -if [[ ! $command =~ ^(lint|test|typecheck)$ ]]; then - echo "Invalid command: $command. Must be one of: lint, test, typecheck." +if [[ ! $command =~ ^(lint|test|typecheck|format)$ ]]; then + echo "Invalid command: $command. Must be one of: format, lint, test, typecheck." exit 1 fi diff --git a/turbo.json b/turbo.json index 3f53ee70c7b..6f1bf6bc611 100644 --- a/turbo.json +++ b/turbo.json @@ -111,6 +111,13 @@ }, "check:circular": {}, "check:deps:usage": {}, + "format": { + "inputs": [ + "**/*.ts", + "**/*.tsx", + "**/*.js" + ] + }, "lint": { "dependsOn": [ "typecheck", diff --git a/yarn.lock b/yarn.lock index 80b9c498152..3852e7740b3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -549,7 +549,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.14.5, @babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.22.5, @babel/helper-module-imports@npm:^7.24.1": +"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.10.4, @babel/helper-module-imports@npm:^7.14.5, @babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.22.5, @babel/helper-module-imports@npm:^7.24.1": version: 7.24.3 resolution: "@babel/helper-module-imports@npm:7.24.3" dependencies: @@ -2097,12 +2097,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.7, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": - version: 7.24.4 - resolution: "@babel/runtime@npm:7.24.4" +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.3, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.19.4, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.7, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": + version: 7.24.7 + resolution: "@babel/runtime@npm:7.24.7" dependencies: regenerator-runtime: ^0.14.0 - checksum: 2f27d4c0ffac7ae7999ac0385e1106f2a06992a8bdcbf3da06adcac7413863cd08c198c2e4e970041bbea849e17f02e1df18875539b6afba76c781b6b59a07c3 + checksum: d17f29eed6f848ac15cdf4202a910b741facfb0419a9d79e5c7fa37df6362fc3227f1cc2e248cc6db5e53ddffb4caa6686c488e6e80ce3d29c36a4e74c8734ea languageName: node linkType: hard @@ -2744,6 +2744,27 @@ __metadata: languageName: node linkType: hard +"@datadog/browser-core@npm:5.20.0": + version: 5.20.0 + resolution: "@datadog/browser-core@npm:5.20.0" + checksum: 82fb37ee0d02bda6f958549fd7b0c7f075613172bcb27133d2fa2fb91b22f6b4b1002042c0e96960993ebf0cde1d14aa55cdfaa2bc99bc358a793e491563d5c2 + languageName: node + linkType: hard + +"@datadog/browser-logs@npm:^5.20.0": + version: 5.20.0 + resolution: "@datadog/browser-logs@npm:5.20.0" + dependencies: + "@datadog/browser-core": 5.20.0 + peerDependencies: + "@datadog/browser-rum": 5.20.0 + peerDependenciesMeta: + "@datadog/browser-rum": + optional: true + checksum: d3ead69bb892e7d4015c58bdf180a318e0e2efcb572e503334852a458c31014b26b9948b1aec759b6b5a2fc743045b071be6a58685b191249b3bc4235b407fca + languageName: node + linkType: hard + "@dependents/detective-less@npm:^3.0.1": version: 3.0.2 resolution: "@dependents/detective-less@npm:3.0.2" @@ -2789,25 +2810,6 @@ __metadata: languageName: node linkType: hard -"@emotion/babel-plugin@npm:^11.11.0": - version: 11.11.0 - resolution: "@emotion/babel-plugin@npm:11.11.0" - dependencies: - "@babel/helper-module-imports": ^7.16.7 - "@babel/runtime": ^7.18.3 - "@emotion/hash": ^0.9.1 - "@emotion/memoize": ^0.8.1 - "@emotion/serialize": ^1.1.2 - babel-plugin-macros: ^3.1.0 - convert-source-map: ^1.5.0 - escape-string-regexp: ^4.0.0 - find-root: ^1.1.0 - source-map: ^0.5.7 - stylis: 4.2.0 - checksum: 6b363edccc10290f7a23242c06f88e451b5feb2ab94152b18bb8883033db5934fb0e421e2d67d09907c13837c21218a3ac28c51707778a54d6cd3706c0c2f3f9 - languageName: node - linkType: hard - "@emotion/cache@npm:^10.0.27": version: 10.0.29 resolution: "@emotion/cache@npm:10.0.29" @@ -2820,19 +2822,6 @@ __metadata: languageName: node linkType: hard -"@emotion/cache@npm:^11.11.0": - version: 11.11.0 - resolution: "@emotion/cache@npm:11.11.0" - dependencies: - "@emotion/memoize": ^0.8.1 - "@emotion/sheet": ^1.2.2 - "@emotion/utils": ^1.2.1 - "@emotion/weak-memoize": ^0.3.1 - stylis: 4.2.0 - checksum: 8eb1dc22beaa20c21a2e04c284d5a2630a018a9d51fb190e52de348c8d27f4e8ca4bbab003d68b4f6cd9cc1c569ca747a997797e0f76d6c734a660dc29decf08 - languageName: node - linkType: hard - "@emotion/core@npm:^10.0.0": version: 10.3.1 resolution: "@emotion/core@npm:10.3.1" @@ -2867,7 +2856,7 @@ __metadata: languageName: node linkType: hard -"@emotion/hash@npm:^0.9.0, @emotion/hash@npm:^0.9.1": +"@emotion/hash@npm:^0.9.0": version: 0.9.1 resolution: "@emotion/hash@npm:0.9.1" checksum: 716e17e48bf9047bf9383982c071de49f2615310fb4e986738931776f5a823bc1f29c84501abe0d3df91a3803c80122d24e28b57351bca9e01356ebb33d89876 @@ -2883,7 +2872,7 @@ __metadata: languageName: node linkType: hard -"@emotion/is-prop-valid@npm:^1.1.0, @emotion/is-prop-valid@npm:^1.2.2": +"@emotion/is-prop-valid@npm:^1.1.0": version: 1.2.2 resolution: "@emotion/is-prop-valid@npm:1.2.2" dependencies: @@ -2913,27 +2902,6 @@ __metadata: languageName: node linkType: hard -"@emotion/react@npm:^11.10.6": - version: 11.11.4 - resolution: "@emotion/react@npm:11.11.4" - dependencies: - "@babel/runtime": ^7.18.3 - "@emotion/babel-plugin": ^11.11.0 - "@emotion/cache": ^11.11.0 - "@emotion/serialize": ^1.1.3 - "@emotion/use-insertion-effect-with-fallbacks": ^1.0.1 - "@emotion/utils": ^1.2.1 - "@emotion/weak-memoize": ^0.3.1 - hoist-non-react-statics: ^3.3.1 - peerDependencies: - react: ">=16.8.0" - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 6abaa7a05c5e1db31bffca7ac79169f5456990022cbb3794e6903221536609a60420f2b4888dd3f84e9634a304e394130cb88dc32c243a1dedc263e50da329f8 - languageName: node - linkType: hard - "@emotion/serialize@npm:^0.11.15, @emotion/serialize@npm:^0.11.16": version: 0.11.16 resolution: "@emotion/serialize@npm:0.11.16" @@ -2947,19 +2915,6 @@ __metadata: languageName: node linkType: hard -"@emotion/serialize@npm:^1.1.2, @emotion/serialize@npm:^1.1.3, @emotion/serialize@npm:^1.1.4": - version: 1.1.4 - resolution: "@emotion/serialize@npm:1.1.4" - dependencies: - "@emotion/hash": ^0.9.1 - "@emotion/memoize": ^0.8.1 - "@emotion/unitless": ^0.8.1 - "@emotion/utils": ^1.2.1 - csstype: ^3.0.2 - checksum: 71b99f816a9c1d61a87c62cf4928da3894bb62213f3aff38b1ea9790b3368f084af98a3e5453b5055c2f36a7d70318d2fa9955b7b5676c2065b868062375df39 - languageName: node - linkType: hard - "@emotion/sheet@npm:0.9.4": version: 0.9.4 resolution: "@emotion/sheet@npm:0.9.4" @@ -2967,13 +2922,6 @@ __metadata: languageName: node linkType: hard -"@emotion/sheet@npm:^1.2.2": - version: 1.2.2 - resolution: "@emotion/sheet@npm:1.2.2" - checksum: d973273c9c15f1c291ca2269728bf044bd3e92a67bca87943fa9ec6c3cd2b034f9a6bfe95ef1b5d983351d128c75b547b43ff196a00a3875f7e1d269793cecfe - languageName: node - linkType: hard - "@emotion/styled-base@npm:^10.3.0": version: 10.3.0 resolution: "@emotion/styled-base@npm:10.3.0" @@ -3002,26 +2950,6 @@ __metadata: languageName: node linkType: hard -"@emotion/styled@npm:^11.10.6": - version: 11.11.5 - resolution: "@emotion/styled@npm:11.11.5" - dependencies: - "@babel/runtime": ^7.18.3 - "@emotion/babel-plugin": ^11.11.0 - "@emotion/is-prop-valid": ^1.2.2 - "@emotion/serialize": ^1.1.4 - "@emotion/use-insertion-effect-with-fallbacks": ^1.0.1 - "@emotion/utils": ^1.2.1 - peerDependencies: - "@emotion/react": ^11.0.0-rc.0 - react: ">=16.8.0" - peerDependenciesMeta: - "@types/react": - optional: true - checksum: ad5fc42d00e8aa9597f6d9665986036d5ebe0e8f8155af6d95831c5e8fb2319fb837724e6c5cd59e5346f14c3263711b7ce7271d34688e974d1f32ffeecb37ba - languageName: node - linkType: hard - "@emotion/stylis@npm:0.8.5, @emotion/stylis@npm:^0.8.4": version: 0.8.5 resolution: "@emotion/stylis@npm:0.8.5" @@ -3036,22 +2964,6 @@ __metadata: languageName: node linkType: hard -"@emotion/unitless@npm:^0.8.1": - version: 0.8.1 - resolution: "@emotion/unitless@npm:0.8.1" - checksum: 385e21d184d27853bb350999471f00e1429fa4e83182f46cd2c164985999d9b46d558dc8b9cc89975cb337831ce50c31ac2f33b15502e85c299892e67e7b4a88 - languageName: node - linkType: hard - -"@emotion/use-insertion-effect-with-fallbacks@npm:^1.0.1": - version: 1.0.1 - resolution: "@emotion/use-insertion-effect-with-fallbacks@npm:1.0.1" - peerDependencies: - react: ">=16.8.0" - checksum: 700b6e5bbb37a9231f203bb3af11295eed01d73b2293abece0bc2a2237015e944d7b5114d4887ad9a79776504aa51ed2a8b0ddbc117c54495dd01a6b22f93786 - languageName: node - linkType: hard - "@emotion/utils@npm:0.11.3": version: 0.11.3 resolution: "@emotion/utils@npm:0.11.3" @@ -3059,13 +2971,6 @@ __metadata: languageName: node linkType: hard -"@emotion/utils@npm:^1.2.1": - version: 1.2.1 - resolution: "@emotion/utils@npm:1.2.1" - checksum: e0b44be0705b56b079c55faff93952150be69e79b660ae70ddd5b6e09fc40eb1319654315a9f34bb479d7f4ec94be6068c061abbb9e18b9778ae180ad5d97c73 - languageName: node - linkType: hard - "@emotion/weak-memoize@npm:0.2.5": version: 0.2.5 resolution: "@emotion/weak-memoize@npm:0.2.5" @@ -3073,13 +2978,6 @@ __metadata: languageName: node linkType: hard -"@emotion/weak-memoize@npm:^0.3.1": - version: 0.3.1 - resolution: "@emotion/weak-memoize@npm:0.3.1" - checksum: b2be47caa24a8122622ea18cd2d650dbb4f8ad37b636dc41ed420c2e082f7f1e564ecdea68122b546df7f305b159bf5ab9ffee872abd0f052e687428459af594 - languageName: node - linkType: hard - "@esbuild-plugins/node-globals-polyfill@npm:^0.2.3": version: 0.2.3 resolution: "@esbuild-plugins/node-globals-polyfill@npm:0.2.3" @@ -6125,7 +6023,7 @@ __metadata: languageName: node linkType: hard -"@metamask/json-rpc-engine@npm:^7.0.0": +"@metamask/json-rpc-engine@npm:^7.0.0, @metamask/json-rpc-engine@npm:^7.3.2": version: 7.3.3 resolution: "@metamask/json-rpc-engine@npm:7.3.3" dependencies: @@ -6136,14 +6034,25 @@ __metadata: languageName: node linkType: hard -"@metamask/object-multiplex@npm:^1.1.0": - version: 1.3.0 - resolution: "@metamask/object-multiplex@npm:1.3.0" +"@metamask/json-rpc-middleware-stream@npm:^6.0.2": + version: 6.0.2 + resolution: "@metamask/json-rpc-middleware-stream@npm:6.0.2" + dependencies: + "@metamask/json-rpc-engine": ^7.3.2 + "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.3.0 + readable-stream: ^3.6.2 + checksum: e831041b03e9f48f584f4425188f72b58974f95b60429c9fe8b5561da69c6bbfad2f2b2199acdff06ee718967214b65c05604d4f85f3287186619683487f1060 + languageName: node + linkType: hard + +"@metamask/object-multiplex@npm:^2.0.0": + version: 2.0.0 + resolution: "@metamask/object-multiplex@npm:2.0.0" dependencies: - end-of-stream: ^1.4.4 once: ^1.4.0 - readable-stream: ^2.3.3 - checksum: 4a2b48fc0e1a8f536edbab9f37b637cd91102538ad76ce07bdfad99b90d98b34585a0e5afa62ca9c1d550a0016347568ff0d635e5bf8cfa266d049e1c0ebedc8 + readable-stream: ^3.6.2 + checksum: 54baea752a3ac7c2742c376512e00d4902d383e9da8787574d3b21eb0081523309e24e3915a98f3ae0341d65712b6832d2eb7eeb862f4ef0da1ead52dcde5387 languageName: node linkType: hard @@ -6156,33 +6065,23 @@ __metadata: languageName: node linkType: hard -"@metamask/post-message-stream@npm:^6.1.0": - version: 6.2.0 - resolution: "@metamask/post-message-stream@npm:6.2.0" - dependencies: - "@metamask/utils": ^5.0.0 - readable-stream: 2.3.3 - checksum: 657cdb2dd61a46a4da7f036a97ef0aa9ad8e918d8f8c0fd620eaede4a32c2ff909738a7dfb2b1e6099e7771fd03c3466b60fedab56e39a5cc5507927758e3cb7 - languageName: node - linkType: hard - -"@metamask/providers@npm:^10.2.1": - version: 10.2.1 - resolution: "@metamask/providers@npm:10.2.1" +"@metamask/providers@npm:^15.0.0": + version: 15.0.0 + resolution: "@metamask/providers@npm:15.0.0" dependencies: - "@metamask/object-multiplex": ^1.1.0 - "@metamask/safe-event-emitter": ^2.0.0 - "@types/chrome": ^0.0.136 + "@metamask/json-rpc-engine": ^7.3.2 + "@metamask/json-rpc-middleware-stream": ^6.0.2 + "@metamask/object-multiplex": ^2.0.0 + "@metamask/rpc-errors": ^6.2.1 + "@metamask/safe-event-emitter": ^3.0.0 + "@metamask/utils": ^8.3.0 detect-browser: ^5.2.0 - eth-rpc-errors: ^4.0.2 - extension-port-stream: ^2.0.1 - fast-deep-equal: ^2.0.1 + extension-port-stream: ^3.0.0 + fast-deep-equal: ^3.1.3 is-stream: ^2.0.0 - json-rpc-engine: ^6.1.0 - json-rpc-middleware-stream: ^4.2.1 - pump: ^3.0.0 - webextension-polyfill-ts: ^0.25.0 - checksum: e88b2db8c4673cc6a7e47d9f0531df3fac73f05f8e9ff6d02c3420dfb3c7a82335d9c44876f2d472c44eac36d66491d2022be4f39600bee561d5de8ad59c5b07 + readable-stream: ^3.6.2 + webextension-polyfill: ^0.10.0 + checksum: 42571450e79d69d943384f557f6a61e0f73101d49804fb6e8075d791959f76c42b8ff626f711d434674792d77aead6cb8a32b04a3dcd53598c8aff24cbb1ad25 languageName: node linkType: hard @@ -6210,79 +6109,92 @@ __metadata: languageName: node linkType: hard -"@metamask/sdk-communication-layer@npm:0.14.3": - version: 0.14.3 - resolution: "@metamask/sdk-communication-layer@npm:0.14.3" +"@metamask/sdk-communication-layer@npm:0.18.5": + version: 0.18.5 + resolution: "@metamask/sdk-communication-layer@npm:0.18.5" dependencies: bufferutil: ^4.0.8 - cross-fetch: ^3.1.5 date-fns: ^2.29.3 - eciesjs: ^0.3.16 - eventemitter2: ^6.4.5 - socket.io-client: ^4.5.1 + debug: ^4.3.4 utf-8-validate: ^6.0.3 uuid: ^8.3.2 - checksum: 1a4d89a8bef3c4a08df151a1f95d0eca65f18715a1de3e66ae3b7dd1f7cb58957edb1cba7f1af13ee037b50866d25a0402917c640e380dbbe32534cfa0764398 + peerDependencies: + cross-fetch: ^3.1.5 + eciesjs: ^0.3.16 + eventemitter2: ^6.4.7 + readable-stream: ^3.6.2 + socket.io-client: ^4.5.1 + checksum: 7d8e632e0d8b95a093272a941adcf8b40d257a3b285dac0821c60a8ff36d1915a4998f0950bc3a7bd89dc9f15b0c00034e7d5a71a590218d1e5bdccc9224a2b5 languageName: node linkType: hard -"@metamask/sdk-install-modal-web@npm:0.14.1": - version: 0.14.1 - resolution: "@metamask/sdk-install-modal-web@npm:0.14.1" +"@metamask/sdk-install-modal-web@npm:0.18.5": + version: 0.18.5 + resolution: "@metamask/sdk-install-modal-web@npm:0.18.5" dependencies: - "@emotion/react": ^11.10.6 - "@emotion/styled": ^11.10.6 - i18next: 22.5.1 qr-code-styling: ^1.6.0-rc.1 + peerDependencies: + i18next: 22.5.1 react: ^18.2.0 react-dom: ^18.2.0 react-i18next: ^13.2.2 - checksum: 9122f3d0395514a4a8c2a4da5d805587b4af5d2112c333ea2dd08fa9c2046aea2f0f91bddade05364653b06899b64a849a902d645307e393c557fd878cffd50b + react-native: "*" + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + react-native: + optional: true + checksum: 57fd8dc802ef0d2949c34df6387abf3633bfb0da03cbce570735060991337835a8846694216bfe14d2764fa21cf0d70fc14cecb442b02f8e468f3560acc7b8e7 languageName: node linkType: hard -"@metamask/sdk@npm:0.14.3": - version: 0.14.3 - resolution: "@metamask/sdk@npm:0.14.3" +"@metamask/sdk@npm:0.18.6": + version: 0.18.6 + resolution: "@metamask/sdk@npm:0.18.6" dependencies: "@metamask/onboarding": ^1.0.1 - "@metamask/post-message-stream": ^6.1.0 - "@metamask/providers": ^10.2.1 - "@metamask/sdk-communication-layer": 0.14.3 - "@metamask/sdk-install-modal-web": 0.14.1 - "@react-native-async-storage/async-storage": ^1.17.11 + "@metamask/providers": ^15.0.0 + "@metamask/sdk-communication-layer": 0.18.5 + "@metamask/sdk-install-modal-web": 0.18.5 "@types/dom-screen-wake-lock": ^1.0.0 bowser: ^2.9.0 cross-fetch: ^4.0.0 + debug: ^4.3.4 eciesjs: ^0.3.15 eth-rpc-errors: ^4.0.3 eventemitter2: ^6.4.7 - extension-port-stream: ^2.0.1 i18next: 22.5.1 - i18next-browser-languagedetector: ^7.1.0 + i18next-browser-languagedetector: 7.1.0 obj-multiplex: ^1.0.0 pump: ^3.0.0 qrcode-terminal-nooctal: ^0.12.1 - react-i18next: ^13.2.2 react-native-webview: ^11.26.0 - readable-stream: ^2.3.7 + readable-stream: ^3.6.2 rollup-plugin-visualizer: ^5.9.2 socket.io-client: ^4.5.1 util: ^0.12.4 uuid: ^8.3.2 peerDependencies: + "@react-native-async-storage/async-storage": ^1.19.6 react: ^18.2.0 + react-dom: ^18.2.0 react-native: "*" peerDependenciesMeta: + "@react-native-async-storage/async-storage": + optional: true react: optional: true + react-dom: + optional: true react-native: optional: true - checksum: da43da4b39c558ec2d6a472634e0b4b9fa3f3e46b4397368438c3a86764fac7e3dde386a0b50d9ce5ad43431879ce80178c01cef507eac28e67980006c82eeb6 + checksum: 936ce46f32cdba4dbc3dc892a245e85fbd7ff73c75b5625f2faa30b771b6d0ff2223c5021629361d0e2c8c45f22227f322444df9f6eaf4a961c61b9f06333200 languageName: node linkType: hard -"@metamask/utils@npm:^5.0.0, @metamask/utils@npm:^5.0.1": +"@metamask/utils@npm:^5.0.1": version: 5.0.2 resolution: "@metamask/utils@npm:5.0.2" dependencies: @@ -11760,16 +11672,6 @@ __metadata: languageName: node linkType: hard -"@types/chrome@npm:^0.0.136": - version: 0.0.136 - resolution: "@types/chrome@npm:0.0.136" - dependencies: - "@types/filesystem": "*" - "@types/har-format": "*" - checksum: af96fdc79fb019d827fdb6269f831921f8f36215ee05a2624436dd2ad4d84d7be12333cc6f54912fb8bae0ca49cbfde5a78de94723bfbd20d309d2e71e274a1b - languageName: node - linkType: hard - "@types/connect-history-api-fallback@npm:^1.3.5": version: 1.3.5 resolution: "@types/connect-history-api-fallback@npm:1.3.5" @@ -13468,8 +13370,6 @@ __metadata: eslint-plugin-storybook: ^0.6.10 eslint-plugin-unused-imports: ^2.0.0 jest: 29.7.0 - prettier: ^2.8.0 - prettier-plugin-organize-imports: 3.2.4 typescript: 5.3.3 peerDependencies: eslint: ^8.0.0 @@ -13514,6 +13414,7 @@ __metadata: "@reach/dialog": 0.10.5 "@reach/portal": 0.10.5 "@reduxjs/toolkit": 1.9.3 + "@rive-app/canvas": 2.8.3 "@rive-app/react-canvas": 4.6.2 "@sentry/browser": 7.80.0 "@sentry/core": 7.80.0 @@ -13525,6 +13426,7 @@ __metadata: "@tamagui/core": 1.95.1 "@tamagui/portal": 1.95.1 "@tamagui/react-native-svg": 1.95.1 + "@tamagui/remove-scroll": 1.95.1 "@tanstack/react-query": 5.28.14 "@tanstack/react-table": 8.10.7 "@testing-library/jest-dom": 5.17.0 @@ -13646,7 +13548,6 @@ __metadata: polished: 3.3.2 polyfill-object.fromentries: 1.0.1 postinstall-postinstall: 2.1.0 - prettier: latest process: 0.11.10 qrcode.react: 3.1.0 qs: 6.9.4 @@ -13695,7 +13596,7 @@ __metadata: uuid: 9.0.0 video-extensions: 1.2.0 viem: 2.x - wagmi: 2.5.19 + wagmi: 2.8.4 wcag-contrast: 3.0.0 web-vitals: 2.1.4 webpack: 5.90.0 @@ -13996,6 +13897,7 @@ __metadata: "@types/redux-persist-webextension-storage": 1.0.3 "@types/ua-parser-js": 0.7.31 "@types/uuid": 9.0.1 + "@uniswap/analytics-events": 2.32.0 "@uniswap/eslint-config": "workspace:^" "@uniswap/universal-router-sdk": 2.2.0 "@uniswap/v3-sdk": 3.13.0 @@ -14507,30 +14409,30 @@ __metadata: languageName: node linkType: hard -"@wagmi/connectors@npm:4.1.25": - version: 4.1.25 - resolution: "@wagmi/connectors@npm:4.1.25" +"@wagmi/connectors@npm:4.3.6": + version: 4.3.6 + resolution: "@wagmi/connectors@npm:4.3.6" dependencies: "@coinbase/wallet-sdk": 3.9.1 - "@metamask/sdk": 0.14.3 + "@metamask/sdk": 0.18.6 "@safe-global/safe-apps-provider": 0.18.1 "@safe-global/safe-apps-sdk": 8.1.0 - "@walletconnect/ethereum-provider": 2.11.2 + "@walletconnect/ethereum-provider": 2.13.0 "@walletconnect/modal": 2.6.2 peerDependencies: - "@wagmi/core": 2.6.16 + "@wagmi/core": 2.9.4 typescript: ">=5.0.4" viem: 2.x peerDependenciesMeta: typescript: optional: true - checksum: 7b402604ca05bbe04d905f8c9c9c9441c37b0baae5d1610b999d0e86a0c0c573b937c509d901bead3b3fdbd4caaf4a47dfc0ad9115e71ebf91ce6c2d900297c9 + checksum: aaa26ae9b2ac4531baa787dc05cbefcf4f9b038d0989d165e8281fa69d717122f3371bfac32ab243edc46be3b3d92d3512005eb32ef2afaa9676f48ba5bf785e languageName: node linkType: hard -"@wagmi/core@npm:2.6.16": - version: 2.6.16 - resolution: "@wagmi/core@npm:2.6.16" +"@wagmi/core@npm:2.9.4": + version: 2.9.4 + resolution: "@wagmi/core@npm:2.9.4" dependencies: eventemitter3: 5.0.1 mipd: 0.0.5 @@ -14544,27 +14446,7 @@ __metadata: optional: true typescript: optional: true - checksum: 6ae6d2f98bb87e2ea039b1e403c07804021fea936882c4a03b3dd67edc8b9ecd3f5e968a321f105c3e2be7186d9980c2e6fa5ac3f4248de06e0677e7ab601fee - languageName: node - linkType: hard - -"@wagmi/core@patch:@wagmi/core@npm%3A2.6.16#./.yarn/patches/@wagmi-core-npm-2.6.16-1baef7c190.patch::locator=universe%40workspace%3A.": - version: 2.6.16 - resolution: "@wagmi/core@patch:@wagmi/core@npm%3A2.6.16#./.yarn/patches/@wagmi-core-npm-2.6.16-1baef7c190.patch::version=2.6.16&hash=a4dce9&locator=universe%40workspace%3A." - dependencies: - eventemitter3: 5.0.1 - mipd: 0.0.5 - zustand: 4.4.1 - peerDependencies: - "@tanstack/query-core": ">=5.0.0" - typescript: ">=5.0.4" - viem: 2.x - peerDependenciesMeta: - "@tanstack/query-core": - optional: true - typescript: - optional: true - checksum: a3252d86a02c9c971ad9c8ba351ef01b14f960d76f73a45d2d7954d40bcdc9c38dcb6bbf6cb330f84c643a6e171c1dfbdfa58d077438fcadb9be7289c5386110 + checksum: ea6dc673a01aca1723abd14b05eb0691855e9f206f54499cec562dec614667f4ee4a535fec890fff1d9fde820d89a9642a24c89029ca6c6b001c88e5fee8b812 languageName: node linkType: hard @@ -21999,7 +21881,7 @@ __metadata: languageName: node linkType: hard -"eciesjs@npm:^0.3.15, eciesjs@npm:^0.3.16": +"eciesjs@npm:^0.3.15": version: 0.3.18 resolution: "eciesjs@npm:0.3.18" dependencies: @@ -22163,7 +22045,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.0, end-of-stream@npm:^1.4.1, end-of-stream@npm:^1.4.4": +"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.0, end-of-stream@npm:^1.4.1": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4" dependencies: @@ -23578,7 +23460,7 @@ __metadata: languageName: node linkType: hard -"eventemitter2@npm:^6.4.5, eventemitter2@npm:^6.4.7": +"eventemitter2@npm:^6.4.7": version: 6.4.9 resolution: "eventemitter2@npm:6.4.9" checksum: be59577c1e1c35509c7ba0e2624335c35bbcfd9485b8a977384c6cc6759341ea1a98d3cb9dbaa5cea4fff9b687e504504e3f9c2cc1674cf3bd8a43a7c74ea3eb @@ -24124,12 +24006,13 @@ __metadata: languageName: node linkType: hard -"extension-port-stream@npm:^2.0.1": - version: 2.1.1 - resolution: "extension-port-stream@npm:2.1.1" +"extension-port-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "extension-port-stream@npm:3.0.0" dependencies: + readable-stream: ^3.6.2 || ^4.4.2 webextension-polyfill: ">=0.10.0 <1.0" - checksum: aee8bbeb2ed6f69a62f58a89580e0e9002dadb11062edbaedb7bb04cfc5a5e0b0d3980bfeaa1c3ee7e08dec7e5fac26e25497fc2f82000db7653442bd5eca157 + checksum: 4f51d2258a96154c2d916a8a5425636a2b0817763e9277f7dc378d08b6f050c90d185dbde4313d27cf66ad99d4b3116479f9f699c40358c64cccfa524d2b55bf languageName: node linkType: hard @@ -24210,13 +24093,6 @@ __metadata: languageName: node linkType: hard -"fast-deep-equal@npm:^2.0.1": - version: 2.0.1 - resolution: "fast-deep-equal@npm:2.0.1" - checksum: b701835a87985e0ec4925bdf1f0c1e7eb56309b5d12d534d5b4b69d95a54d65bb16861c081781ead55f73f12d6c60ba668713391ee7fbf6b0567026f579b7b0b - languageName: node - linkType: hard - "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -26376,7 +26252,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": +"hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -26778,12 +26654,12 @@ __metadata: languageName: node linkType: hard -"i18next-browser-languagedetector@npm:^7.1.0": - version: 7.2.1 - resolution: "i18next-browser-languagedetector@npm:7.2.1" +"i18next-browser-languagedetector@npm:7.1.0": + version: 7.1.0 + resolution: "i18next-browser-languagedetector@npm:7.1.0" dependencies: - "@babel/runtime": ^7.23.2 - checksum: 159958be2d8f19444e9378512c36c2bf13a8ab85eddac2fc0000198a03dbc28c73a6f44594ab040b242bdc82dfeabb7c1ab805884b5438ee0a48a8e2b52ca062 + "@babel/runtime": ^7.19.4 + checksum: 36981b9a9995ed66387f3735cceffe107ed3cdb6ca278d45fa243fabc65669c0eca095ed4a55a93dac046ca1eb23fd986ec0079723be7ebb8505e6ba25f379bb languageName: node linkType: hard @@ -29877,17 +29753,6 @@ __metadata: languageName: node linkType: hard -"json-rpc-middleware-stream@npm:^4.2.1": - version: 4.2.3 - resolution: "json-rpc-middleware-stream@npm:4.2.3" - dependencies: - "@metamask/safe-event-emitter": ^3.0.0 - json-rpc-engine: ^6.1.0 - readable-stream: ^2.3.3 - checksum: 0907d34935a8b58c3c67626e344272758f684c13175b2e7de2bac37309c3211fca7a129bce042d50faed605615f51fbba01e173bdc2ae6c14d95aefb9bfb4e09 - languageName: node - linkType: hard - "json-rpc-random-id@npm:^1.0.0, json-rpc-random-id@npm:^1.0.1": version: 1.0.1 resolution: "json-rpc-random-id@npm:1.0.1" @@ -35398,21 +35263,21 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.1.2, prettier@npm:^2.6.2, prettier@npm:^2.8.0": - version: 2.8.8 - resolution: "prettier@npm:2.8.8" +"prettier@npm:3.3.2": + version: 3.3.2 + resolution: "prettier@npm:3.3.2" bin: - prettier: bin-prettier.js - checksum: b49e409431bf129dd89238d64299ba80717b57ff5a6d1c1a8b1a28b590d998a34e083fa13573bc732bb8d2305becb4c9a4407f8486c81fa7d55100eb08263cf8 + prettier: bin/prettier.cjs + checksum: 5557d8caed0b182f68123c2e1e370ef105251d1dd75800fadaece3d061daf96b1389141634febf776050f9d732c7ae8fd444ff0b4a61b20535e7610552f32c69 languageName: node linkType: hard -"prettier@npm:latest": - version: 2.8.7 - resolution: "prettier@npm:2.8.7" +"prettier@npm:^2.1.2, prettier@npm:^2.6.2": + version: 2.8.8 + resolution: "prettier@npm:2.8.8" bin: prettier: bin-prettier.js - checksum: fdc8f2616f099f5f0d685907f4449a70595a0fc1d081a88919604375989e0d5e9168d6121d8cc6861f21990b31665828e00472544d785d5940ea08a17660c3a6 + checksum: b49e409431bf129dd89238d64299ba80717b57ff5a6d1c1a8b1a28b590d998a34e083fa13573bc732bb8d2305becb4c9a4407f8486c81fa7d55100eb08263cf8 languageName: node linkType: hard @@ -35530,13 +35395,6 @@ __metadata: languageName: node linkType: hard -"process-nextick-args@npm:~1.0.6": - version: 1.0.7 - resolution: "process-nextick-args@npm:1.0.7" - checksum: 41224fbc803ac6c96907461d4dfc20942efa3ca75f2d521bcf7cf0e89f8dec127fb3fb5d76746b8fb468a232ea02d84824fae08e027aec185fd29049c66d49f8 - languageName: node - linkType: hard - "process-warning@npm:^1.0.0": version: 1.0.0 resolution: "process-warning@npm:1.0.0" @@ -36344,24 +36202,6 @@ __metadata: languageName: node linkType: hard -"react-i18next@npm:^13.2.2": - version: 13.5.0 - resolution: "react-i18next@npm:13.5.0" - dependencies: - "@babel/runtime": ^7.22.5 - html-parse-stringify: ^3.0.1 - peerDependencies: - i18next: ">= 23.2.3" - react: ">= 16.8.0" - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - checksum: 2f68ccd24daf72ddd2d11a526fb3c2b66c11ea4fcd2e24ac7aed42bf57ec7bffa7455ad1dc93679968ff629e9b1896465cdf6d1a61c29b92138ef88098e8dcba - languageName: node - linkType: hard - "react-infinite-scroll-component@npm:6.1.0": version: 6.1.0 resolution: "react-infinite-scroll-component@npm:6.1.0" @@ -36486,11 +36326,11 @@ __metadata: "react-native-context-menu-view@patch:react-native-context-menu-view@npm%3A1.15.0#./.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch::locator=universe%40workspace%3A.": version: 1.15.0 - resolution: "react-native-context-menu-view@patch:react-native-context-menu-view@npm%3A1.15.0#./.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch::version=1.15.0&hash=4a3bf4&locator=universe%40workspace%3A." + resolution: "react-native-context-menu-view@patch:react-native-context-menu-view@npm%3A1.15.0#./.yarn/patches/react-native-context-menu-view-npm-1.15.0-c8a9d10d8c.patch::version=1.15.0&hash=86e788&locator=universe%40workspace%3A." peerDependencies: react: ^16.8.1 || ^17.0.0 || ^18.0.0 react-native: ">=0.60.0-rc.0 <1.0.x" - checksum: ad09d011a06e243d2501ec4bc0f3b6a054f539360f5e80f38fd599fe03736197def2f46fcc9c3d75578cd63536594888dcc08544437fbcc693fb6cbacc31050c + checksum: f41bd5ea81fe5b15f7f30f0975da7643dc795c5b9ecd5a7b6f8ca5b3ffcda5c3d112f146ada98a1cf7e475318b7bfc616a7389380e7feb6f51fbd8189b6ec9ce languageName: node linkType: hard @@ -37403,22 +37243,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:2.3.3": - version: 2.3.3 - resolution: "readable-stream@npm:2.3.3" - dependencies: - core-util-is: ~1.0.0 - inherits: ~2.0.3 - isarray: ~1.0.0 - process-nextick-args: ~1.0.6 - safe-buffer: ~5.1.1 - string_decoder: ~1.0.3 - util-deprecate: ~1.0.1 - checksum: 76f9863065d7edc14abd78e68784048487e83a4b6908336ba3eacb5e9544d642ad60836f91fab16e1dc6ad9e493dfe6c2e5b65f370ec65454d415efa50361a76 - languageName: node - linkType: hard - -"readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:3, readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.5.0, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.2": version: 3.6.2 resolution: "readable-stream@npm:3.6.2" dependencies: @@ -37429,7 +37254,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.5, readable-stream@npm:^2.0.6, readable-stream@npm:^2.1.5, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.5, readable-stream@npm:^2.3.6, readable-stream@npm:^2.3.7, readable-stream@npm:~2.3.6": +"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.1, readable-stream@npm:^2.0.2, readable-stream@npm:^2.0.5, readable-stream@npm:^2.0.6, readable-stream@npm:^2.1.5, readable-stream@npm:^2.3.3, readable-stream@npm:^2.3.5, readable-stream@npm:^2.3.6, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -37444,15 +37269,16 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^4.0.0": - version: 4.4.0 - resolution: "readable-stream@npm:4.4.0" +"readable-stream@npm:^3.6.2 || ^4.4.2, readable-stream@npm:^4.0.0": + version: 4.5.2 + resolution: "readable-stream@npm:4.5.2" dependencies: abort-controller: ^3.0.0 buffer: ^6.0.3 events: ^3.3.0 process: ^0.11.10 - checksum: cc1630c2de134aee92646e77b1770019633000c408fd48609babf2caa53f00ca794928023aa9ad3d435a1044cec87d2ce7e2b7389dd1caf948b65c175edb7f52 + string_decoder: ^1.3.0 + checksum: c4030ccff010b83e4f33289c535f7830190773e274b3fcb6e2541475070bdfd69c98001c3b0cb78763fc00c8b62f514d96c2b10a8bd35d5ce45203a25fa1d33a languageName: node linkType: hard @@ -40247,15 +40073,6 @@ __metadata: languageName: node linkType: hard -"string_decoder@npm:~1.0.3": - version: 1.0.3 - resolution: "string_decoder@npm:1.0.3" - dependencies: - safe-buffer: ~5.1.0 - checksum: 57ef02a148fd1ff2f20fe1accd944505ed3703e78bb28d302d940b2ad3dfb469508f79dcd0275ba1960d9675aa206452f76b2416059a6d0b0200bd7e9f552cdb - languageName: node - linkType: hard - "string_decoder@npm:~1.1.1": version: 1.1.1 resolution: "string_decoder@npm:1.1.1" @@ -40490,13 +40307,6 @@ __metadata: languageName: node linkType: hard -"stylis@npm:4.2.0": - version: 4.2.0 - resolution: "stylis@npm:4.2.0" - checksum: 0eb6cc1b866dc17a6037d0a82ac7fa877eba6a757443e79e7c4f35bacedbf6421fadcab4363b39667b43355cbaaa570a3cde850f776498e5450f32ed2f9b7584 - languageName: node - linkType: hard - "stylus-lookup@npm:^3.0.1": version: 3.0.2 resolution: "stylus-lookup@npm:3.0.2" @@ -42596,6 +42406,8 @@ __metadata: resolution: "uniswap@workspace:packages/uniswap" dependencies: "@apollo/client": 3.10.4 + "@ethersproject/address": 5.7.0 + "@ethersproject/bignumber": 5.7.0 "@ethersproject/providers": 5.7.2 "@gorhom/bottom-sheet": 4.5.1 "@graphql-codegen/cli": ^3.3.1 @@ -42611,6 +42423,7 @@ __metadata: "@testing-library/react-native": 11.5.0 "@typechain/ethers-v5": 7.2.0 "@types/chrome": 0.0.254 + "@types/react-window": 1.8.2 "@uniswap/analytics-events": 2.32.0 "@uniswap/eslint-config": "workspace:^" "@uniswap/router-sdk": 1.9.2 @@ -42620,6 +42433,7 @@ __metadata: eslint: 8.44.0 ethers: 5.7.2 expo-blur: 12.9.2 + fuse.js: 6.5.3 get-graphql-schema: ^2.1.2 i18next: 23.10.0 i18next-resources-for-ts: 1.5.0 @@ -42634,13 +42448,15 @@ __metadata: react-native-dotenv: 3.2.0 react-native-reanimated: 3.8.1 react-test-renderer: 18.2.0 + react-virtualized-auto-sizer: 1.0.20 + react-window: 1.8.9 statsig-react: 1.32.0 statsig-react-native: 4.11.0 typechain: 5.2.0 typescript: 5.3.3 ui: "workspace:^" utilities: "workspace:^" - wagmi: 2.5.19 + wagmi: 2.8.4 languageName: unknown linkType: soft @@ -42694,6 +42510,8 @@ __metadata: husky: ^8.0.3 i18next: 23.10.0 i18next-parser: 8.6.0 + prettier: 3.3.2 + prettier-plugin-organize-imports: 3.2.4 syncpack: ^8.5.14 turbo: 1.10.16 turbo-ignore: ^1.11.3 @@ -43103,6 +42921,7 @@ __metadata: "@amplitude/analytics-react-native": 1.4.0 "@amplitude/analytics-types": 0.13.0 "@apollo/client": 3.10.4 + "@datadog/browser-logs": ^5.20.0 "@ethersproject/abstract-signer": 5.7.0 "@ethersproject/address": 5.7.0 "@ethersproject/constants": 5.7.0 @@ -43115,6 +42934,7 @@ __metadata: "@testing-library/react-hooks": 7.0.2 "@types/chrome": 0.0.254 "@types/react": ^18.0.15 + "@types/uuid": 9.0.1 "@uniswap/analytics": 1.7.0 "@uniswap/analytics-events": 2.32.0 "@uniswap/eslint-config": "workspace:^" @@ -43129,9 +42949,11 @@ __metadata: jsbi: 3.2.5 react: 18.2.0 react-native: 0.73.6 + react-native-device-info: 10.0.2 react-test-renderer: 18.2.0 subscriptions-transport-ws: 0.11.0 typescript: 5.3.3 + uuid: 9.0.0 zen-observable-ts: 1.2.5 languageName: unknown linkType: soft @@ -43567,12 +43389,12 @@ __metadata: languageName: node linkType: hard -"wagmi@npm:2.5.19": - version: 2.5.19 - resolution: "wagmi@npm:2.5.19" +"wagmi@npm:2.8.4": + version: 2.8.4 + resolution: "wagmi@npm:2.8.4" dependencies: - "@wagmi/connectors": 4.1.25 - "@wagmi/core": 2.6.16 + "@wagmi/connectors": 4.3.6 + "@wagmi/core": 2.9.4 use-sync-external-store: 1.2.0 peerDependencies: "@tanstack/react-query": ">=5.0.0" @@ -43582,7 +43404,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: bdf767c0de083a485d9e4e13dc98e68968aac576eb7be9c1a59e3f9ea9eb6316edfb47b19238eb68c9f151ca5df95bf859c17c2003211f4df841b0ae72c46419 + checksum: 9e470a45da6612ce40721db2c405d9a5ebbccb7a1212c16f829ca45bc8f8d23b0f5419bfb00e6ddc18346146916f4773947baf79bcef836557d61e5b9ce66359 languageName: node linkType: hard @@ -43654,7 +43476,6 @@ __metadata: "@testing-library/react-hooks": 7.0.2 "@testing-library/react-native": 11.5.0 "@types/react": ^18.0.15 - "@types/react-window": 1.8.2 "@types/zxcvbn": 4.4.2 "@uniswap/analytics-events": 2.32.0 "@uniswap/eslint-config": "workspace:^" @@ -43703,8 +43524,6 @@ __metadata: react-native-webview: 11.23.1 react-redux: 8.0.5 react-test-renderer: 18.2.0 - react-virtualized-auto-sizer: 1.0.20 - react-window: 1.8.9 redux: 4.2.1 redux-persist: 6.0.0 redux-saga: 1.2.2 @@ -43824,29 +43643,13 @@ __metadata: languageName: node linkType: hard -"webextension-polyfill-ts@npm:^0.25.0": - version: 0.25.0 - resolution: "webextension-polyfill-ts@npm:0.25.0" - dependencies: - webextension-polyfill: ^0.7.0 - checksum: c4dc82c86e34cea717a26af549f2822d63e92af52632f5e909ea13b5e7bd9d6110781f10313e36ada2b54c770ebca018bc3784756d12ba3b0b623d285f1a14a7 - languageName: node - linkType: hard - -"webextension-polyfill@npm:>=0.10.0 <1.0": +"webextension-polyfill@npm:>=0.10.0 <1.0, webextension-polyfill@npm:^0.10.0": version: 0.10.0 resolution: "webextension-polyfill@npm:0.10.0" checksum: 4a59036bda571360c2c0b2fb03fe1dc244f233946bcf9a6766f677956c40fd14d270aaa69cdba95e4ac521014afbe4008bfa5959d0ac39f91c990eb206587f91 languageName: node linkType: hard -"webextension-polyfill@npm:^0.7.0": - version: 0.7.0 - resolution: "webextension-polyfill@npm:0.7.0" - checksum: fb738a5de07feb593875e02f25c3ab4276c8736118929556c8d4bdf965bb0f11c96ea263cd397b9b21259e8faf2dce2eaaa42ce08c922d96de7adb5896ec7d10 - languageName: node - linkType: hard - "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1"