From 7058b40c17d3831c9fbdbfda0e301e7c0243931d Mon Sep 17 00:00:00 2001 From: Uniswap Labs Service Account Date: Tue, 2 Apr 2024 14:32:58 +0000 Subject: [PATCH] ci(release): publish latest release --- RELEASE | 46 +- VERSION | 2 +- apps/mobile/README.md | 2 + apps/mobile/android/app/build.gradle | 12 +- .../ios/Uniswap.xcodeproj/project.pbxproj | 32 +- apps/mobile/package.json | 4 +- apps/mobile/src/app/App.tsx | 12 +- .../app/MobileWalletNavigationProvider.tsx | 4 +- apps/mobile/src/app/migrations.test.ts | 13 +- apps/mobile/src/app/migrations.ts | 12 + apps/mobile/src/app/modals/AppModals.tsx | 5 + .../src/app/modals/ExtensionPromoModal.tsx | 58 +++ .../AccountSwitcherModal.test.tsx.snap | 8 +- apps/mobile/src/app/schema.ts | 12 +- apps/mobile/src/app/store.ts | 2 +- apps/mobile/src/components/AnimatedNumber.tsx | 31 +- .../PriceExplorer/PriceExplorer.tsx | 2 +- .../PriceExplorerAnimatedNumber.tsx | 45 +- .../PriceExplorer/usePriceHistory.test.ts | 2 +- .../PriceExplorer/usePriceHistory.ts | 3 +- .../src/components/QRCodeScanner/QRCode.tsx | 3 +- .../TokenBalanceItemContextMenu.tsx | 2 +- .../TokenBalanceList/TokenBalanceList.tsx | 2 +- .../components/TokenDetails/TokenBalances.tsx | 4 +- .../src/components/TokenDetails/hooks.ts | 4 +- .../TokenSelector/TokenFiatOnRampList.tsx | 2 +- .../WalletConnect/DappHeaderIcon.tsx | 2 +- .../ModalWithOverlay/ModalWithOverlay.tsx | 201 ++++++++ .../ModalWithOverlay/ScrollDownOverlay.tsx | 61 +++ .../components/WalletConnect/NetworkLogos.tsx | 3 +- .../WalletConnect/RequestModal/HeaderText.tsx | 3 +- .../WalletConnectRequestModal.tsx | 249 ++------- .../WalletConnectRequestModalContent.tsx | 213 ++++++++ .../ScanSheet/PendingConnectionModal.tsx | 257 ++++++---- .../ScanSheet/WalletConnectModal.tsx | 34 +- .../WalletConnect/ScanSheet/util.ts | 26 +- .../src/components/accounts/AccountHeader.tsx | 8 +- .../__snapshots__/AccountHeader.test.tsx.snap | 4 +- .../banners/ExtensionPromoBanner.tsx | 126 +++++ .../src/components/buttons/LinkButton.tsx | 18 +- .../__snapshots__/LinkButton.test.tsx.snap | 1 + .../explore/FavoriteHeaderRow.test.tsx | 80 +++ .../components/explore/FavoriteHeaderRow.tsx | 9 +- .../explore/FavoriteTokenCard.test.tsx | 153 ++++++ .../components/explore/FavoriteTokenCard.tsx | 2 +- .../explore/FavoriteWalletCard.test.tsx | 153 ++++++ .../components/explore/FavoriteWalletCard.tsx | 3 +- .../components/explore/RemoveButton.test.tsx | 39 ++ .../src/components/explore/RemoveButton.tsx | 1 + .../components/explore/SortButton.test.tsx | 83 +++ .../src/components/explore/TokenItem.test.tsx | 123 +++++ .../src/components/explore/TokenItem.tsx | 8 +- .../FavoriteHeaderRow.test.tsx.snap | 268 ++++++++++ .../FavoriteTokenCard.test.tsx.snap | 39 ++ .../FavoriteWalletCard.test.tsx.snap | 402 +++++++++++++++ .../__snapshots__/RemoveButton.test.tsx.snap | 63 +++ .../__snapshots__/SortButton.test.tsx.snap | 198 +++++++ .../__snapshots__/TokenItem.test.tsx.snap | 250 +++++++++ .../src/components/explore/hooks.test.ts | 42 +- apps/mobile/src/components/explore/hooks.ts | 3 +- .../explore/search/SearchEmptySection.tsx | 18 +- .../explore/search/SearchResultsLoader.tsx | 17 +- .../explore/search/SearchResultsSection.tsx | 45 +- .../explore/search/SearchSectionHeader.tsx | 4 +- .../explore/search/items/SearchUnitagItem.tsx | 14 +- .../src/components/explore/search/types.tsx | 2 +- apps/mobile/src/components/home/TokensTab.tsx | 2 +- apps/mobile/src/components/loading/index.tsx | 7 +- apps/mobile/src/features/balances/hooks.ts | 22 +- apps/mobile/src/features/dataApi/balances.ts | 4 +- .../src/features/deepLinking/constants.ts | 6 + .../deepLinking/handleDeepLinkSaga.ts | 101 +++- .../externalProfile/ProfileHeader.tsx | 2 +- apps/mobile/src/features/favorites/hooks.ts | 3 +- .../fiatOnRamp/FiatOnRampAmountSection.tsx | 2 +- .../features/fiatOnRamp/FiatOnRampContext.tsx | 7 + apps/mobile/src/features/fiatOnRamp/hooks.ts | 64 ++- apps/mobile/src/features/fiatOnRamp/types.ts | 2 +- .../mobile/src/features/modals/ModalsState.ts | 2 + apps/mobile/src/features/modals/modalSlice.ts | 11 + .../nfts/collection/NFTCollectionHeader.tsx | 11 +- .../scantastic/ExtensionWaitlistModal.tsx | 80 +++ .../scantastic/ExtensionWaitlistModalState.ts | 3 + .../features/scantastic/ScantasticModal.tsx | 18 +- apps/mobile/src/features/swap/hooks.ts | 48 -- .../unitags/EditUnitagProfileScreen.tsx | 2 +- apps/mobile/src/features/widgets/widgets.ts | 2 +- apps/mobile/src/index.ts | 17 + .../src/screens/FiatOnRampConnecting.tsx | 7 +- apps/mobile/src/screens/FiatOnRampScreen.tsx | 3 +- apps/mobile/src/screens/HomeScreen.tsx | 55 +- apps/mobile/src/screens/NFTItemScreen.tsx | 2 +- .../mobile/src/screens/TokenDetailsScreen.tsx | 16 +- apps/mobile/src/test/fixtures/explore.ts | 27 + apps/mobile/src/test/fixtures/index.ts | 1 + apps/mobile/src/utils/reanimated.test.ts | 6 +- apps/mobile/src/utils/reanimated.ts | 4 +- apps/mobile/src/utils/version.ts | 7 + apps/web/.depcheckrc | 1 + apps/web/cypress/e2e/service-worker.test.ts | 2 +- .../api/image/nfts/collection/[index].tsx | 6 +- .../functions/api/image/tokens/[[index]].tsx | 6 +- apps/web/functions/utils/getRGBColor.ts | 14 + apps/web/package.json | 18 +- .../Activity/OffchainActivityModal.tsx | 11 +- .../__snapshots__/parseRemote.test.tsx.snap | 4 +- .../MiniPortfolio/Activity/parseLocal.ts | 4 +- .../Activity/parseRemote.test.tsx | 1 - .../MiniPortfolio/Activity/parseRemote.tsx | 9 +- .../MiniPortfolio/Activity/utils.ts | 37 +- .../MiniPortfolio/Limits/LimitsMenu.tsx | 6 +- .../hooks/useCancelLimitsGasEstimate.ts | 9 +- .../MiniPortfolio/Pools/cache.ts | 2 +- .../MiniPortfolio/Pools/hooks.ts | 7 +- .../Pools/useMultiChainPositions.tsx | 1 + .../MiniPortfolio/PortfolioLogo.tsx | 14 +- .../AccountDrawer/UniwalletModal.tsx | 4 +- .../src/components/AnimatedDropdown/index.tsx | 16 +- apps/web/src/components/Button/index.tsx | 1 + .../LimitPriceInputPanel/LimitPriceButton.tsx | 4 +- .../LimitPriceInputPanel.test.tsx | 5 +- .../LimitPriceInputPanel.tsx | 5 +- .../SwapCurrencyInputPanel.tsx | 8 +- .../web/src/components/Dialog/Dialog.test.tsx | 2 + apps/web/src/components/Dialog/Dialog.tsx | 9 +- .../FeatureFlagModal/FeatureFlagModal.tsx | 2 +- .../web/src/components/FeeSelector/shared.tsx | 1 + apps/web/src/components/Logo/AssetLogo.tsx | 14 +- apps/web/src/components/Logo/ChainLogo.tsx | 14 + .../components/Logo/ChainSymbols/blast.svg | 4 + .../Logo/ChainSymbols/blast_light.svg | 4 + .../src/components/NavBar/SearchBar.css.ts | 77 +-- .../components/NavBar/SearchBarDropdown.tsx | 27 +- .../src/components/NavBar/SuggestionRow.tsx | 303 +++++------ .../ChainSelectorRow.test.tsx.snap | 322 ++++++++++++ .../__snapshots__/SearchBar.test.tsx.snap | 12 +- .../SearchBarDropdown.test.tsx.snap | 482 +++++++++++++++--- .../PoolDetailsStatsButtons.test.tsx.snap | 6 +- .../src/components/PositionPreview/index.tsx | 6 +- .../SearchModal/CurrencySearchModal.tsx | 2 +- .../SearchModal/useCurrencySearchResults.ts | 40 +- .../TokenSafety/TokenSafetyMessage.tsx | 3 +- .../components/Tokens/TokenDetails/index.tsx | 2 +- apps/web/src/components/WalletModal/index.tsx | 13 +- .../web/src/components/Web3Provider/index.tsx | 6 +- .../addLiquidity/OutOfSyncWarning.tsx | 49 ++ .../src/components/swap/SwapHeader.test.tsx | 2 +- apps/web/src/components/swap/SwapHeader.tsx | 7 +- apps/web/src/components/swap/SwapLineItem.tsx | 2 + .../__snapshots__/SwapDetails.test.tsx.snap | 16 + .../SwapDetailsDropdown.test.tsx.snap | 8 + .../__snapshots__/SwapLineItem.test.tsx.snap | 56 ++ apps/web/src/connection/WalletConnectV2.ts | 4 +- apps/web/src/connection/eagerlyConnect.ts | 4 +- apps/web/src/connection/index.ts | 21 +- apps/web/src/connection/types.ts | 1 - apps/web/src/constants/chainInfo.ts | 11 + apps/web/src/constants/chains.test.ts | 1 + apps/web/src/constants/chains.ts | 17 +- apps/web/src/constants/networks.ts | 9 + apps/web/src/constants/providers.ts | 21 +- apps/web/src/constants/routing.ts | 1 + apps/web/src/constants/tokenColors.ts | 73 --- apps/web/src/constants/tokenSafetyLookup.ts | 2 +- apps/web/src/constants/tokens.ts | 7 + .../src/featureFlags/flags/outageBanner.ts | 4 + apps/web/src/graphql/data/types.ts | 14 +- apps/web/src/graphql/data/util.tsx | 26 +- apps/web/src/graphql/thegraph/apollo.ts | 2 + apps/web/src/hooks/Tokens.ts | 25 +- apps/web/src/hooks/useColor.ts | 99 +--- apps/web/src/hooks/useContract.ts | 7 +- apps/web/src/hooks/useFetchListCallback.ts | 7 +- apps/web/src/hooks/useIsPoolOutOfSync.ts | 77 +++ apps/web/src/hooks/useNetworkProviders.ts | 7 - apps/web/src/hooks/useStablecoinPrice.ts | 2 + apps/web/src/hooks/useSwapCallback.tsx | 4 +- apps/web/src/hooks/useSwitchChain.ts | 8 +- apps/web/src/hooks/useUniswapXSwapCallback.ts | 3 +- apps/web/src/hooks/useUniversalRouter.ts | 40 +- apps/web/src/index.tsx | 2 +- apps/web/src/lib/hooks/orders/updater.tsx | 2 +- .../routing/clientSideSmartOrderRouter.ts | 22 +- .../src/lib/hooks/transactions/updater.tsx | 1 + apps/web/src/lib/hooks/useBlockNumber.tsx | 13 +- apps/web/src/lib/hooks/useCurrencyLogoURIs.ts | 13 +- .../src/pages/AddLiquidity/blastAlerts.tsx | 151 ++++++ apps/web/src/pages/AddLiquidity/index.tsx | 36 +- apps/web/src/pages/App.tsx | 16 +- apps/web/src/pages/Landing/sections/Hero.tsx | 3 - .../Swap/Limit/LimitExpirySection.test.tsx | 3 +- .../pages/Swap/Limit/LimitExpirySection.tsx | 3 +- apps/web/src/pages/Swap/Limit/LimitForm.tsx | 7 +- .../Swap/Send/SendCurrencyInputForm.test.tsx | 2 +- .../pages/Swap/Send/SendCurrencyInputForm.tsx | 3 +- apps/web/src/pages/Swap/Send/SendForm.tsx | 3 +- .../Swap/Send/SendRecipientForm.test.tsx | 2 +- .../pages/Swap/Send/SendReviewModal.test.tsx | 2 +- apps/web/src/pages/Swap/SwapForm.tsx | 9 +- apps/web/src/pages/Swap/index.tsx | 17 +- .../web/src/pages/TokenDetails/TDPContext.tsx | 2 +- apps/web/src/pages/TokenDetails/index.tsx | 9 +- apps/web/src/setupTests.ts | 1 + apps/web/src/state/limit/LimitContext.tsx | 24 +- apps/web/src/state/limit/hooks.ts | 4 +- apps/web/src/state/limit/types.ts | 22 + apps/web/src/state/lists/reducer.test.ts | 3 +- apps/web/src/state/lists/reducer.ts | 16 +- apps/web/src/state/lists/types.ts | 14 + apps/web/src/state/migrations.test.ts | 2 +- apps/web/src/state/migrations.ts | 20 +- apps/web/src/state/migrations/3.ts | 2 +- apps/web/src/state/reducer.ts | 2 +- apps/web/src/state/reducerTypeTest.ts | 2 +- apps/web/src/state/routing/gas.ts | 6 +- apps/web/src/state/routing/quickRouteSlice.ts | 1 - apps/web/src/state/routing/slice.ts | 8 +- apps/web/src/state/routing/types.ts | 7 + apps/web/src/state/send/SendContext.tsx | 2 +- apps/web/src/state/signatures/hooks.ts | 22 +- apps/web/src/state/signatures/types.ts | 11 +- apps/web/src/state/signatures/updater.tsx | 5 +- apps/web/src/state/swap/SwapContext.test.tsx | 11 +- apps/web/src/state/swap/SwapContext.tsx | 99 +--- apps/web/src/state/swap/hooks.test.ts | 3 +- apps/web/src/state/swap/hooks.tsx | 58 +-- apps/web/src/state/swap/types.ts | 114 +++++ apps/web/src/state/user/hooks.test.tsx | 3 +- apps/web/src/state/user/hooks.tsx | 42 +- apps/web/src/state/user/userAddedTokens.ts | 22 + apps/web/src/state/user/utils.ts | 22 + apps/web/src/test-utils/constants.ts | 4 +- apps/web/src/test-utils/images.ts | 89 ---- apps/web/src/test-utils/render.tsx | 13 +- apps/web/src/theme/colors.ts | 2 + apps/web/src/theme/utils.ts | 16 +- .../tracing/SwapEventTimestampTracker.test.ts | 33 +- apps/web/src/tracing/request.test.ts | 8 +- apps/web/src/tracing/request.ts | 11 +- apps/web/src/tracing/trace.test.ts | 24 +- apps/web/src/tracing/trace.ts | 50 +- apps/web/src/tracing/types.ts | 1 - apps/web/src/utils/getColor.test.ts | 80 --- apps/web/src/utils/getColor.ts | 75 --- apps/web/src/utils/getExplorerLink.test.ts | 3 + apps/web/src/utils/getExplorerLink.ts | 1 + apps/web/src/utils/isEmpty.ts | 5 +- apps/web/src/utils/openDownloadApp.ts | 6 +- apps/web/src/utils/platform.ts | 7 - package.json | 6 +- packages/ui/package.json | 4 +- .../graphics/extension-promo-banner-dark.png | Bin 0 -> 52594 bytes .../graphics/extension-promo-banner-light.png | Bin 0 -> 46581 bytes .../graphics/extension-promo-modal-dark.png | Bin 0 -> 342538 bytes .../graphics/extension-promo-modal-light.png | Bin 0 -> 319237 bytes packages/ui/src/assets/icons/code.svg | 5 + .../ui/src/assets/icons/document-list.svg | 3 + packages/ui/src/assets/icons/gallery.svg | 3 + packages/ui/src/assets/icons/globe.svg | 6 +- packages/ui/src/assets/icons/mobile.svg | 3 + packages/ui/src/assets/icons/person.svg | 3 + packages/ui/src/assets/icons/settings.svg | 2 +- packages/ui/src/assets/icons/time-past.svg | 3 + packages/ui/src/assets/index.ts | 6 +- .../ui/src/assets/logos/png/blast-logo.png | Bin 4823 -> 0 bytes packages/ui/src/components/button/Button.tsx | 2 +- .../src/components/icons/ArrowDownCircle.tsx | 6 +- packages/ui/src/components/icons/Code.tsx | 17 + .../ui/src/components/icons/DocumentList.tsx | 17 + .../ui/src/components/icons/FileListLock.tsx | 4 +- packages/ui/src/components/icons/Gallery.tsx | 17 + packages/ui/src/components/icons/Globe.tsx | 25 +- packages/ui/src/components/icons/Mobile.tsx | 6 +- packages/ui/src/components/icons/Person.tsx | 17 + packages/ui/src/components/icons/Settings.tsx | 2 +- packages/ui/src/components/icons/TimePast.tsx | 4 +- packages/ui/src/components/icons/index.ts | 4 + packages/ui/src/env.d.ts | 2 + packages/ui/src/index.ts | 1 + packages/ui/src/theme/color/colors.ts | 3 - packages/ui/src/theme/fonts.ts | 33 +- packages/ui/src/utils/colors.ts | 356 +++++++++++++ packages/uniswap/package.json | 3 +- packages/uniswap/src/constants/urls.ts | 7 +- packages/uniswap/src/data/cache.ts | 25 +- .../graphql/uniswap-data-api/queries.graphql | 16 +- .../graphql/uniswap-data-api/schema.graphql | 43 +- .../uniswap-data-api/web/search.graphql | 4 +- .../uniswap/src/features/chains/utils.test.ts | 13 + packages/uniswap/src/features/chains/utils.ts | 36 ++ .../src/features/dataApi/types.ts | 2 +- .../uniswap/src/features/experiments/flags.ts | 8 +- packages/uniswap/src/features/unitags/api.ts | 26 +- .../uniswap/src/features/unitags/types.ts | 19 + .../src/i18n/locales/source/en-US.json | 89 +++- .../src/i18n/locales/translations/es-ES.json | 180 ++++++- .../src/i18n/locales/translations/fr-FR.json | 172 ++++++- .../src/i18n/locales/translations/hi-IN.json | 174 ++++++- .../src/i18n/locales/translations/id-ID.json | 172 ++++++- .../src/i18n/locales/translations/ja-JP.json | 180 ++++++- .../src/i18n/locales/translations/ms-MY.json | 178 ++++++- .../src/i18n/locales/translations/nl-NL.json | 172 ++++++- .../src/i18n/locales/translations/pt-PT.json | 172 ++++++- .../src/i18n/locales/translations/ru-RU.json | 172 ++++++- .../src/i18n/locales/translations/th-TH.json | 174 ++++++- .../src/i18n/locales/translations/tr-TR.json | 174 ++++++- .../src/i18n/locales/translations/uk-UA.json | 180 ++++++- .../src/i18n/locales/translations/ur-PK.json | 178 ++++++- .../src/i18n/locales/translations/vi-VN.json | 172 ++++++- .../src/i18n/locales/translations/zh-CN.json | 182 ++++++- .../src/i18n/locales/translations/zh-TW.json | 178 ++++++- packages/uniswap/src/types/currency.ts | 1 + packages/utilities/package.json | 2 +- packages/utilities/src/format/urls.test.ts | 101 +++- packages/utilities/src/format/urls.ts | 46 +- packages/utilities/src/primitives/string.ts | 13 + packages/wallet/package.json | 11 +- .../components/CurrencyLogo/CurrencyLogo.tsx | 2 +- .../CurrencyLogo/LogoWithTxStatus.tsx | 2 +- .../src/components/CurrencyLogo/SplitLogo.tsx | 2 +- .../src/components/CurrencyLogo/TokenLogo.tsx | 2 +- .../src/components/RecipientSearch/hooks.ts | 6 + .../TokenSelector/SelectTokenButton.tsx | 2 +- .../TokenSelector/TokenSelector.tsx | 12 +- .../TokenSelectorEmptySearchList.tsx | 2 +- .../TokenSelector/TokenSelectorList.tsx | 2 +- .../src/components/TokenSelector/hooks.ts | 8 +- .../src/components/TokenSelector/types.ts | 2 +- .../src/components/TokenSelector/utils.ts | 2 +- .../WalletPreviewCard/WalletPreviewCard.tsx | 36 +- .../WalletPreviewCard.test.tsx.snap | 215 ++++---- .../components/accounts/AccountDetails.tsx | 6 +- .../src/components/input/MaxAmountButton.tsx | 34 +- .../legacy/CurrencyInputPanelLegacy.tsx | 6 +- .../src/components/text/RelativeChange.tsx | 3 +- .../RelativeChange.test.tsx.snap | 3 + packages/wallet/src/constants/chains.ts | 31 -- packages/wallet/src/constants/tokens.ts | 8 - .../src/contexts/WalletNavigationContext.tsx | 43 +- .../src/features/behaviorHistory/selectors.ts | 4 + .../src/features/behaviorHistory/slice.ts | 12 + .../wallet/src/features/chains/utils.test.ts | 11 - packages/wallet/src/features/chains/utils.ts | 28 - .../src/features/dataApi/balances.test.ts | 2 +- .../wallet/src/features/dataApi/balances.ts | 5 +- .../src/features/dataApi/searchTokens.ts | 4 +- .../src/features/dataApi/tokenProjects.ts | 4 +- .../wallet/src/features/dataApi/topTokens.ts | 4 +- .../wallet/src/features/dataApi/utils.test.ts | 2 +- packages/wallet/src/features/dataApi/utils.ts | 7 +- .../wallet/src/features/favorites/slice.ts | 6 +- .../wallet/src/features/images/NFTViewer.tsx | 4 +- .../src/features/notifications/types.ts | 2 +- .../features/portfolio/PortfolioBalance.tsx | 7 +- .../features/portfolio/TokenBalanceItem.tsx | 4 +- .../portfolio/TokenBalanceListContext.tsx | 2 +- .../wallet/src/features/providers/utils.ts | 3 - .../src/features/search/SearchTextInput.tsx | 6 +- .../wallet/src/features/tokens/safetyHooks.ts | 2 +- .../wallet/src/features/tokens/tokensSlice.ts | 2 +- .../src/features/tokens/useCurrencyInfo.ts | 2 +- .../SummaryItems/TransactionActionsModal.tsx | 2 +- .../SummaryItems/TransactionSummaryLayout.tsx | 2 +- .../TransactionReview/TransactionReview.tsx | 8 +- .../features/transactions/history/utils.ts | 3 +- .../wallet/src/features/transactions/hooks.ts | 5 +- .../hooks/useTokenAndFiatDisplayAmounts.tsx | 2 +- .../transactions/refetchGQLQueriesSaga.ts | 2 +- .../transactions/swap/CurrencyInputPanel.tsx | 22 +- .../swap/GasAndWarningRows.web.tsx | 2 +- .../transactions/swap/MaxAmountButton.tsx | 70 --- .../transactions/swap/SwapDetails.tsx | 2 +- .../swap/TransactionAmountsReview.tsx | 2 +- .../swap/hooks/useSwapPrefilledState.ts | 39 ++ .../swap/trade/hooks/useUSDCPrice.ts | 2 - .../swap/trade/hooks/useUSDTokenUpdater.ts | 2 +- .../src/features/transactions/swap/types.ts | 2 +- .../src/features/transactions/swap/utils.ts | 2 +- .../transactions/transactionWatcherSaga.ts | 4 +- .../transfer/TokenSelectorPanel.tsx | 3 +- .../transfer/TransferAmountInput.tsx | 7 +- .../transfer/hooks/useTransferWarnings.ts | 2 +- .../features/transactions/transfer/types.ts | 2 +- packages/wallet/src/features/unitags/api.ts | 75 ++- packages/wallet/src/features/unitags/hooks.ts | 48 +- packages/wallet/src/features/wallet/hooks.ts | 15 +- packages/wallet/src/telemetry/constants.ts | 6 + packages/wallet/src/telemetry/types.ts | 5 + .../src/test/fixtures/gql/assets/tokens.ts | 27 +- .../src/test/fixtures/wallet/balances.ts | 2 +- .../src/test/fixtures/wallet/currencies.ts | 2 +- .../wallet/src/test/utils/wallet/balances.ts | 2 +- packages/wallet/src/utils/balance.ts | 7 +- packages/wallet/src/utils/colors.tsx | 343 +------------ packages/wallet/src/utils/currencyId.ts | 3 +- yarn.lock | 149 +++--- 396 files changed, 9564 insertions(+), 3267 deletions(-) create mode 100644 apps/mobile/src/app/modals/ExtensionPromoModal.tsx create mode 100644 apps/mobile/src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay.tsx create mode 100644 apps/mobile/src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay.tsx create mode 100644 apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModalContent.tsx create mode 100644 apps/mobile/src/components/banners/ExtensionPromoBanner.tsx create mode 100644 apps/mobile/src/components/explore/FavoriteHeaderRow.test.tsx create mode 100644 apps/mobile/src/components/explore/FavoriteTokenCard.test.tsx create mode 100644 apps/mobile/src/components/explore/FavoriteWalletCard.test.tsx create mode 100644 apps/mobile/src/components/explore/RemoveButton.test.tsx create mode 100644 apps/mobile/src/components/explore/SortButton.test.tsx create mode 100644 apps/mobile/src/components/explore/TokenItem.test.tsx create mode 100644 apps/mobile/src/components/explore/__snapshots__/FavoriteHeaderRow.test.tsx.snap create mode 100644 apps/mobile/src/components/explore/__snapshots__/FavoriteTokenCard.test.tsx.snap create mode 100644 apps/mobile/src/components/explore/__snapshots__/FavoriteWalletCard.test.tsx.snap create mode 100644 apps/mobile/src/components/explore/__snapshots__/RemoveButton.test.tsx.snap create mode 100644 apps/mobile/src/components/explore/__snapshots__/SortButton.test.tsx.snap create mode 100644 apps/mobile/src/components/explore/__snapshots__/TokenItem.test.tsx.snap create mode 100644 apps/mobile/src/features/deepLinking/constants.ts create mode 100644 apps/mobile/src/features/scantastic/ExtensionWaitlistModal.tsx create mode 100644 apps/mobile/src/features/scantastic/ExtensionWaitlistModalState.ts delete mode 100644 apps/mobile/src/features/swap/hooks.ts create mode 100644 apps/mobile/src/test/fixtures/explore.ts create mode 100644 apps/web/functions/utils/getRGBColor.ts create mode 100644 apps/web/src/components/Logo/ChainSymbols/blast.svg create mode 100644 apps/web/src/components/Logo/ChainSymbols/blast_light.svg create mode 100644 apps/web/src/components/addLiquidity/OutOfSyncWarning.tsx delete mode 100644 apps/web/src/constants/tokenColors.ts create mode 100644 apps/web/src/hooks/useIsPoolOutOfSync.ts delete mode 100644 apps/web/src/hooks/useNetworkProviders.ts create mode 100644 apps/web/src/pages/AddLiquidity/blastAlerts.tsx create mode 100644 apps/web/src/state/limit/types.ts create mode 100644 apps/web/src/state/lists/types.ts create mode 100644 apps/web/src/state/swap/types.ts create mode 100644 apps/web/src/state/user/userAddedTokens.ts create mode 100644 apps/web/src/state/user/utils.ts delete mode 100644 apps/web/src/test-utils/images.ts delete mode 100644 apps/web/src/utils/getColor.test.ts delete mode 100644 apps/web/src/utils/getColor.ts delete mode 100644 apps/web/src/utils/platform.ts create mode 100644 packages/ui/src/assets/graphics/extension-promo-banner-dark.png create mode 100644 packages/ui/src/assets/graphics/extension-promo-banner-light.png create mode 100644 packages/ui/src/assets/graphics/extension-promo-modal-dark.png create mode 100644 packages/ui/src/assets/graphics/extension-promo-modal-light.png create mode 100644 packages/ui/src/assets/icons/code.svg create mode 100644 packages/ui/src/assets/icons/document-list.svg create mode 100644 packages/ui/src/assets/icons/gallery.svg create mode 100644 packages/ui/src/assets/icons/mobile.svg create mode 100644 packages/ui/src/assets/icons/person.svg create mode 100644 packages/ui/src/assets/icons/time-past.svg delete mode 100644 packages/ui/src/assets/logos/png/blast-logo.png create mode 100644 packages/ui/src/components/icons/Code.tsx create mode 100644 packages/ui/src/components/icons/DocumentList.tsx create mode 100644 packages/ui/src/components/icons/Gallery.tsx create mode 100644 packages/ui/src/components/icons/Person.tsx create mode 100644 packages/ui/src/utils/colors.ts create mode 100644 packages/uniswap/src/features/chains/utils.test.ts create mode 100644 packages/uniswap/src/features/chains/utils.ts rename packages/{wallet => uniswap}/src/features/dataApi/types.ts (91%) create mode 100644 packages/uniswap/src/types/currency.ts delete mode 100644 packages/wallet/src/features/transactions/swap/MaxAmountButton.tsx diff --git a/RELEASE b/RELEASE index 6bb1eabc5c0..cd3fa122b09 100644 --- a/RELEASE +++ b/RELEASE @@ -1,6 +1,6 @@ IPFS hash of the deployment: -- CIDv0: `QmbYSi6bDw9e8CzLAzN8k6UkcgGYCheJGqsexFZW9HCLnP` -- CIDv1: `bafybeigefvkmjdhesneplmk35lfudrzcycnhtb7awt76obc2ceggoimo5q` +- CIDv0: `QmUnNYjRF1siz1YhXyqh2UEoB5AJiTyiYihJCvPVm3FCdJ` +- CIDv1: `bafybeic7xtznmrazhjjf2wlmdwzng2qfw3th6skmbj6bjc4i452fvjevsu` The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org). @@ -10,15 +10,47 @@ You can also access the Uniswap Interface from an IPFS gateway. Your Uniswap settings are never remembered across different URLs. IPFS gateways: -- https://bafybeigefvkmjdhesneplmk35lfudrzcycnhtb7awt76obc2ceggoimo5q.ipfs.dweb.link/ -- https://bafybeigefvkmjdhesneplmk35lfudrzcycnhtb7awt76obc2ceggoimo5q.ipfs.cf-ipfs.com/ -- [ipfs://QmbYSi6bDw9e8CzLAzN8k6UkcgGYCheJGqsexFZW9HCLnP/](ipfs://QmbYSi6bDw9e8CzLAzN8k6UkcgGYCheJGqsexFZW9HCLnP/) +- https://bafybeic7xtznmrazhjjf2wlmdwzng2qfw3th6skmbj6bjc4i452fvjevsu.ipfs.dweb.link/ +- https://bafybeic7xtznmrazhjjf2wlmdwzng2qfw3th6skmbj6bjc4i452fvjevsu.ipfs.cf-ipfs.com/ +- [ipfs://QmUnNYjRF1siz1YhXyqh2UEoB5AJiTyiYihJCvPVm3FCdJ/](ipfs://QmUnNYjRF1siz1YhXyqh2UEoB5AJiTyiYihJCvPVm3FCdJ/) -### 5.21.1 (2024-03-29) +## 5.22.0 (2024-04-02) + + +### Features + +* **web:** add orderType to uniswapX POST order (#7071) 296a280 +* **web:** add warning when pool is out of sync (#7074) 0b4142b +* **web:** alerts for adding liquidity to blast (#7090) 4bb5949 +* **web:** blast (#6853) 2e14697 +* **web:** Condense SuggestionRow and Remove VE (#6961) ff0aea0 +* **web:** fix orderType to backend types (#7093) 516b4a9 +* **web:** only search for tokens on the connected chain (#7062) f21bd47 +* **web:** rm deprecated providers usage (#6891) c27e170 +* **web:** share gql types + query with mobile (#6898) 4f047c7 +* **web:** show user added tokens in currency list when using gql tokens (#7095) c21d5bd +* **web:** support parsing X v2 encoded orders (#7157) bbc7791 +* **web:** unregister the service worker (#7155) 563c75b ### Bug Fixes -* **web:** [hotfix] use chainId when getting token list currency (#7186) 92ec24b +* **web:** auto bade not centered (#7166) 1b11359 +* **web:** avoid stacking nested AnimatedDropdowns (#7097) cdee8c0 +* **web:** bump redux version to 8 (#7121) b0c0e4e +* **web:** clean up sentry integration (#6945) 7073673 +* **web:** fix failing type error on main (#7187) cec328f +* **web:** fix platform checks for mweb breaking uniwallet deeplinking (#7076) a4c6b86 +* **web:** fix prepare not generating graphql breaking vercel build (#7049) d6b7e02 +* **web:** remove circular dependencies in unit test code (#7057) 769c0b4 +* **web:** responsive swap header (#7117) cce2323 +* **web:** universal search e2e test (#7153) f767f6b +* **web:** update limit price signage (#7150) 2b303a9 +* **web:** use chainId when getting token list currency (#7184) 93e5c4c + + +### Tests + +* **web:** update SwapEventTimestampTracker to not use mocks (#7056) b1c7c61 diff --git a/VERSION b/VERSION index a32f0742657..3561d5c1296 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -web/5.21.1 \ No newline at end of file +web/5.22.0 \ No newline at end of file diff --git a/apps/mobile/README.md b/apps/mobile/README.md index 19ef49b29bb..0bd76016b7f 100644 --- a/apps/mobile/README.md +++ b/apps/mobile/README.md @@ -131,6 +131,8 @@ Note: The app will likely have limited functionality when running it locally wit Use the environment variables defined in the `.env.defaults.local` file to run the app locally. +You can use the command `yarn mobile env:local:download` if you have the 1password CLI to copy that file to your root folder. + ### Compile contract ABI types This is done in bootstrap but good to know about. Before the code will compile you need to generate types for the smart contracts the wallet interacts with. Run `yarn g:prepare` at the top level. Re-run this if the ABIs are ever changed. diff --git a/apps/mobile/android/app/build.gradle b/apps/mobile/android/app/build.gradle index 0454bfada44..815958611e7 100644 --- a/apps/mobile/android/app/build.gradle +++ b/apps/mobile/android/app/build.gradle @@ -107,9 +107,9 @@ android { include (*reactNativeArchitectures()) } } - lintOptions { - abortOnError false - } + lintOptions { + abortOnError false + } signingConfigs { debug { storeFile file('debug.keystore') @@ -131,17 +131,17 @@ android { dev { isDefault(true) applicationIdSuffix ".dev" - versionName "1.24" + versionName "1.25" dimension "variant" } beta { applicationIdSuffix ".beta" - versionName "1.24" + versionName "1.25" dimension "variant" } prod { dimension "variant" - versionName "1.24" + versionName "1.25" } } diff --git a/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj b/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj index 36a96ef310f..2fc2abbec90 100644 --- a/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj +++ b/apps/mobile/ios/Uniswap.xcodeproj/project.pbxproj @@ -2450,7 +2450,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -2496,7 +2496,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets; @@ -2542,7 +2542,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.widgets; @@ -2588,7 +2588,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets; @@ -2630,7 +2630,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -2673,7 +2673,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension; @@ -2716,7 +2716,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.WidgetIntentExtension; @@ -2759,7 +2759,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension; @@ -2795,7 +2795,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -2833,7 +2833,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -3003,7 +3003,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; @@ -3047,7 +3047,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension; @@ -3143,7 +3143,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -3214,7 +3214,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; MTL_FAST_MATH = YES; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension; @@ -3310,7 +3310,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -3381,7 +3381,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.24; + MARKETING_VERSION = 1.25; 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/package.json b/apps/mobile/package.json index 2a54cba272d..6c4b6ab6c0d 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -83,8 +83,8 @@ "@uniswap/analytics": "1.7.0", "@uniswap/analytics-events": "2.32.0", "@uniswap/ethers-rs-mobile": "0.0.5", - "@uniswap/sdk-core": "4.1.2", - "@uniswap/v3-sdk": "3.10.2", + "@uniswap/sdk-core": "4.2.0", + "@uniswap/v3-sdk": "3.11.0", "@walletconnect/core": "2.11.2", "@walletconnect/react-native-compat": "2.11.2", "@walletconnect/utils": "2.11.2", diff --git a/apps/mobile/src/app/App.tsx b/apps/mobile/src/app/App.tsx index 3f598554bb5..d8684a052e0 100644 --- a/apps/mobile/src/app/App.tsx +++ b/apps/mobile/src/app/App.tsx @@ -40,7 +40,11 @@ import { setI18NUserDefaults, } from 'src/features/widgets/widgets' import { useAppStateTrigger } from 'src/utils/useAppStateTrigger' -import { getSentryEnvironment, getStatsigEnvironmentTier } from 'src/utils/version' +import { + getSentryEnvironment, + getSentryTracesSamplingRate, + getStatsigEnvironmentTier, +} from 'src/utils/version' import { Statsig, StatsigProvider } from 'statsig-react-native' import { flexStyles, useIsDarkMode } from 'ui/src' import { config } from 'uniswap/src/config' @@ -52,6 +56,7 @@ import { import { WALLET_FEATURE_FLAG_NAMES } from 'uniswap/src/features/experiments/flags' 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 { registerConsoleOverrides } from 'utilities/src/logger/console' import { logger } from 'utilities/src/logger/logger' @@ -70,7 +75,6 @@ import { Account } from 'wallet/src/features/wallet/accounts/types' import { WalletContextProvider } from 'wallet/src/features/wallet/context' import { useAccounts } from 'wallet/src/features/wallet/hooks' import { SharedProvider } from 'wallet/src/provider' -import { CurrencyId } from 'wallet/src/utils/currencyId' import { beforeSend } from 'wallet/src/utils/sentry' enableFreeze(true) @@ -88,9 +92,7 @@ if (!__DEV__ && !isDetoxBuild) { dsn: config.sentryDsn, attachViewHierarchy: true, enableCaptureFailedRequests: true, - tracesSampler: (_) => { - return 0.2 - }, + tracesSampleRate: getSentryTracesSamplingRate(), integrations: [ new Sentry.ReactNativeTracing({ enableUserInteractionTracing: true, diff --git a/apps/mobile/src/app/MobileWalletNavigationProvider.tsx b/apps/mobile/src/app/MobileWalletNavigationProvider.tsx index 975e793bfdb..b6e364802a6 100644 --- a/apps/mobile/src/app/MobileWalletNavigationProvider.tsx +++ b/apps/mobile/src/app/MobileWalletNavigationProvider.tsx @@ -9,6 +9,7 @@ import { NavigateToNftItemArgs, NavigateToSwapFlowArgs, WalletNavigationProvider, + getNavigateToSwapFlowArgsInitialState, } from 'wallet/src/contexts/WalletNavigationContext' import { useFiatOnRampIpAddressQuery } from 'wallet/src/features/fiatOnRamp/api' import { ModalName } from 'wallet/src/telemetry/constants' @@ -47,8 +48,7 @@ function useNavigateToSwapFlow(): (args: NavigateToSwapFlowArgs) => void { return useCallback( (args: NavigateToSwapFlowArgs): void => { - const initialState = args?.initialState - + const initialState = getNavigateToSwapFlowArgsInitialState(args) dispatch(closeModal({ name: ModalName.Swap })) dispatch(openModal({ name: ModalName.Swap, initialState })) }, diff --git a/apps/mobile/src/app/migrations.test.ts b/apps/mobile/src/app/migrations.test.ts index b75af54cf34..9fe41f4050e 100644 --- a/apps/mobile/src/app/migrations.test.ts +++ b/apps/mobile/src/app/migrations.test.ts @@ -61,6 +61,7 @@ import { v59Schema, v5Schema, v60Schema, + v61Schema, v6Schema, v7Schema, v8Schema, @@ -76,7 +77,10 @@ import { initialTelemetryState } from 'src/features/telemetry/slice' import { initialTweaksState } from 'src/features/tweaks/slice' import { initialWalletConnectState } from 'src/features/walletConnect/walletConnectSlice' import { ChainId } from 'wallet/src/constants/chains' -import { initialBehaviorHistoryState } from 'wallet/src/features/behaviorHistory/slice' +import { + ExtensionOnboardingState, + initialBehaviorHistoryState, +} from 'wallet/src/features/behaviorHistory/slice' import { initialFavoritesState } from 'wallet/src/features/favorites/slice' import { initialFiatCurrencyState } from 'wallet/src/features/fiatCurrency/slice' import { initialLanguageState } from 'wallet/src/features/language/slice' @@ -1379,4 +1383,11 @@ describe('Redux state migrations', () => { [nftKey4]: { isVisible: false }, }) }) + + it('migrates from v61 to 62', () => { + const v61Stub = { ...v61Schema } + const v62 = migrations[62](v61Stub) + + expect(v62.behaviorHistory.extensionOnboardingState).toBe(ExtensionOnboardingState.Undefined) + }) }) diff --git a/apps/mobile/src/app/migrations.ts b/apps/mobile/src/app/migrations.ts index 1e3f4d32ea0..266352da374 100644 --- a/apps/mobile/src/app/migrations.ts +++ b/apps/mobile/src/app/migrations.ts @@ -5,6 +5,7 @@ import dayjs from 'dayjs' import { ChainId } from 'wallet/src/constants/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' @@ -863,4 +864,15 @@ export const migrations = { return newState }, + + 62: function addExtensionOnboardingState(state: any) { + const newState = { ...state } + + newState.behaviorHistory = { + ...state.behaviorHistory, + extensionOnboardingState: ExtensionOnboardingState.Undefined, + } + + return newState + }, } diff --git a/apps/mobile/src/app/modals/AppModals.tsx b/apps/mobile/src/app/modals/AppModals.tsx index f670de78ea0..03a706d1e34 100644 --- a/apps/mobile/src/app/modals/AppModals.tsx +++ b/apps/mobile/src/app/modals/AppModals.tsx @@ -15,6 +15,7 @@ import { LockScreenModal } from 'src/features/authentication/LockScreenModal' import { ExchangeTransferModal } from 'src/features/fiatOnRamp/ExchangeTransferModal' import { FiatOnRampAggregatorModal } from 'src/features/fiatOnRamp/FiatOnRampAggregatorModal' import { FiatOnRampModal } from 'src/features/fiatOnRamp/FiatOnRampModal' +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' @@ -48,6 +49,10 @@ export function AppModals(): JSX.Element { + + + + diff --git a/apps/mobile/src/app/modals/ExtensionPromoModal.tsx b/apps/mobile/src/app/modals/ExtensionPromoModal.tsx new file mode 100644 index 00000000000..211ccee0d18 --- /dev/null +++ b/apps/mobile/src/app/modals/ExtensionPromoModal.tsx @@ -0,0 +1,58 @@ +import React from 'react' +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 { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' +import { ModalName } from 'wallet/src/telemetry/constants' + +export function ExtensionPromoModal({ onClose }: { onClose: () => void }): JSX.Element { + const { t } = useTranslation() + const isDarkMode = useIsDarkMode() + + return ( + + + + + {t('home.modal.getExtension.title')} + + + , + }} + i18nKey="home.modal.getExtension.step1" + /> + + + {t('home.modal.getExtension.step2')} + + + {t('home.modal.getExtension.step3')} + + + + + + + ) +} + +const ImageStyles = StyleSheet.create({ + responsiveImage: { + aspectRatio: 686 / 430, + height: undefined, + width: '100%', + }, +}) 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 3a625899ee2..fcf6cd9bd41 100644 --- a/apps/mobile/src/app/modals/__snapshots__/AccountSwitcherModal.test.tsx.snap +++ b/apps/mobile/src/app/modals/__snapshots__/AccountSwitcherModal.test.tsx.snap @@ -420,8 +420,8 @@ exports[`AccountSwitcher renders correctly 1`] = ` "alignItems": "center", "backgroundColor": "#F9F9F9", "borderBottomColor": "transparent", - "borderBottomLeftRadius": 8, - "borderBottomRightRadius": 8, + "borderBottomLeftRadius": 12, + "borderBottomRightRadius": 12, "borderBottomWidth": 1, "borderLeftColor": "transparent", "borderLeftWidth": 1, @@ -429,8 +429,8 @@ exports[`AccountSwitcher renders correctly 1`] = ` "borderRightWidth": 1, "borderStyle": "solid", "borderTopColor": "transparent", - "borderTopLeftRadius": 8, - "borderTopRightRadius": 8, + "borderTopLeftRadius": 12, + "borderTopRightRadius": 12, "borderTopWidth": 1, "flexDirection": "row", "gap": 4, diff --git a/apps/mobile/src/app/schema.ts b/apps/mobile/src/app/schema.ts index 0f558c37cdb..70bc54dc125 100644 --- a/apps/mobile/src/app/schema.ts +++ b/apps/mobile/src/app/schema.ts @@ -1,3 +1,4 @@ +import { ExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/slice' import { initialFiatCurrencyState } from 'wallet/src/features/fiatCurrency/slice' import { initialLanguageState } from 'wallet/src/features/language/slice' import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice' @@ -465,6 +466,15 @@ export const v61Schema = { nftsVisibility: {}, }, } + +export const v62Schema = { + ...v61Schema, + behaviorHistory: { + ...v61Schema.behaviorHistory, + extensionOnboardingState: ExtensionOnboardingState.Undefined, + }, +} + // TODO: [MOB-201] use function with typed output when API reducers are removed from rootReducer // export const getSchema = (): RootState => v0Schema -export const getSchema = (): typeof v61Schema => v61Schema +export const getSchema = (): typeof v62Schema => v62Schema diff --git a/apps/mobile/src/app/store.ts b/apps/mobile/src/app/store.ts index 6b6c1494d65..537c0208fe8 100644 --- a/apps/mobile/src/app/store.ts +++ b/apps/mobile/src/app/store.ts @@ -75,7 +75,7 @@ export const persistConfig = { key: 'root', storage: reduxStorage, whitelist, - version: 61, + version: 62, migrate: createMigrate(migrations), } diff --git a/apps/mobile/src/components/AnimatedNumber.tsx b/apps/mobile/src/components/AnimatedNumber.tsx index 6f247a8810b..7c89c8914f0 100644 --- a/apps/mobile/src/components/AnimatedNumber.tsx +++ b/apps/mobile/src/components/AnimatedNumber.tsx @@ -42,6 +42,7 @@ const RollNumber = ({ chars, commonPrefixLength, shouldFadeDecimals, + disableAnimations, }: { chars: string[] digit?: string @@ -49,6 +50,7 @@ const RollNumber = ({ index: number commonPrefixLength: number shouldFadeDecimals: boolean + disableAnimations?: boolean }): JSX.Element => { const colors = useSporeColors() const fontColor = useSharedValue( @@ -60,6 +62,10 @@ const RollNumber = ({ useEffect(() => { const finishColor = shouldFadeDecimals && index > chars.length - 4 ? colors.neutral3.val : colors.neutral1.val + if (disableAnimations) { + fontColor.value = finishColor + return + } if (nextColor && index > commonPrefixLength - 1) { fontColor.value = withSequence( withTiming(nextColor, { duration: 250 }), @@ -78,6 +84,7 @@ const RollNumber = ({ commonPrefixLength, fontColor, shouldFadeDecimals, + disableAnimations, ]) const animatedFontStyle = useAnimatedStyle(() => { @@ -99,7 +106,8 @@ const RollNumber = ({ useEffect(() => { if (digit && Number(digit) >= 0) { - yOffset.value = withTiming(DIGIT_HEIGHT * -digit) + const newOffset = DIGIT_HEIGHT * -digit + yOffset.value = disableAnimations ? newOffset : withTiming(newOffset) } }) @@ -139,23 +147,26 @@ const Char = ({ nextColor, commonPrefixLength, shouldFadeDecimals, + disableAnimations, }: { index: number chars: string[] nextColor?: string commonPrefixLength: number shouldFadeDecimals: boolean + disableAnimations?: boolean }): JSX.Element => { return ( { const prevValue = usePrevious(value) const [chars, setChars] = useState() @@ -237,11 +250,11 @@ const AnimatedNumber = ({ if (newScale < 1) { const newOffset = (e.nativeEvent.layout.width - e.nativeEvent.layout.width * newScale) / 2 - scale.value = withTiming(newScale) - offset.value = withTiming(-newOffset) + scale.value = disableAnimations ? newScale : withTiming(newScale) + offset.value = disableAnimations ? -newOffset : withTiming(-newOffset) } else if (scale.value < 1) { - scale.value = withTiming(1) - offset.value = withTiming(0) + scale.value = disableAnimations ? 1 : withTiming(1) + offset.value = disableAnimations ? 0 : withTiming(0) } } @@ -278,6 +291,7 @@ const AnimatedNumber = ({ } chars={placeholderChars} commonPrefixLength={commonPrefixLength} + disableAnimations={disableAnimations} index={index} nextColor={nextColor} shouldFadeDecimals={shouldFadeDecimals} @@ -306,6 +320,7 @@ const AnimatedNumber = ({ } chars={chars} commonPrefixLength={commonPrefixLength} + disableAnimations={disableAnimations} index={index} nextColor={nextColor} shouldFadeDecimals={shouldFadeDecimals} diff --git a/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx b/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx index 77d26a4d0ff..79c21d57cbb 100644 --- a/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx +++ b/apps/mobile/src/components/PriceExplorer/PriceExplorer.tsx @@ -13,9 +13,9 @@ import { Loader } from 'src/components/loading' import { Flex, HapticFeedback } from 'ui/src' import { spacing } from 'ui/src/theme' import { HistoryDuration } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { CurrencyId } from 'uniswap/src/types/currency' import { useAppFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' -import { CurrencyId } from 'wallet/src/utils/currencyId' import { PriceNumberOfDigits, TokenSpotData, useTokenPriceHistory } from './usePriceHistory' type PriceTextProps = { diff --git a/apps/mobile/src/components/PriceExplorer/PriceExplorerAnimatedNumber.tsx b/apps/mobile/src/components/PriceExplorer/PriceExplorerAnimatedNumber.tsx index 10626f4923a..8cec026e13c 100644 --- a/apps/mobile/src/components/PriceExplorer/PriceExplorerAnimatedNumber.tsx +++ b/apps/mobile/src/components/PriceExplorer/PriceExplorerAnimatedNumber.tsx @@ -1,8 +1,10 @@ +import { SCREEN_WIDTH } from '@gorhom/bottom-sheet' import _ from 'lodash' import React, { useEffect, useState } from 'react' import { StyleSheet, Text, View } from 'react-native' import Animated, { SharedValue, + useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, @@ -294,6 +296,8 @@ const LoadingWrapper = (): JSX.Element | null => { ) } +const SCREEN_WIDTH_BUFFER = 50 + const PriceExplorerAnimatedNumber = ({ price, numberOfDigits, @@ -305,6 +309,8 @@ const PriceExplorerAnimatedNumber = ({ }): JSX.Element => { const colors = useSporeColors() const hideShimmer = useSharedValue(false) + const scale = useSharedValue(1) + const offset = useSharedValue(0) const animatedWrapperStyle = useAnimatedStyle(() => { return { opacity: price.value.value > 0 && hideShimmer.value ? 0 : 1, @@ -320,6 +326,31 @@ const PriceExplorerAnimatedNumber = ({ } }) + useAnimatedReaction( + () => { + return Number( + [0, ...price.formatted.value.split('')].reduce((accumulator, currentValue) => { + if (NUMBER_WIDTH_ARRAY[Number(currentValue)]) { + return Number(accumulator) + Number(NUMBER_WIDTH_ARRAY[Number(currentValue)]) + } + return accumulator + }) + ) + }, + (priceWidth: number) => { + const newScale = (SCREEN_WIDTH - SCREEN_WIDTH_BUFFER) / priceWidth + + if (newScale < 1) { + const newOffset = (priceWidth - priceWidth * newScale) / 2 + scale.value = withTiming(newScale) + offset.value = withTiming(-newOffset) + } else if (scale.value < 1) { + scale.value = withTiming(1) + offset.value = withTiming(0) + } + } + ) + const hidePlaceholder = (): void => { hideShimmer.value = true } @@ -344,8 +375,18 @@ const PriceExplorerAnimatedNumber = ({ ) + const scaleWraper = useAnimatedStyle(() => { + return { + transform: [ + { translateX: -SCREEN_WIDTH / 2 }, + { scale: scale.value }, + { translateX: SCREEN_WIDTH / 2 }, + ], + } + }) + return ( - <> + @@ -356,7 +397,7 @@ const PriceExplorerAnimatedNumber = ({ {Numbers({ price, hidePlaceholder, numberOfDigits, currency })} {!currency.symbolAtFront && currencySymbol} - + ) } diff --git a/apps/mobile/src/components/PriceExplorer/usePriceHistory.test.ts b/apps/mobile/src/components/PriceExplorer/usePriceHistory.test.ts index b6855878a52..5041e60c8c2 100644 --- a/apps/mobile/src/components/PriceExplorer/usePriceHistory.test.ts +++ b/apps/mobile/src/components/PriceExplorer/usePriceHistory.test.ts @@ -115,7 +115,7 @@ describe(useTokenPriceHistory, () => { expect(result.current.numberOfDigits).toEqual({ left: 1, - right: 10, + right: 16, }) }) diff --git a/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts b/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts index 7199861575c..40e60bcd5a5 100644 --- a/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts +++ b/apps/mobile/src/components/PriceExplorer/usePriceHistory.ts @@ -100,12 +100,13 @@ export function useTokenPriceHistory( if (!maxPriceInHistory && price === undefined) { return lastNumberOfDigits.current } + const maxPrice = Math.max(maxPriceInHistory || 0, price || 0) const convertedMaxValue = convertFiatAmount(maxPrice).amount const newNumberOfDigits = { left: String(convertedMaxValue).split('.')[0]?.length || 10, - right: Number(String(convertedMaxValue.toFixed(10)).split('.')[0]) > 0 ? 2 : 10, + right: Number(String(convertedMaxValue.toFixed(16)).split('.')[0]) > 0 ? 2 : 16, } lastNumberOfDigits.current = newNumberOfDigits diff --git a/apps/mobile/src/components/QRCodeScanner/QRCode.tsx b/apps/mobile/src/components/QRCodeScanner/QRCode.tsx index 456e875f688..86ea046a120 100644 --- a/apps/mobile/src/components/QRCodeScanner/QRCode.tsx +++ b/apps/mobile/src/components/QRCodeScanner/QRCode.tsx @@ -5,6 +5,8 @@ import { ColorTokens, Flex, getUniconV2Colors, + passesContrast, + useExtractedColors, useIsDarkMode, useSporeColors, useUniconColors, @@ -16,7 +18,6 @@ import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' import { isAndroid } from 'uniswap/src/utils/platform' import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' import { useAvatar } from 'wallet/src/features/wallet/hooks' -import { passesContrast, useExtractedColors } from 'wallet/src/utils/colors' type AvatarColors = { primary: string diff --git a/apps/mobile/src/components/TokenBalanceList/TokenBalanceItemContextMenu.tsx b/apps/mobile/src/components/TokenBalanceList/TokenBalanceItemContextMenu.tsx index a7cab10ecb6..6f1753de7b9 100644 --- a/apps/mobile/src/components/TokenBalanceList/TokenBalanceItemContextMenu.tsx +++ b/apps/mobile/src/components/TokenBalanceList/TokenBalanceItemContextMenu.tsx @@ -2,7 +2,7 @@ import React, { memo, useMemo } from 'react' import ContextMenu from 'react-native-context-menu-view' import { useTokenContextMenu } from 'src/features/balances/hooks' import { borderRadii } from 'ui/src/theme' -import { PortfolioBalance } from 'wallet/src/features/dataApi/types' +import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' export const TokenBalanceItemContextMenu = memo(function _TokenBalanceItem({ portfolioBalance, diff --git a/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx b/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx index a1e577d1b8e..aed50aadd53 100644 --- a/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx +++ b/apps/mobile/src/components/TokenBalanceList/TokenBalanceList.tsx @@ -23,6 +23,7 @@ import { useSporeColors, } from 'ui/src' import { zIndices } from 'ui/src/theme' +import { CurrencyId } from 'uniswap/src/types/currency' import { isAndroid } from 'uniswap/src/utils/platform' import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard' import { isError, isNonPollingRequestInFlight } from 'wallet/src/data/utils' @@ -34,7 +35,6 @@ import { TokenBalanceListRow, useTokenBalanceListContext, } from 'wallet/src/features/portfolio/TokenBalanceListContext' -import { CurrencyId } from 'wallet/src/utils/currencyId' type TokenBalanceListProps = TabProps & { empty?: JSX.Element | null diff --git a/apps/mobile/src/components/TokenDetails/TokenBalances.tsx b/apps/mobile/src/components/TokenDetails/TokenBalances.tsx index a864686e1c0..7b165c7a513 100644 --- a/apps/mobile/src/components/TokenDetails/TokenBalances.tsx +++ b/apps/mobile/src/components/TokenDetails/TokenBalances.tsx @@ -5,15 +5,15 @@ import Trace from 'src/components/Trace/Trace' import { MobileEventName } from 'src/features/telemetry/constants' import { Flex, Separator, Text, TouchableArea, useSporeColors } from 'ui/src' import { iconSizes } from 'ui/src/theme' +import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' +import { CurrencyId } from 'uniswap/src/types/currency' import { NumberType } from 'utilities/src/format/types' import { TokenLogo } from 'wallet/src/components/CurrencyLogo/TokenLogo' import { InlineNetworkPill } from 'wallet/src/components/network/NetworkPill' -import { PortfolioBalance } from 'wallet/src/features/dataApi/types' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { AccountType } from 'wallet/src/features/wallet/accounts/types' import { useActiveAccount, useDisplayName } from 'wallet/src/features/wallet/hooks' import { getSymbolDisplayText } from 'wallet/src/utils/currency' -import { CurrencyId } from 'wallet/src/utils/currencyId' import { SendButton } from './SendButton' /** diff --git a/apps/mobile/src/components/TokenDetails/hooks.ts b/apps/mobile/src/components/TokenDetails/hooks.ts index 151d04021c8..0534f935e58 100644 --- a/apps/mobile/src/components/TokenDetails/hooks.ts +++ b/apps/mobile/src/components/TokenDetails/hooks.ts @@ -6,11 +6,11 @@ import { Chain, useTokenDetailsScreenLazyQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' +import { CurrencyId } from 'uniswap/src/types/currency' import { fromGraphQLChain } from 'wallet/src/features/chains/utils' -import { PortfolioBalance } from 'wallet/src/features/dataApi/types' import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils' import { - CurrencyId, buildCurrencyId, buildNativeCurrencyId, currencyIdToChain, diff --git a/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx b/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx index 940ac176c20..920f20d5689 100644 --- a/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx +++ b/apps/mobile/src/components/TokenSelector/TokenFiatOnRampList.tsx @@ -4,11 +4,11 @@ import { useTranslation } from 'react-i18next' import { ListRenderItemInfo } from 'react-native' import { FiatOnRampCurrency } from 'src/features/fiatOnRamp/types' import { Flex, Inset, Loader } from 'ui/src' +import { CurrencyId } from 'uniswap/src/types/currency' import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard' import { TokenOptionItem } from 'wallet/src/components/TokenSelector/TokenOptionItem' import { useBottomSheetFocusHook } from 'wallet/src/components/modals/hooks' import { ChainId } from 'wallet/src/constants/chains' -import { CurrencyId } from 'wallet/src/utils/currencyId' interface Props { onSelectCurrency: (currency: FiatOnRampCurrency) => void diff --git a/apps/mobile/src/components/WalletConnect/DappHeaderIcon.tsx b/apps/mobile/src/components/WalletConnect/DappHeaderIcon.tsx index 4f7d71a50c4..ac45eea48e7 100644 --- a/apps/mobile/src/components/WalletConnect/DappHeaderIcon.tsx +++ b/apps/mobile/src/components/WalletConnect/DappHeaderIcon.tsx @@ -2,9 +2,9 @@ import React from 'react' import { StyleSheet } from 'react-native' import { Flex } from 'ui/src' import { borderRadii, iconSizes } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { CurrencyLogo } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' import { DappIconPlaceholder } from 'wallet/src/components/WalletConnect/DappIconPlaceholder' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { ImageUri } from 'wallet/src/features/images/ImageUri' import { DappInfo } from 'wallet/src/features/walletConnect/types' diff --git a/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay.tsx b/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay.tsx new file mode 100644 index 00000000000..4819132e19e --- /dev/null +++ b/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay.tsx @@ -0,0 +1,201 @@ +import { + BottomSheetFooter, + BottomSheetScrollView, + useBottomSheetInternal, +} from '@gorhom/bottom-sheet' +import { PropsWithChildren, useCallback, useRef, useState } from 'react' +import { useTranslation } from 'react-i18next' +import { + LayoutChangeEvent, + MeasureLayoutOnSuccessCallback, + NativeScrollEvent, + NativeSyntheticEvent, + ScrollView, + View, +} from 'react-native' +import { useDerivedValue } from 'react-native-reanimated' +import { ScrollDownOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay' +import { Button, Flex, useDeviceInsets } from 'ui/src' +import { spacing } from 'ui/src/theme' +import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' +import { BottomSheetModalProps } from 'wallet/src/components/modals/BottomSheetModalProps' +import { ElementName } from 'wallet/src/telemetry/constants' + +const MEASURE_LAYOUT_TIMEOUT = 100 + +type ModalWithOverlayProps = PropsWithChildren< + BottomSheetModalProps & { + confirmationButtonText?: string + scrollDownButtonText?: string + onReject: () => void + onConfirm: () => void + } +> + +const isCloseToBottom = ({ + layoutMeasurement, + contentOffset, + contentSize, +}: NativeScrollEvent): boolean => { + return layoutMeasurement.height + contentOffset.y >= contentSize.height - spacing.spacing24 +} + +export function ModalWithOverlay({ + children, + confirmationButtonText, + scrollDownButtonText, + onReject, + onConfirm, + ...bottomSheetModalProps +}: ModalWithOverlayProps): JSX.Element { + const scrollViewRef = useRef(null) + const contentViewRef = useRef(null) + const measureLayoutTimeoutRef = useRef() + + const startedScrollingRef = useRef(false) + const [showOverlay, setShowOverlay] = useState(false) + const [confirmationEnabled, setConfirmationEnabled] = useState(false) + + const handleScroll = useCallback( + ({ nativeEvent }: NativeSyntheticEvent) => { + startedScrollingRef.current = true + if (showOverlay) { + setShowOverlay(false) + } + if (isCloseToBottom(nativeEvent)) { + setConfirmationEnabled(true) + } + }, + [showOverlay] + ) + + const handleScrollDown = useCallback(() => { + scrollViewRef.current?.scrollToEnd() + }, []) + + const measureContent = useCallback((parentHeight: number) => { + const onSuccess: MeasureLayoutOnSuccessCallback = (x, y, w, h) => { + if (h > parentHeight) { + setShowOverlay(!startedScrollingRef.current) + } else { + setConfirmationEnabled(true) + } + } + + const contentNode = contentViewRef.current + + if (contentNode) { + contentNode.measure(onSuccess) + } else { + setConfirmationEnabled(true) + } + }, []) + + const handleScrollViewLayout = useCallback( + (e: LayoutChangeEvent) => { + const parentHeight = e.nativeEvent.layout.height + if (measureLayoutTimeoutRef.current) { + clearTimeout(measureLayoutTimeoutRef.current) + } + // BottomSheetScrollView calls onLayout multiple times with different + // height values. In order to make a correct measurement, we have to + // ignore all measurements except the last one, thus we add the timeout + // to cancel measurements when onLayout is called within a small interval + measureLayoutTimeoutRef.current = setTimeout(() => { + measureContent(parentHeight) + }, MEASURE_LAYOUT_TIMEOUT) + }, + [measureContent] + ) + + return ( + + + {children} + + + + + ) +} + +type ModalFooterProps = { + confirmationEnabled: boolean + showScrollDownOverlay: boolean + confirmationButtonText?: string + scrollDownButtonText?: string + onScrollDownPress: () => void + onReject: () => void + onConfirm: () => void +} + +function ModalFooter({ + confirmationEnabled, + showScrollDownOverlay, + scrollDownButtonText, + confirmationButtonText, + onScrollDownPress, + onReject, + onConfirm, +}: ModalFooterProps): JSX.Element { + const { t } = useTranslation() + const insets = useDeviceInsets() + const { animatedPosition, animatedHandleHeight, animatedFooterHeight, animatedContainerHeight } = + useBottomSheetInternal() + + // Calculate position of the modal footer to ensure it stays at the bottom of the screen + // when the modal content is scrolled + const animatedFooterPosition = useDerivedValue( + () => + Math.max(0, animatedContainerHeight.value - animatedPosition.value) - + animatedFooterHeight.value - + animatedHandleHeight.value + ) + + return ( + + {showScrollDownOverlay && ( + + )} + + + + + + + ) +} diff --git a/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay.tsx b/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay.tsx new file mode 100644 index 00000000000..3b1ded6e677 --- /dev/null +++ b/apps/mobile/src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay.tsx @@ -0,0 +1,61 @@ +import { useTranslation } from 'react-i18next' +import { StyleSheet } from 'react-native' +import { FadeInDown, FadeOut } from 'react-native-reanimated' +import Svg, { Defs, LinearGradient, Rect, Stop } from 'react-native-svg' +import { + AnimatedFlex, + Flex, + Text, + TouchableArea, + useDeviceDimensions, + useSporeColors, +} from 'ui/src' +import { ArrowDown } from 'ui/src/components/icons' +import { iconSizes } from 'ui/src/theme' + +type ScrollDownOverlayProps = { + scrollDownButonText?: string + onScrollDownPress: () => void +} + +export function ScrollDownOverlay({ + onScrollDownPress, + scrollDownButonText, +}: ScrollDownOverlayProps): JSX.Element { + const { t } = useTranslation() + const { fullHeight, fullWidth } = useDeviceDimensions() + const colors = useSporeColors() + + return ( + + + + + + + + + + + + + + + + {scrollDownButonText ?? t('common.button.scrollDown')} + + + + + ) +} diff --git a/apps/mobile/src/components/WalletConnect/NetworkLogos.tsx b/apps/mobile/src/components/WalletConnect/NetworkLogos.tsx index 63fe433a3e7..ffc41ba33dc 100644 --- a/apps/mobile/src/components/WalletConnect/NetworkLogos.tsx +++ b/apps/mobile/src/components/WalletConnect/NetworkLogos.tsx @@ -35,10 +35,11 @@ export function NetworkLogos({ {chains.map((chainId) => ( - + ))} diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/HeaderText.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/HeaderText.tsx index 3fd42bf45c7..bbe662e38ca 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/HeaderText.tsx +++ b/apps/mobile/src/components/WalletConnect/RequestModal/HeaderText.tsx @@ -1,7 +1,6 @@ import { Currency } from '@uniswap/sdk-core' import React from 'react' import { Trans } from 'react-i18next' -import { truncateDappName } from 'src/components/WalletConnect/ScanSheet/util' import { WalletConnectRequest } from 'src/features/walletConnect/walletConnectSlice' import { Text } from 'ui/src' import { EthMethod } from 'wallet/src/features/walletConnect/types' @@ -70,7 +69,7 @@ export function HeaderText({ return ( - {getReadableMethodName(method, truncateDappName(dapp.name || dapp.url))} + {getReadableMethodName(method, dapp.name || dapp.url)} ) } diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModal.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModal.tsx index d3e8496fcad..e02a892dd53 100644 --- a/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModal.tsx +++ b/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModal.tsx @@ -1,87 +1,40 @@ import { useNetInfo } from '@react-native-community/netinfo' import { getSdkError } from '@walletconnect/utils' import { providers } from 'ethers' -import React, { PropsWithChildren, useMemo, useRef } from 'react' +import React, { useMemo, useRef } from 'react' import { useTranslation } from 'react-i18next' -import { StyleProp, ViewStyle } from 'react-native' import { useAppDispatch, useAppSelector } from 'src/app/hooks' -import { ClientDetails, PermitInfo } from 'src/components/WalletConnect/RequestModal/ClientDetails' +import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay' +import { + WalletConnectRequestModalContent, + methodCostsGas, +} from 'src/components/WalletConnect/RequestModal/WalletConnectRequestModalContent' import { useHasSufficientFunds } from 'src/components/WalletConnect/RequestModal/hooks' -import { RequestDetails } from 'src/components/WalletConnect/RequestModal/RequestDetails' import { useBiometricAppSettings, useBiometricPrompt } from 'src/features/biometrics/hooks' import { sendMobileAnalyticsEvent } from 'src/features/telemetry' import { MobileEventName } from 'src/features/telemetry/constants' +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 { returnToPreviousApp } from 'src/features/walletConnect/WalletConnect' import { - isTransactionRequest, SignRequest, TransactionRequest, - WalletConnectRequest, + isTransactionRequest, } from 'src/features/walletConnect/walletConnectSlice' -import { Button, Flex, Text, useSporeColors } from 'ui/src' -import AlertTriangle from 'ui/src/assets/icons/alert-triangle.svg' -import { iconSizes } from 'ui/src/theme' -import { logger } from 'utilities/src/logger/logger' -import { AccountDetails } from 'wallet/src/components/accounts/AccountDetails' -import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard' -import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' -import { NetworkFee } from 'wallet/src/components/network/NetworkFee' -import { NetworkPill } from 'wallet/src/components/network/NetworkPill' import { useTransactionGasFee } from 'wallet/src/features/gas/hooks' import { GasSpeed } from 'wallet/src/features/gas/types' -import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' -import { BlockedAddressWarning } from 'wallet/src/features/trm/BlockedAddressWarning' import { useIsBlocked, useIsBlockedActiveAddress } from 'wallet/src/features/trm/hooks' import { useSignerAccounts } from 'wallet/src/features/wallet/hooks' -import { - EthMethod, - isPrimaryTypePermit, - WCEventType, - WCRequestOutcome, -} from 'wallet/src/features/walletConnect/types' -import { ElementName, ModalName } from 'wallet/src/telemetry/constants' +import { EthMethod, WCEventType, WCRequestOutcome } from 'wallet/src/features/walletConnect/types' +import { ModalName } from 'wallet/src/telemetry/constants' import { areAddressesEqual } from 'wallet/src/utils/addresses' -import { buildCurrencyId } from 'wallet/src/utils/currencyId' - -const MAX_MODAL_MESSAGE_HEIGHT = 200 interface Props { onClose: () => void request: SignRequest | TransactionRequest } -const isPotentiallyUnsafe = (request: WalletConnectRequest): boolean => - request.type !== EthMethod.PersonalSign - -const methodCostsGas = (request: WalletConnectRequest): request is TransactionRequest => - request.type === EthMethod.EthSendTransaction - -/** If the request is a permit then parse the relevant information otherwise return undefined. */ -const getPermitInfo = (request: WalletConnectRequest): PermitInfo | undefined => { - if (request.type !== EthMethod.SignTypedDataV4) { - return undefined - } - - try { - const message = JSON.parse(request.rawMessage) - if (!isPrimaryTypePermit(message)) { - return undefined - } - - const { domain, message: permitPayload } = message - const currencyId = buildCurrencyId(domain.chainId, domain.verifyingContract) - const amount = permitPayload.value - - return { currencyId, amount } - } catch (error) { - logger.error(error, { tags: { file: 'WalletConnectRequestModal', function: 'getPermitInfo' } }) - return undefined - } -} - const VALID_REQUEST_TYPES = [ EthMethod.PersonalSign, EthMethod.SignTypedData, @@ -90,19 +43,8 @@ const VALID_REQUEST_TYPES = [ EthMethod.EthSendTransaction, ] -function SectionContainer({ - children, - style, -}: PropsWithChildren<{ style?: StyleProp }>): JSX.Element | null { - return children ? ( - - {children} - - ) : null -} - export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Element | null { - const colors = useSporeColors() + const { t } = useTranslation() const netInfo = useNetInfo() const didOpenFromDeepLink = useAppSelector(selectDidOpenFromDeepLink) const chainId = request.chainId @@ -129,7 +71,6 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem const { isBlocked: isSenderBlocked, isBlockedLoading: isSenderBlockedLoading } = useIsBlockedActiveAddress() - const { isBlocked: isRecipientBlocked, isBlockedLoading: isRecipientBlockedLoading } = useIsBlocked(tx?.to) @@ -161,8 +102,6 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem } const confirmEnabled = checkConfirmEnabled() - - const { t } = useTranslation() const dispatch = useAppDispatch() /** * TODO: [MOB-239] implement this behavior in a less janky way. Ideally if we can distinguish between `onClose` being called programmatically and `onClose` as a results of a user dismissing the modal then we can determine what this value should be without this class variable. @@ -271,150 +210,30 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem } } - const nativeCurrency = chainId && NativeCurrency.onChain(chainId) - const permitInfo = getPermitInfo(request) - return ( - <> - - - - - - {!permitInfo && ( - - - - - - )} - - {methodCostsGas(request) ? ( - - ) : ( - - - {t('walletConnect.request.label.network')} - - - - )} - - - - - {!hasSufficientFunds && ( - - {t('walletConnect.request.error.insufficientFunds', { - currencySymbol: nativeCurrency?.symbol, - })} - - )} - - - {!netInfo.isInternetReachable ? ( - - } - textColor="$DEP_accentWarning" - title={t('walletConnect.request.error.network')} - /> - ) : ( - - )} - - - - - - - - - ) -} - -function WarningSection({ - request, - showUnsafeWarning, - isBlockedAddress, -}: { - request: WalletConnectRequest - showUnsafeWarning: boolean - isBlockedAddress: boolean -}): JSX.Element | null { - const colors = useSporeColors() - const { t } = useTranslation() - - if (!showUnsafeWarning && !isBlockedAddress) { - return null - } - - if (isBlockedAddress) { - return - } - - return ( - - => { + if (requiredForTransactions) { + await actionButtonTrigger() + } else { + await onConfirm() + } + }} + onReject={onReject}> + - - {isTransactionRequest(request) - ? t('walletConnect.request.warning.general.transaction') - : t('walletConnect.request.warning.general.message')} - - + ) } - -const requestMessageStyle: StyleProp = { - // need a fixed height here or else modal gets confused about total height - maxHeight: MAX_MODAL_MESSAGE_HEIGHT, - overflow: 'hidden', -} diff --git a/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModalContent.tsx b/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModalContent.tsx new file mode 100644 index 00000000000..fd0c1f7d5dd --- /dev/null +++ b/apps/mobile/src/components/WalletConnect/RequestModal/WalletConnectRequestModalContent.tsx @@ -0,0 +1,213 @@ +import { useBottomSheetInternal } from '@gorhom/bottom-sheet' +import { useNetInfo } from '@react-native-community/netinfo' +import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' + +import React, { PropsWithChildren } from 'react' +import { useTranslation } from 'react-i18next' +import { StyleProp, ViewStyle } from 'react-native' +import Animated, { useAnimatedStyle } from 'react-native-reanimated' +import { ClientDetails, PermitInfo } from 'src/components/WalletConnect/RequestModal/ClientDetails' +import { RequestDetails } from 'src/components/WalletConnect/RequestModal/RequestDetails' +import { + TransactionRequest, + WalletConnectRequest, + isTransactionRequest, +} from 'src/features/walletConnect/walletConnectSlice' +import { Flex, Text, useSporeColors } from 'ui/src' +import AlertTriangle from 'ui/src/assets/icons/alert-triangle.svg' +import { iconSizes } from 'ui/src/theme' +import { logger } from 'utilities/src/logger/logger' +import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard' +import { AccountDetails } from 'wallet/src/components/accounts/AccountDetails' +import { NetworkFee } from 'wallet/src/components/network/NetworkFee' +import { NetworkPill } from 'wallet/src/components/network/NetworkPill' +import { GasFeeResult } from 'wallet/src/features/gas/types' +import { BlockedAddressWarning } from 'wallet/src/features/trm/BlockedAddressWarning' +import { EthMethod, isPrimaryTypePermit } from 'wallet/src/features/walletConnect/types' +import { buildCurrencyId } from 'wallet/src/utils/currencyId' + +const MAX_MODAL_MESSAGE_HEIGHT = 200 + +const isPotentiallyUnsafe = (request: WalletConnectRequest): boolean => + request.type !== EthMethod.PersonalSign + +export const methodCostsGas = (request: WalletConnectRequest): request is TransactionRequest => + request.type === EthMethod.EthSendTransaction + +/** If the request is a permit then parse the relevant information otherwise return undefined. */ +const getPermitInfo = (request: WalletConnectRequest): PermitInfo | undefined => { + if (request.type !== EthMethod.SignTypedDataV4) { + return undefined + } + + try { + const message = JSON.parse(request.rawMessage) + if (!isPrimaryTypePermit(message)) { + return undefined + } + + const { domain, message: permitPayload } = message + const currencyId = buildCurrencyId(domain.chainId, domain.verifyingContract) + const amount = permitPayload.value + + return { currencyId, amount } + } catch (error) { + logger.error(error, { tags: { file: 'WalletConnectRequestModal', function: 'getPermitInfo' } }) + return undefined + } +} + +type WalletConnectRequestModalContentProps = { + gasFee: GasFeeResult + hasSufficientFunds: boolean + request: WalletConnectRequest + isBlocked: boolean +} + +export function WalletConnectRequestModalContent({ + request, + hasSufficientFunds, + isBlocked, + gasFee, +}: WalletConnectRequestModalContentProps): JSX.Element { + const chainId = request.chainId + const permitInfo = getPermitInfo(request) + const nativeCurrency = chainId && NativeCurrency.onChain(chainId) + + const { t } = useTranslation() + const colors = useSporeColors() + const { animatedFooterHeight } = useBottomSheetInternal() + + const netInfo = useNetInfo() + + const bottomSpacerStyle = useAnimatedStyle(() => ({ + height: animatedFooterHeight.value, + })) + + return ( + <> + + + + {!permitInfo && ( + + + + + + )} + + {methodCostsGas(request) ? ( + + ) : ( + + + {t('walletConnect.request.label.network')} + + + + )} + + + + + {!hasSufficientFunds && ( + + {t('walletConnect.request.error.insufficientFunds', { + currencySymbol: nativeCurrency?.symbol, + })} + + )} + + + + {!netInfo.isInternetReachable ? ( + + } + textColor="$DEP_accentWarning" + title={t('walletConnect.request.error.network')} + /> + ) : ( + + )} + + + + ) +} + +function SectionContainer({ + children, + style, +}: PropsWithChildren<{ style?: StyleProp }>): JSX.Element | null { + return children ? ( + + {children} + + ) : null +} + +function WarningSection({ + request, + showUnsafeWarning, + isBlockedAddress, +}: { + request: WalletConnectRequest + showUnsafeWarning: boolean + isBlockedAddress: boolean +}): JSX.Element | null { + const colors = useSporeColors() + const { t } = useTranslation() + + if (!showUnsafeWarning && !isBlockedAddress) { + return null + } + + if (isBlockedAddress) { + return + } + + return ( + + + + {isTransactionRequest(request) + ? t('walletConnect.request.warning.general.transaction') + : t('walletConnect.request.warning.general.message')} + + + ) +} + +const requestMessageStyle: StyleProp = { + // need a fixed height here or else modal gets confused about total height + maxHeight: MAX_MODAL_MESSAGE_HEIGHT, + overflow: 'hidden', +} diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionModal.tsx b/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionModal.tsx index 3734fe0d608..19c50208cc9 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionModal.tsx +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/PendingConnectionModal.tsx @@ -1,28 +1,30 @@ +import { useBottomSheetInternal } from '@gorhom/bottom-sheet' import { getSdkError } from '@walletconnect/utils' import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' +import Animated, { useAnimatedStyle } from 'react-native-reanimated' import { useAppDispatch, useAppSelector } from 'src/app/hooks' -import { LinkButton } from 'src/components/buttons/LinkButton' import { DappHeaderIcon } from 'src/components/WalletConnect/DappHeaderIcon' +import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay' import { NetworkLogos } from 'src/components/WalletConnect/NetworkLogos' import { PendingConnectionSwitchAccountModal } from 'src/components/WalletConnect/ScanSheet/PendingConnectionSwitchAccountModal' -import { truncateDappName } from 'src/components/WalletConnect/ScanSheet/util' +import { truncateQueryParams } from 'src/components/WalletConnect/ScanSheet/util' +import { LinkButton } from 'src/components/buttons/LinkButton' import { sendMobileAnalyticsEvent } from 'src/features/telemetry' import { MobileEventName } from 'src/features/telemetry/constants' +import { returnToPreviousApp } from 'src/features/walletConnect/WalletConnect' import { wcWeb3Wallet } from 'src/features/walletConnect/saga' import { selectDidOpenFromDeepLink } from 'src/features/walletConnect/selectors' import { getSessionNamespaces } from 'src/features/walletConnect/utils' -import { returnToPreviousApp } from 'src/features/walletConnect/WalletConnect' import { + WalletConnectPendingSession, addSession, removePendingSession, - WalletConnectPendingSession, } from 'src/features/walletConnect/walletConnectSlice' -import { AnimatedFlex, Button, Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src' +import { Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src' import { iconSizes } from 'ui/src/theme' import { ONE_SECOND_MS } from 'utilities/src/time/time' import { AccountDetails } from 'wallet/src/components/accounts/AccountDetails' -import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' import { ChainId } from 'wallet/src/constants/chains' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' @@ -33,9 +35,9 @@ import { } from 'wallet/src/features/wallet/hooks' import { setAccountAsActive } from 'wallet/src/features/wallet/slice' import { - WalletConnectEvent, WCEventType, WCRequestOutcome, + WalletConnectEvent, } from 'wallet/src/features/walletConnect/types' import { ElementName, ModalName } from 'wallet/src/telemetry/constants' @@ -53,55 +55,59 @@ enum PendingConnectionModalState { const SitePermissions = (): JSX.Element => { const { t } = useTranslation() - const normalInfoTextSize = 'body2' - const shortInfoTextSize = 'body3' + const infoTextSize = 'body3' return ( - - {t('walletConnect.permissions.title')} - - - - - {t('walletConnect.permissions.option.viewWalletAddress')} - - - - + borderWidth={1} + minHeight={44} + p="$spacing12"> + - {t('walletConnect.permissions.option.viewTokenBalances')} + color="$neutral2" + variant="body3"> + {t('walletConnect.permissions.title')} - - - - {t('walletConnect.permissions.option.transferAssets')} - + + + + + {t('walletConnect.permissions.option.viewWalletAddress')} + + + + + + {t('walletConnect.permissions.option.viewTokenBalances')} + + + + + + {t('walletConnect.permissions.option.transferAssets')} + + ) @@ -115,18 +121,21 @@ const NetworksRow = ({ chains }: { chains: ChainId[] }): JSX.Element => { row shrink alignItems="center" - backgroundColor="$surface2" + borderColor="$surface3" + borderEndWidth={1} + borderStartWidth={1} + height="$spacing48" justifyContent="space-between" - px="$spacing16" + px="$spacing12" py="$spacing12"> + variant="body3"> {t('walletConnect.permissions.networks')} - + ) } @@ -146,12 +155,14 @@ const SwitchAccountRow = ({ activeAddress, setModalState }: SwitchAccountProps): return ( @@ -159,6 +170,7 @@ const SwitchAccountRow = ({ activeAddress, setModalState }: SwitchAccountProps): address={activeAddress} allowFontScaling={false} chevron={accountIsSwitchable} + chevronColor="$neutral3" /> ) @@ -166,9 +178,9 @@ const SwitchAccountRow = ({ activeAddress, setModalState }: SwitchAccountProps): export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX.Element => { const { t } = useTranslation() - const colors = useSporeColors() - const activeAddress = useActiveAccountAddressWithThrow() + const dispatch = useAppDispatch() + const activeAddress = useActiveAccountAddressWithThrow() const activeAccount = useActiveAccountWithThrow() const didOpenFromDeepLink = useAppSelector(selectDidOpenFromDeepLink) @@ -238,65 +250,26 @@ export const PendingConnectionModal = ({ pendingSession, onClose }: Props): JSX. }, [activeAddress, dispatch, onClose, pendingSession, didOpenFromDeepLink] ) + const dappName = pendingSession.dapp.name || pendingSession.dapp.url || '' return ( - - - - - - {t('walletConnect.pending.title', { - dappName: truncateDappName(dappName), - })}{' '} - - - - - - - - - - - - - + <> + => onPressSettleConnection(true)} + onReject={(): Promise => onPressSettleConnection(false)}> + + + {modalState === PendingConnectionModalState.SwitchAccount && ( )} - + + ) +} + +type PendingConnectionModalContentProps = { + activeAddress: string + dappName: string + pendingSession: WalletConnectPendingSession + setModalState: (state: PendingConnectionModalState.SwitchAccount) => void +} + +function PendingConnectionModalContent({ + activeAddress, + dappName, + pendingSession, + setModalState, +}: PendingConnectionModalContentProps): JSX.Element { + const { t } = useTranslation() + const colors = useSporeColors() + + const { animatedFooterHeight } = useBottomSheetInternal() + + const bottomSpacerStyle = useAnimatedStyle(() => ({ + height: animatedFooterHeight.value, + })) + + return ( + <> + + + + {t('walletConnect.pending.title', { + dappName, + })}{' '} + + + + + + + + + + ) } diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/WalletConnectModal.tsx b/apps/mobile/src/components/WalletConnect/ScanSheet/WalletConnectModal.tsx index da0a5c9c151..f6b7d3fea67 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/WalletConnectModal.tsx +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/WalletConnectModal.tsx @@ -14,10 +14,9 @@ import { UWULINK_PREFIX, getSupportedURI, isAllowedUwULinkRequest, - parseScantasticParams, } from 'src/components/WalletConnect/ScanSheet/util' import { BackButtonView } from 'src/components/layout/BackButtonView' -import { closeAllModals, openModal } from 'src/features/modals/modalSlice' +import { openDeepLink } from 'src/features/deepLinking/handleDeepLinkSaga' import { useWalletConnect } from 'src/features/walletConnect/useWalletConnect' import { pairWithWalletConnectURI } from 'src/features/walletConnect/utils' import { addRequest } from 'src/features/walletConnect/walletConnectSlice' @@ -136,36 +135,9 @@ export function WalletConnectModal({ } if (supportedURI.type === URIType.Scantastic) { - const params = parseScantasticParams(supportedURI.value) - - if (!params) { - setShouldFreezeCamera(true) - Alert.alert( - t('walletConnect.error.scantastic.title'), - t('walletConnect.error.scantastic.message'), - [ - { - text: t('common.button.ok'), - onPress: (): void => { - setShouldFreezeCamera(false) - }, - }, - ] - ) - return - } - setShouldFreezeCamera(true) - dispatch(closeAllModals()) - dispatch( - openModal({ - name: ModalName.Scantastic, - initialState: { - params, - }, - }) - ) - + dispatch(openDeepLink({ url: uri, coldStart: false })) + onClose() return } diff --git a/apps/mobile/src/components/WalletConnect/ScanSheet/util.ts b/apps/mobile/src/components/WalletConnect/ScanSheet/util.ts index 1e89586923a..d9ece5fd2fb 100644 --- a/apps/mobile/src/components/WalletConnect/ScanSheet/util.ts +++ b/apps/mobile/src/components/WalletConnect/ScanSheet/util.ts @@ -2,9 +2,10 @@ import { parseUri } from '@walletconnect/utils' import { parseEther } from 'ethers/lib/utils' import { UNISWAP_URL_SCHEME, + UNISWAP_URL_SCHEME_SCANTASTIC, UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM, UNISWAP_WALLETCONNECT_URL, -} from 'src/features/deepLinking/handleDeepLinkSaga' +} from 'src/features/deepLinking/constants' import { logger } from 'utilities/src/logger/logger' import { ScantasticParams, ScantasticParamsSchema } from 'wallet/src/features/scantastic/types' import { UwULinkRequest } from 'wallet/src/features/walletConnect/types' @@ -36,12 +37,11 @@ const UWULINK_MAX_TXN_VALUE = '0.001' const EASTER_EGG_QR_CODE = 'DO_NOT_SCAN_OR_ELSE_YOU_WILL_GO_TO_MOBILE_TEAM_JAIL' export const CUSTOM_UNI_QR_CODE_PREFIX = 'hello_uniwallet:' export const UWULINK_PREFIX = 'uwulink' -const MAX_DAPP_NAME_LENGTH = 60 -export function truncateDappName(name: string): string { - return name && name.length > MAX_DAPP_NAME_LENGTH - ? `${name.slice(0, MAX_DAPP_NAME_LENGTH)}...` - : name +export const truncateQueryParams = (url: string): string => { + // In fact, the first element will be always returned below. url is + // added as a fallback just to satisfy TypeScript. + return url.split('?')[0] ?? url } export async function getSupportedURI( @@ -62,9 +62,9 @@ export async function getSupportedURI( return { type: URIType.Address, value: maybeMetamaskAddress } } - const maybeScantasticAddress = getScantasticAddress(uri) - if (enabledFeatureFlags?.isScantasticEnabled && maybeScantasticAddress) { - return { type: URIType.Scantastic, value: maybeScantasticAddress } + const maybeScantasticQueryParams = getScantasticQueryParams(uri) + if (enabledFeatureFlags?.isScantasticEnabled && maybeScantasticQueryParams) { + return { type: URIType.Scantastic, value: maybeScantasticQueryParams } } // The check for custom prefixes must be before the parseUri version 2 check because @@ -153,13 +153,13 @@ function getMetamaskAddress(uri: string): Nullable { return getValidAddress(uriParts[1], /*withChecksum=*/ true, /*log=*/ false) } -// format is scantastic:// -function getScantasticAddress(uri: string): Nullable { - if (!uri.startsWith('scantastic://')) { +// format is uniswap://scantastic? +export function getScantasticQueryParams(uri: string): Nullable { + if (!uri.startsWith(UNISWAP_URL_SCHEME_SCANTASTIC)) { return null } - const uriParts = uri.split('://') + const uriParts = uri.split('://scantastic?') if (uriParts.length < 2) { return null diff --git a/apps/mobile/src/components/accounts/AccountHeader.tsx b/apps/mobile/src/components/accounts/AccountHeader.tsx index 6c31de3c397..929810de28e 100644 --- a/apps/mobile/src/components/accounts/AccountHeader.tsx +++ b/apps/mobile/src/components/accounts/AccountHeader.tsx @@ -69,7 +69,13 @@ export function AccountHeader(): JSX.Element { const iconSize = 52 return ( - + {activeAddress && ( 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 8cfa773babb..1cf4b91ed6c 100644 --- a/apps/mobile/src/components/accounts/__snapshots__/AccountHeader.test.tsx.snap +++ b/apps/mobile/src/components/accounts/__snapshots__/AccountHeader.test.tsx.snap @@ -7,6 +7,8 @@ exports[`AccountHeader renders correctly 1`] = ` "flexDirection": "column", "gap": 12, "overflow": "scroll", + "paddingLeft": 12, + "paddingRight": 12, "paddingTop": 8, "width": "100%", } @@ -395,7 +397,7 @@ exports[`AccountHeader renders correctly 1`] = ` strokeWidth="8" > void +}): JSX.Element { + const dispatch = useAppDispatch() + const { t } = useTranslation() + const { fullWidth } = useDeviceDimensions() + const colors = useSporeColors() + const isDarkMode = useIsDarkMode() + + const imageWidth = IMAGE_SCREEN_WIDTH_PROPORTION * fullWidth + const imageHeight = imageWidth / IMAGE_ASPECT_RATIO + + const onPressClaimNow = (): void => { + Keyboard.dismiss() + sendWalletAnalyticsEvent(ExtensionOnboardingEventName.PromoBannerActionTaken, { + action: 'join', + }) + onShowExtensionPromoModal() + } + + const onPressMaybeLater = (): void => { + sendWalletAnalyticsEvent(ExtensionOnboardingEventName.PromoBannerActionTaken, { + action: 'dismiss', + }) + dispatch(setExtensionOnboardingState(ExtensionOnboardingState.Completed)) + } + + const baseButtonStyle: StyleProp = { + borderRadius: borderRadii.rounded12, + justifyContent: 'center', + height: iconSizes.icon36, + paddingVertical: spacing.spacing8, + paddingHorizontal: spacing.spacing12, + } + + return ( + + + + + {t('home.banner.extension.title')} + + + {t('home.banner.extension.message')} + + + + + + {t('home.banner.extension.confirm')} + + + + + {t('common.button.later')} + + + + + + + + + ) +} diff --git a/apps/mobile/src/components/buttons/LinkButton.tsx b/apps/mobile/src/components/buttons/LinkButton.tsx index 1ab18c7ec67..858d9f4ccdb 100644 --- a/apps/mobile/src/components/buttons/LinkButton.tsx +++ b/apps/mobile/src/components/buttons/LinkButton.tsx @@ -11,6 +11,7 @@ interface LinkButtonProps extends Omit { isSafeUri?: boolean color?: string iconColor?: string + showIcon?: boolean size?: number textVariant?: TextVariantTokens } @@ -21,6 +22,7 @@ export function LinkButton({ textVariant, color, iconColor, + showIcon = true, openExternalBrowser = false, isSafeUri = false, size = iconSizes.icon20, @@ -40,15 +42,17 @@ export function LinkButton({ onPress={(): Promise => openUri(url, openExternalBrowser, isSafeUri)} {...rest}> - + {label} - + {showIcon && ( + + )} ) diff --git a/apps/mobile/src/components/buttons/__snapshots__/LinkButton.test.tsx.snap b/apps/mobile/src/components/buttons/__snapshots__/LinkButton.test.tsx.snap index 66ed118d42d..6d066d10d16 100644 --- a/apps/mobile/src/components/buttons/__snapshots__/LinkButton.test.tsx.snap +++ b/apps/mobile/src/components/buttons/__snapshots__/LinkButton.test.tsx.snap @@ -41,6 +41,7 @@ exports[`LinkButton renders without error 1`] = ` style={ { "color": "#222222", + "flexShrink": 1, "fontFamily": "Basel-Book", } } diff --git a/apps/mobile/src/components/explore/FavoriteHeaderRow.test.tsx b/apps/mobile/src/components/explore/FavoriteHeaderRow.test.tsx new file mode 100644 index 00000000000..178578f4d19 --- /dev/null +++ b/apps/mobile/src/components/explore/FavoriteHeaderRow.test.tsx @@ -0,0 +1,80 @@ +import { fireEvent, render } from 'src/test/test-utils' +import { ON_PRESS_EVENT_PAYLOAD } from 'wallet/src/test/fixtures' +import { FavoriteHeaderRow } from './FavoriteHeaderRow' + +const defaultProps = { + title: 'Title', + editingTitle: 'Editing Title', + isEditing: false, + onPress: jest.fn(), +} + +describe(FavoriteHeaderRow, () => { + describe('when not editing', () => { + it('renders without error', () => { + const tree = render() + + expect(tree.toJSON()).toMatchSnapshot() + }) + + it('renders title', () => { + const { queryByText } = render() + + expect(queryByText(defaultProps.title)).toBeTruthy() + expect(queryByText(defaultProps.editingTitle)).toBeFalsy() + }) + + it('renders favorite button', () => { + const { queryByTestId } = render() + + const favoriteButton = queryByTestId('favorite-header-row/favorite-button') + const doneButton = queryByTestId('favorite-header-row/done-button') + + expect(favoriteButton).toBeTruthy() + expect(doneButton).toBeFalsy() + }) + + it('calls onPress when favorite icon pressed', () => { + const { getByTestId } = render() + + const favoriteButton = getByTestId('favorite-header-row/favorite-button') + fireEvent.press(favoriteButton, ON_PRESS_EVENT_PAYLOAD) + + expect(defaultProps.onPress).toHaveBeenCalledTimes(1) + }) + }) + + describe('when editing', () => { + it('renders without error', () => { + const tree = render() + + expect(tree.toJSON()).toMatchSnapshot() + }) + + it('renders editingTitle', () => { + const { queryByText } = render() + + expect(queryByText(defaultProps.editingTitle)).toBeTruthy() + expect(queryByText(defaultProps.title)).toBeFalsy() + }) + + it('renders done button', () => { + const { queryByTestId } = render() + + const favoriteButton = queryByTestId('favorite-header-row/favorite-button') + const doneButton = queryByTestId('favorite-header-row/done-button') + + expect(favoriteButton).toBeFalsy() + expect(doneButton).toBeTruthy() + }) + + it('calls onPress when done button pressed', () => { + const { getByTestId } = render() + + const doneButton = getByTestId('favorite-header-row/done-button') + fireEvent.press(doneButton, ON_PRESS_EVENT_PAYLOAD) + + expect(defaultProps.onPress).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/apps/mobile/src/components/explore/FavoriteHeaderRow.tsx b/apps/mobile/src/components/explore/FavoriteHeaderRow.tsx index db3fc6e781a..6f5b57fc497 100644 --- a/apps/mobile/src/components/explore/FavoriteHeaderRow.tsx +++ b/apps/mobile/src/components/explore/FavoriteHeaderRow.tsx @@ -2,7 +2,6 @@ import { default as React } from 'react' import { useTranslation } from 'react-i18next' import { Flex, Icons, Text, TouchableArea } from 'ui/src' import { iconSizes } from 'ui/src/theme' -import { ElementName } from 'wallet/src/telemetry/constants' export function FavoriteHeaderRow({ title, @@ -28,7 +27,11 @@ export function FavoriteHeaderRow({ {isEditing ? editingTitle : title} {!isEditing ? ( - + ) : ( - + {t('common.button.done')} diff --git a/apps/mobile/src/components/explore/FavoriteTokenCard.test.tsx b/apps/mobile/src/components/explore/FavoriteTokenCard.test.tsx new file mode 100644 index 00000000000..f3f30698d26 --- /dev/null +++ b/apps/mobile/src/components/explore/FavoriteTokenCard.test.tsx @@ -0,0 +1,153 @@ +import { makeMutable } from 'react-native-reanimated' +import configureMockStore from 'redux-mock-store' +import { act, cleanup, fireEvent, render, waitFor } from 'src/test/test-utils' +import { FiatCurrency } from 'wallet/src/features/fiatCurrency/constants' +import { Language } from 'wallet/src/features/language/constants' +import { + ON_PRESS_EVENT_PAYLOAD, + SAMPLE_CURRENCY_ID_1, + amount, + ethToken, + tokenProject, + tokenProjectMarket, +} from 'wallet/src/test/fixtures' +import { queryResolvers } from 'wallet/src/test/utils' +import { getSymbolDisplayText } from 'wallet/src/utils/currency' +import FavoriteTokenCard, { FavoriteTokenCardProps } from './FavoriteTokenCard' + +const mockedNavigation = { + navigate: jest.fn(), +} + +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native') + return { + ...actualNav, + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + useNavigation: () => mockedNavigation, + } +}) + +const mockStore = configureMockStore() + +const favoriteToken = ethToken({ + project: tokenProject({ + markets: [ + tokenProjectMarket({ + price: amount({ value: 12345.67 }), + pricePercentChange24h: amount({ value: 4.56 }), + }), + ], + }), +}) + +const touchableId = `token-box-${favoriteToken.symbol}` + +const defaultProps: FavoriteTokenCardProps = { + currencyId: SAMPLE_CURRENCY_ID_1, + isTouched: makeMutable(false), + dragActivationProgress: makeMutable(0), + setIsEditing: jest.fn(), + isEditing: false, +} + +const { resolvers } = queryResolvers({ + token: () => favoriteToken, +}) + +describe('FavoriteTokenCard', () => { + it('renders without error', async () => { + const tree = render() + + expect(tree).toMatchSnapshot() + cleanup() + }) + + describe('when token data is being fetched', () => { + it('renders loader', async () => { + const { queryByTestId } = render(, { resolvers }) + + const loader = queryByTestId('loader/favorite') + + // loading + expect(loader).toBeTruthy() + + // loading finished + await waitFor(() => { + expect(queryByTestId(touchableId)).toBeTruthy() + }) + }) + }) + + describe('when token data is available', () => { + const cases = [ + { test: 'symbol', value: getSymbolDisplayText(favoriteToken.symbol)! }, + { test: 'price', value: '$12,345.67' }, + { test: 'relative price change', value: '4.56%' }, + ] + + it.each(cases)('renders correct $test', async ({ value }) => { + const { queryByText } = render(, { resolvers }) + + await waitFor(() => { + expect(queryByText(value)).toBeTruthy() + }) + }) + + it('navigates to the token details screen when pressed', async () => { + const { findByTestId } = render(, { resolvers }) + + const touchable = await findByTestId(`token-box-${favoriteToken.symbol}`) + await act(() => { + fireEvent.press(touchable, ON_PRESS_EVENT_PAYLOAD) + }) + + expect(mockedNavigation.navigate).toHaveBeenCalledTimes(1) + expect(mockedNavigation.navigate).toHaveBeenCalledWith('TokenDetails', { + currencyId: SAMPLE_CURRENCY_ID_1, // passed in component props + }) + }) + + it('does not show remove button when not in edit mode', async () => { + const { findByTestId } = render(, { resolvers }) + + const removeButton = await findByTestId('explore/remove-button') + + expect(removeButton).toHaveAnimatedStyle({ opacity: 0 }) + }) + }) + + describe('edit mode', () => { + it('shows remove button when in edit mode', async () => { + const { findByTestId } = render(, { + resolvers, + }) + + const removeButton = await findByTestId('explore/remove-button') + + expect(removeButton).toHaveAnimatedStyle({ opacity: 1 }) + }) + + it('dispatches removeFavoriteToken action when remove button is pressed', async () => { + const store = mockStore({ + favorites: { tokens: [] }, + fiatCurrencySettings: { currentCurrency: FiatCurrency.UnitedStatesDollar }, + languageSettings: { currentLanguage: Language.English }, + }) + const { findByTestId } = render(, { + resolvers, + store, + }) + + const removeButton = await findByTestId('explore/remove-button') + await act(() => { + fireEvent.press(removeButton, ON_PRESS_EVENT_PAYLOAD) + }) + + const actions = store.getActions() + expect(actions).toEqual([ + { type: 'favorites/removeFavoriteToken', payload: { currencyId: SAMPLE_CURRENCY_ID_1 } }, + ]) + }) + }) +}) diff --git a/apps/mobile/src/components/explore/FavoriteTokenCard.tsx b/apps/mobile/src/components/explore/FavoriteTokenCard.tsx index 9c949f5b49e..172c3df03dd 100644 --- a/apps/mobile/src/components/explore/FavoriteTokenCard.tsx +++ b/apps/mobile/src/components/explore/FavoriteTokenCard.tsx @@ -28,7 +28,7 @@ import { getSymbolDisplayText } from 'wallet/src/utils/currency' export const FAVORITE_TOKEN_CARD_LOADER_HEIGHT = 114 -type FavoriteTokenCardProps = { +export type FavoriteTokenCardProps = { currencyId: string isEditing?: boolean isTouched: SharedValue diff --git a/apps/mobile/src/components/explore/FavoriteWalletCard.test.tsx b/apps/mobile/src/components/explore/FavoriteWalletCard.test.tsx new file mode 100644 index 00000000000..4b1b14a3aa0 --- /dev/null +++ b/apps/mobile/src/components/explore/FavoriteWalletCard.test.tsx @@ -0,0 +1,153 @@ +import { makeMutable } from 'react-native-reanimated' +import configureMockStore from 'redux-mock-store' +import { Screens } from 'src/screens/Screens' +import { preloadedMobileState } from 'src/test/fixtures' +import { fireEvent, render } from 'src/test/test-utils' +import * as ensHooks from 'wallet/src/features/ens/api' +import * as unitagHooks from 'wallet/src/features/unitags/hooks' +import { + ON_PRESS_EVENT_PAYLOAD, + SAMPLE_SEED_ADDRESS_1, + preloadedWalletState, + signerMnemonicAccount, +} from 'wallet/src/test/fixtures' +import { sanitizeAddressText, shortenAddress } from 'wallet/src/utils/addresses' +import FavoriteWalletCard, { FavoriteWalletCardProps } from './FavoriteWalletCard' + +const mockedNavigation = { + navigate: jest.fn(), +} + +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native') + return { + ...actualNav, + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + useNavigation: () => mockedNavigation, + } +}) + +const mockStore = configureMockStore() + +const defaultProps: FavoriteWalletCardProps = { + address: SAMPLE_SEED_ADDRESS_1, + isTouched: makeMutable(false), + isEditing: false, + dragActivationProgress: makeMutable(0), + setIsEditing: jest.fn(), +} + +describe('FavoriteWalletCard', () => { + it('renders without error', () => { + const tree = render() + + expect(tree).toMatchSnapshot() + }) + + describe('displayName', () => { + afterEach(() => { + jest.restoreAllMocks() + }) + + it('renders unitag name if available', () => { + jest.spyOn(unitagHooks, 'useUnitagByAddress').mockReturnValue({ + unitag: { username: 'unitagname' }, + loading: false, + }) + + const { queryByText } = render() + + expect(queryByText('unitagname')).toBeTruthy() + }) + + it('renders ens name if available', () => { + jest.spyOn(ensHooks, 'useENSName').mockReturnValue({ + data: 'ensname.eth', + loading: false, + error: undefined, + }) + + const { queryByText } = render() + + expect(queryByText('ensname.eth')).toBeTruthy() + }) + + it('renders local name if wallet name is set locally', () => { + const { queryByText } = render(, { + preloadedState: preloadedMobileState({ + wallet: preloadedWalletState({ + account: signerMnemonicAccount({ + address: defaultProps.address, + name: 'Local account', + }), + }), + }), + }) + + expect(queryByText('Local account')).toBeTruthy() + }) + + it('renders wallet address in other cases', () => { + const { queryByText } = render() + + const displayedAddress = sanitizeAddressText(shortenAddress(defaultProps.address))! + + expect(queryByText(displayedAddress)).toBeTruthy() + }) + }) + + describe('when not editing', () => { + it('navigates to the wallet details screen when pressed', () => { + const { getByTestId } = render() + + const touchable = getByTestId('favorite-wallet-card') + fireEvent.press(touchable, ON_PRESS_EVENT_PAYLOAD) + + expect(mockedNavigation.navigate).toHaveBeenCalledWith(Screens.ExternalProfile, { + address: defaultProps.address, + }) + }) + + it('does not display the remove button', () => { + const { getByTestId } = render() + + const removeButton = getByTestId('explore/remove-button') + + expect(removeButton).toHaveAnimatedStyle({ opacity: 0 }) + }) + }) + + describe('when editing', () => { + it('displays the remove button', () => { + const { getByTestId } = render() + + const removeButton = getByTestId('explore/remove-button') + + expect(removeButton).toHaveAnimatedStyle({ opacity: 1 }) + }) + + it('dispatches removeWatchedAddress when remove button is pressed', () => { + const store = mockStore({ + favorites: { tokens: [] }, + wallet: { + accounts: { + [defaultProps.address]: signerMnemonicAccount({ address: defaultProps.address }), + }, + }, + }) + const { getByTestId } = render(, { + store, + }) + + const removeButton = getByTestId('explore/remove-button') + fireEvent.press(removeButton, ON_PRESS_EVENT_PAYLOAD) + + expect(store.getActions()).toEqual([ + { + type: 'favorites/removeWatchedAddress', + payload: { address: defaultProps.address }, + }, + ]) + }) + }) +}) diff --git a/apps/mobile/src/components/explore/FavoriteWalletCard.tsx b/apps/mobile/src/components/explore/FavoriteWalletCard.tsx index f1a1becdd55..04237b64137 100644 --- a/apps/mobile/src/components/explore/FavoriteWalletCard.tsx +++ b/apps/mobile/src/components/explore/FavoriteWalletCard.tsx @@ -17,7 +17,7 @@ import { removeWatchedAddress } from 'wallet/src/features/favorites/slice' import { useAvatar, useDisplayName } from 'wallet/src/features/wallet/hooks' import { DisplayNameType } from 'wallet/src/features/wallet/types' -type FavoriteWalletCardProps = { +export type FavoriteWalletCardProps = { address: Address isEditing?: boolean isTouched: SharedValue @@ -84,6 +84,7 @@ function FavoriteWalletCard({ disabled={isEditing} hapticStyle={ImpactFeedbackStyle.Light} m="$spacing4" + testID="favorite-wallet-card" onLongPress={disableOnPress} onPress={(): void => { navigate(address) diff --git a/apps/mobile/src/components/explore/RemoveButton.test.tsx b/apps/mobile/src/components/explore/RemoveButton.test.tsx new file mode 100644 index 00000000000..4c9134c5bb6 --- /dev/null +++ b/apps/mobile/src/components/explore/RemoveButton.test.tsx @@ -0,0 +1,39 @@ +import { fireEvent, render } from 'src/test/test-utils' +import { ON_PRESS_EVENT_PAYLOAD } from 'wallet/src/test/fixtures' +import RemoveButton from './RemoveButton' + +describe(RemoveButton, () => { + it('renders without error', () => { + const tree = render() + + expect(tree.toJSON()).toMatchSnapshot() + }) + + it('calls onPress when pressed', () => { + const onPress = jest.fn() + const { getByTestId } = render() + + const button = getByTestId('explore/remove-button') + fireEvent.press(button, ON_PRESS_EVENT_PAYLOAD) + + expect(onPress).toHaveBeenCalledTimes(1) + }) + + describe('visibility', () => { + it('renders with opacity 1 when visible', () => { + const { getByTestId } = render() + + const button = getByTestId('explore/remove-button') + + expect(button).toHaveAnimatedStyle({ opacity: 1 }) + }) + + it('renders with opacity 0 when not visible', () => { + const { getByTestId } = render() + + const button = getByTestId('explore/remove-button') + + expect(button).toHaveAnimatedStyle({ opacity: 0 }) + }) + }) +}) diff --git a/apps/mobile/src/components/explore/RemoveButton.tsx b/apps/mobile/src/components/explore/RemoveButton.tsx index 5d0d24c30c4..a42ada56f75 100644 --- a/apps/mobile/src/components/explore/RemoveButton.tsx +++ b/apps/mobile/src/components/explore/RemoveButton.tsx @@ -20,6 +20,7 @@ export default function RemoveButton({ visible = true, ...rest }: RemoveButtonPr height={imageSizes.image24} justifyContent="center" style={animatedVisibilityStyle} + testID="explore/remove-button" width={imageSizes.image24} zIndex="$tooltip" {...rest}> diff --git a/apps/mobile/src/components/explore/SortButton.test.tsx b/apps/mobile/src/components/explore/SortButton.test.tsx new file mode 100644 index 00000000000..d8440038265 --- /dev/null +++ b/apps/mobile/src/components/explore/SortButton.test.tsx @@ -0,0 +1,83 @@ +import ContextMenu from 'react-native-context-menu-view' +import { render } from 'src/test/test-utils' +import { TokenSortableField } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { ClientTokensOrderBy } from 'wallet/src/features/wallet/types' +import { SortButton } from './SortButton' + +jest.mock('react-native-context-menu-view', () => { + // Use the actual implementation of `react-native-context-menu-view` as the mock implementation + // (we use mock just to get the props of the component in test) + return jest.fn(jest.requireActual('react-native-context-menu-view').default) +}) + +describe('SortButton', () => { + it('renders without error', () => { + const tree = render() + + expect(tree).toMatchSnapshot() + }) + + const cases = [ + { test: 'volume', orderBy: TokenSortableField.Volume, label: 'Volume' }, + { test: 'total value locked', orderBy: TokenSortableField.TotalValueLocked, label: 'TVL' }, + { test: 'market cap', orderBy: TokenSortableField.MarketCap, label: 'Market cap' }, + { + test: 'price increase', + orderBy: ClientTokensOrderBy.PriceChangePercentage24hDesc, + label: 'Price increase', + }, + { + test: 'price decrease', + orderBy: ClientTokensOrderBy.PriceChangePercentage24hAsc, + label: 'Price decrease', + }, + ] + + describe.each(cases)('when ordering by $test', ({ orderBy, label }) => { + it(`renders ${label} as the selected option`, () => { + const { queryByText } = render() + const selectedOption = queryByText(label) + + expect(selectedOption).toBeTruthy() + }) + + it(`returns correct context menu actions with checmark near the ${label} option`, () => { + jest.clearAllMocks() + render() + + expect((ContextMenu as unknown as jest.Mock).mock.calls[0][0]).toEqual( + expect.objectContaining({ + actions: [ + { + title: 'Uniswap volume (24H)', + systemIcon: orderBy === TokenSortableField.Volume ? 'checkmark' : '', + orderBy: TokenSortableField.Volume, + }, + { + title: 'Uniswap TVL', + systemIcon: orderBy === TokenSortableField.TotalValueLocked ? 'checkmark' : '', + orderBy: TokenSortableField.TotalValueLocked, + }, + { + title: 'Market cap', + systemIcon: orderBy === TokenSortableField.MarketCap ? 'checkmark' : '', + orderBy: TokenSortableField.MarketCap, + }, + { + title: 'Price increase (24H)', + systemIcon: + orderBy === ClientTokensOrderBy.PriceChangePercentage24hDesc ? 'checkmark' : '', + orderBy: ClientTokensOrderBy.PriceChangePercentage24hDesc, + }, + { + title: 'Price decrease (24H)', + systemIcon: + orderBy === ClientTokensOrderBy.PriceChangePercentage24hAsc ? 'checkmark' : '', + orderBy: ClientTokensOrderBy.PriceChangePercentage24hAsc, + }, + ], + }) + ) + }) + }) +}) diff --git a/apps/mobile/src/components/explore/TokenItem.test.tsx b/apps/mobile/src/components/explore/TokenItem.test.tsx new file mode 100644 index 00000000000..dd22ba2f6a7 --- /dev/null +++ b/apps/mobile/src/components/explore/TokenItem.test.tsx @@ -0,0 +1,123 @@ +import * as tokenDetailsHooks from 'src/components/TokenDetails/hooks' +import { TOKEN_ITEM_DATA, tokenItemData } from 'src/test/fixtures' +import { fireEvent, render, within } from 'src/test/test-utils' +import { TokenMetadataDisplayType } from 'wallet/src/features/wallet/types' +import { ON_PRESS_EVENT_PAYLOAD } from 'wallet/src/test/fixtures' +import { buildCurrencyId } from 'wallet/src/utils/currencyId' +import { TokenItem } from './TokenItem' +import * as exploreHooks from './hooks' + +describe('TokenItem', () => { + const mockedTokenDetailsNavigation = { + navigate: jest.fn(), + navigateWithPop: jest.fn(), + preload: jest.fn(), + } + + beforeAll(() => { + jest + .spyOn(tokenDetailsHooks, 'useTokenDetailsNavigation') + .mockReturnValue(mockedTokenDetailsNavigation) + jest.spyOn(exploreHooks, 'useExploreTokenContextMenu').mockReturnValue({ + menuActions: [], + onContextMenuPress: jest.fn(), + }) + }) + + it('renders without error', () => { + const tree = render() + + expect(tree).toMatchSnapshot() + }) + + it('renders correct token number based on index', () => { + const data = tokenItemData() + const { queryByText } = render() + + expect(queryByText('2')).toBeTruthy() + }) + + it('renders proper token name', () => { + const data = tokenItemData() + const { queryByText } = render() + + expect(queryByText(data.name)).toBeTruthy() + }) + + it('navigates to the token details screen when pressed', () => { + const data = tokenItemData() + const { getByTestId } = render() + + fireEvent.press(getByTestId(`token-item-${data.name}`), ON_PRESS_EVENT_PAYLOAD) + + expect(mockedTokenDetailsNavigation.navigate).toHaveBeenCalledWith( + buildCurrencyId(data.chainId, data.address) + ) + }) + + describe('token price', () => { + it('renders token price if it is provided', () => { + const data = tokenItemData({ price: 123.45 }) + const { getByTestId } = render() + + const tokenPrice = getByTestId('token-item/price') + + expect(within(tokenPrice).queryByText('$123.45')).toBeTruthy() + expect(within(tokenPrice).queryByText('-')).toBeFalsy() + }) + + it('renders price placeholder if token price is not provided', () => { + const data = tokenItemData({ price: undefined }) + const { getByTestId } = render() + + const tokenPrice = getByTestId('token-item/price') + + expect(within(tokenPrice).queryByText('-')).toBeTruthy() + }) + }) + + describe('token price change', () => { + it('renders token price change if it is provided', () => { + const data = tokenItemData({ pricePercentChange24h: 12.34 }) + const { getByTestId } = render() + + const relativeChange = getByTestId('relative-change') + + expect(within(relativeChange).queryByText('12.34%')).toBeTruthy() + }) + + it('renders price change placeholder if token price change is not provided', () => { + const data = tokenItemData({ pricePercentChange24h: undefined }) + const { getByTestId } = render() + + const relativeChange = getByTestId('relative-change') + + expect(within(relativeChange).queryByText('-')).toBeTruthy() + }) + }) + + describe('metadata subtitle', () => { + const data = tokenItemData({ + marketCap: 123.45, + volume24h: 234.56, + totalValueLocked: 345.67, + }) + + const cases = [ + { test: 'market cap', type: TokenMetadataDisplayType.MarketCap, expected: '$123.45 MCap' }, + { test: 'volume', type: TokenMetadataDisplayType.Volume, expected: '$234.56 Vol' }, + { test: 'total value locked', type: TokenMetadataDisplayType.TVL, expected: '$345.67 TVL' }, + { test: 'symbol', type: TokenMetadataDisplayType.Symbol, expected: data.symbol }, + ] + + it.each(cases)('renders $test metadata subtitle', ({ type, expected }) => { + const { getByTestId } = render( + + ) + + const metadataSubtitle = getByTestId('token-item/metadata-subtitle') + + expect(within(metadataSubtitle).queryByText(expected)).toBeTruthy() + }) + }) +}) diff --git a/apps/mobile/src/components/explore/TokenItem.tsx b/apps/mobile/src/components/explore/TokenItem.tsx index 694b9f73365..37832d6f487 100644 --- a/apps/mobile/src/components/explore/TokenItem.tsx +++ b/apps/mobile/src/components/explore/TokenItem.tsx @@ -125,13 +125,17 @@ export const TokenItem = memo(function _TokenItem({ {name} - + {getMetadataSubtitle()} - + {convertFiatAmountFormatted(price, NumberType.FiatTokenPrice)} diff --git a/apps/mobile/src/components/explore/__snapshots__/FavoriteHeaderRow.test.tsx.snap b/apps/mobile/src/components/explore/__snapshots__/FavoriteHeaderRow.test.tsx.snap new file mode 100644 index 00000000000..d832a972764 --- /dev/null +++ b/apps/mobile/src/components/explore/__snapshots__/FavoriteHeaderRow.test.tsx.snap @@ -0,0 +1,268 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FavoriteHeaderRow when editing renders without error 1`] = ` + + + Editing Title + + + + Done + + + +`; + +exports[`FavoriteHeaderRow when not editing renders without error 1`] = ` + + + Title + + + + + + + + + + + +`; diff --git a/apps/mobile/src/components/explore/__snapshots__/FavoriteTokenCard.test.tsx.snap b/apps/mobile/src/components/explore/__snapshots__/FavoriteTokenCard.test.tsx.snap new file mode 100644 index 00000000000..83956586024 --- /dev/null +++ b/apps/mobile/src/components/explore/__snapshots__/FavoriteTokenCard.test.tsx.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FavoriteTokenCard renders without error 1`] = ` + + + + + +`; diff --git a/apps/mobile/src/components/explore/__snapshots__/FavoriteWalletCard.test.tsx.snap b/apps/mobile/src/components/explore/__snapshots__/FavoriteWalletCard.test.tsx.snap new file mode 100644 index 00000000000..91a8d122d6f --- /dev/null +++ b/apps/mobile/src/components/explore/__snapshots__/FavoriteWalletCard.test.tsx.snap @@ -0,0 +1,402 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FavoriteWalletCard renders without error 1`] = ` + + + + + + + + + + + + + } + > + + + + + + + + + + + } + > + + + + + + + + + + 0x​82D5...3Fa6 + + + + + + + + + + + +`; diff --git a/apps/mobile/src/components/explore/__snapshots__/RemoveButton.test.tsx.snap b/apps/mobile/src/components/explore/__snapshots__/RemoveButton.test.tsx.snap new file mode 100644 index 00000000000..62b2a54ce94 --- /dev/null +++ b/apps/mobile/src/components/explore/__snapshots__/RemoveButton.test.tsx.snap @@ -0,0 +1,63 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`RemoveButton renders without error 1`] = ` + + + +`; diff --git a/apps/mobile/src/components/explore/__snapshots__/SortButton.test.tsx.snap b/apps/mobile/src/components/explore/__snapshots__/SortButton.test.tsx.snap new file mode 100644 index 00000000000..39854e39284 --- /dev/null +++ b/apps/mobile/src/components/explore/__snapshots__/SortButton.test.tsx.snap @@ -0,0 +1,198 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SortButton renders without error 1`] = ` + + + + + Volume + + + + + + + + + + + +`; diff --git a/apps/mobile/src/components/explore/__snapshots__/TokenItem.test.tsx.snap b/apps/mobile/src/components/explore/__snapshots__/TokenItem.test.tsx.snap new file mode 100644 index 00000000000..6f940c3e1e0 --- /dev/null +++ b/apps/mobile/src/components/explore/__snapshots__/TokenItem.test.tsx.snap @@ -0,0 +1,250 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`TokenItem renders without error 1`] = ` + + + + + + + 1 + + + + + + + + + cum + + + + + + + + - + + + + + - + + + + + + + + + +`; diff --git a/apps/mobile/src/components/explore/hooks.test.ts b/apps/mobile/src/components/explore/hooks.test.ts index 46153e77d2c..bcbafc3d192 100644 --- a/apps/mobile/src/components/explore/hooks.test.ts +++ b/apps/mobile/src/components/explore/hooks.test.ts @@ -1,6 +1,5 @@ import { NativeSyntheticEvent, Share } from 'react-native' import { ContextMenuAction, ContextMenuOnPressNativeEvent } from 'react-native-context-menu-view' -import { act } from 'react-test-renderer' import configureMockStore from 'redux-mock-store' import { useExploreTokenContextMenu } from 'src/components/explore/hooks' import { renderHookWithProviders } from 'src/test/render' @@ -9,6 +8,7 @@ import { FavoritesState } from 'wallet/src/features/favorites/slice' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { SectionName } from 'wallet/src/telemetry/constants' import { SAMPLE_SEED_ADDRESS_1 } from 'wallet/src/test/fixtures/constants' +import { cleanup } from 'wallet/src/test/test-utils' const tokenId = SAMPLE_SEED_ADDRESS_1 const currencyId = `1-${tokenId}` @@ -35,10 +35,6 @@ describe(useExploreTokenContextMenu, () => { { resolvers } ) - await act(async () => { - // Wait for the token query to resolve - }) - expect(result.current.menuActions).toEqual([ expect.objectContaining({ title: 'Favorite token', @@ -57,6 +53,7 @@ describe(useExploreTokenContextMenu, () => { onPress: expect.any(Function), }), ]) + cleanup() }) it('renders proper context menu items when onEditFavorites is provided', async () => { @@ -66,10 +63,6 @@ describe(useExploreTokenContextMenu, () => { { resolvers } ) - await act(async () => { - // Wait for the token query to resolve - }) - expect(result.current.menuActions).toEqual([ expect.objectContaining({ title: 'Favorite token', @@ -88,6 +81,7 @@ describe(useExploreTokenContextMenu, () => { onPress: expect.any(Function), }), ]) + cleanup() }) it('calls onEditFavorites when edit favorites is pressed', async () => { @@ -97,10 +91,6 @@ describe(useExploreTokenContextMenu, () => { { resolvers } ) - await act(async () => { - // Wait for the token query to resolve - }) - const editFavoritesActionIndex = result.current.menuActions.findIndex( (action: ContextMenuAction) => action.title === 'Edit favorites' ) @@ -109,6 +99,7 @@ describe(useExploreTokenContextMenu, () => { } as NativeSyntheticEvent) expect(onEditFavorites).toHaveBeenCalledTimes(1) + cleanup() }) }) @@ -124,10 +115,6 @@ describe(useExploreTokenContextMenu, () => { } ) - await act(async () => { - // Wait for the token query to resolve - }) - expect(result.current.menuActions).toEqual([ expect.objectContaining({ title: 'Remove favorite', @@ -146,6 +133,7 @@ describe(useExploreTokenContextMenu, () => { onPress: expect.any(Function), }), ]) + cleanup() }) it("dispatches add to favorites redux action when 'Favorite token' is pressed", async () => { @@ -155,10 +143,6 @@ describe(useExploreTokenContextMenu, () => { { resolvers, store } ) - await act(async () => { - // Wait for the token query to resolve - }) - const favoriteTokenActionIndex = result.current.menuActions.findIndex( (action: ContextMenuAction) => action.title === 'Favorite token' ) @@ -173,6 +157,7 @@ describe(useExploreTokenContextMenu, () => { payload: { currencyId: tokenMenuParams.currencyId }, }, ]) + cleanup() }) it("dispatches remove from favorites redux action when 'Remove favorite' is pressed", async () => { @@ -185,10 +170,6 @@ describe(useExploreTokenContextMenu, () => { { resolvers, store } ) - await act(async () => { - // Wait for the token query to resolve - }) - const removeFavoriteTokenActionIndex = result.current.menuActions.findIndex( (action: ContextMenuAction) => action.title === 'Remove favorite' ) @@ -203,6 +184,7 @@ describe(useExploreTokenContextMenu, () => { payload: { currencyId: tokenMenuParams.currencyId }, }, ]) + cleanup() }) }) @@ -216,10 +198,6 @@ describe(useExploreTokenContextMenu, () => { resolvers, }) - await act(async () => { - // Wait for the token query to resolve - }) - const swapActionIndex = result.current.menuActions.findIndex( (action: ContextMenuAction) => action.title === 'Swap' ) @@ -246,6 +224,7 @@ describe(useExploreTokenContextMenu, () => { }, }, ]) + cleanup() }) it('opens share modal when share is pressed', async () => { @@ -253,10 +232,6 @@ describe(useExploreTokenContextMenu, () => { resolvers, }) - await act(async () => { - // Wait for the token query to resolve - }) - jest.spyOn(Share, 'share') const shareActionIndex = result.current.menuActions.findIndex( @@ -267,5 +242,6 @@ describe(useExploreTokenContextMenu, () => { } as NativeSyntheticEvent) expect(Share.share).toHaveBeenCalledTimes(1) + cleanup() }) }) diff --git a/apps/mobile/src/components/explore/hooks.ts b/apps/mobile/src/components/explore/hooks.ts index 867313d9679..422e12ae517 100644 --- a/apps/mobile/src/components/explore/hooks.ts +++ b/apps/mobile/src/components/explore/hooks.ts @@ -16,6 +16,7 @@ import { useSelectHasTokenFavorited, useToggleFavoriteCallback } from 'src/featu import { openModal } from 'src/features/modals/modalSlice' import { sendMobileAnalyticsEvent } from 'src/features/telemetry' import { MobileEventName, ShareableEntity } from 'src/features/telemetry/constants' +import { CurrencyId } from 'uniswap/src/types/currency' import { logger } from 'utilities/src/logger/logger' import { ChainId } from 'wallet/src/constants/chains' import { AssetType } from 'wallet/src/entities/assets' @@ -25,7 +26,7 @@ import { } from 'wallet/src/features/transactions/transactionState/types' import { useAppDispatch } from 'wallet/src/state' import { ElementName, ModalName, SectionNameType } from 'wallet/src/telemetry/constants' -import { CurrencyId, currencyIdToAddress } from 'wallet/src/utils/currencyId' +import { currencyIdToAddress } from 'wallet/src/utils/currencyId' import { getTokenUrl } from 'wallet/src/utils/linking' interface TokenMenuParams { diff --git a/apps/mobile/src/components/explore/search/SearchEmptySection.tsx b/apps/mobile/src/components/explore/search/SearchEmptySection.tsx index 05290f2d914..75fddeefb0d 100644 --- a/apps/mobile/src/components/explore/search/SearchEmptySection.tsx +++ b/apps/mobile/src/components/explore/search/SearchEmptySection.tsx @@ -7,9 +7,8 @@ import { SearchPopularNFTCollections } from 'src/components/explore/search/Searc import { SearchPopularTokens } from 'src/components/explore/search/SearchPopularTokens' import { renderSearchItem } from 'src/components/explore/search/SearchResultsSection' import { SectionHeaderText } from 'src/components/explore/search/SearchSectionHeader' -import { AnimatedFlex, Flex, Text, TouchableArea, useSporeColors } from 'ui/src' +import { AnimatedFlex, Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src' import ClockIcon from 'ui/src/assets/icons/clock.svg' -import TrendArrowIcon from 'ui/src/assets/icons/trend-up.svg' import { iconSizes } from 'ui/src/theme' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' @@ -21,6 +20,8 @@ import { } from 'wallet/src/features/search/SearchResult' import { selectSearchHistory } from 'wallet/src/features/search/selectSearchHistory' +const TrendUpIcon = + export const SUGGESTED_WALLETS: WalletSearchResult[] = [ { type: SearchResultType.ENSAddress, @@ -62,7 +63,7 @@ export function SearchEmptySection(): JSX.Element { // Show search history (if applicable), trending tokens, and wallets return ( - + {searchHistory.length > 0 && ( )} - } title={t('explore.search.section.popularTokens')} /> + - } title={t('explore.search.section.popularNFT')} /> + } + icon={TrendUpIcon} title={t('explore.search.section.suggestedWallets')} /> } @@ -118,11 +119,6 @@ const walletKey = (wallet: WalletSearchResult): string => { return wallet.address } -export const TrendIcon = (): JSX.Element => { - const colors = useSporeColors() - return -} - export const RecentIcon = (): JSX.Element => { const colors = useSporeColors() return ( diff --git a/apps/mobile/src/components/explore/search/SearchResultsLoader.tsx b/apps/mobile/src/components/explore/search/SearchResultsLoader.tsx index 326559d26bc..921c2974151 100644 --- a/apps/mobile/src/components/explore/search/SearchResultsLoader.tsx +++ b/apps/mobile/src/components/explore/search/SearchResultsLoader.tsx @@ -2,26 +2,35 @@ import React from 'react' import { useTranslation } from 'react-i18next' import { FadeIn, FadeOut } from 'react-native-reanimated' import { SectionHeaderText } from 'src/components/explore/search/SearchSectionHeader' -import { AnimatedFlex, Flex, Loader } from 'ui/src' +import { AnimatedFlex, Flex, Icons, Loader } from 'ui/src' export const SearchResultsLoader = (): JSX.Element => { const { t } = useTranslation() return ( - + } + title={t('explore.search.section.tokens')} + /> - + } + title={t('explore.search.section.nft')} + /> - + } + title={t('explore.search.section.wallets')} + /> diff --git a/apps/mobile/src/components/explore/search/SearchResultsSection.tsx b/apps/mobile/src/components/explore/search/SearchResultsSection.tsx index 824c30112f6..509fb1bd458 100644 --- a/apps/mobile/src/components/explore/search/SearchResultsSection.tsx +++ b/apps/mobile/src/components/explore/search/SearchResultsSection.tsx @@ -16,7 +16,7 @@ import { formatTokenSearchResults, getSearchResultId, } from 'src/components/explore/search/utils' -import { AnimatedFlex, Flex, Text } from 'ui/src' +import { AnimatedFlex, Flex, Icons, Text } from 'ui/src' import { SafetyLevel, useExploreSearchQuery, @@ -35,15 +35,21 @@ import { getValidAddress } from 'wallet/src/utils/addresses' import { SEARCH_RESULT_HEADER_KEY } from './constants' import { SearchResultOrHeader } from './types' +const ICON_SIZE = '$icon.24' +const ICON_COLOR = '$neutral2' + const WalletHeaderItem: SearchResultOrHeader = { + icon: , type: SEARCH_RESULT_HEADER_KEY, title: i18n.t('explore.search.section.wallets'), } const TokenHeaderItem: SearchResultOrHeader = { + icon: , type: SEARCH_RESULT_HEADER_KEY, title: i18n.t('explore.search.section.tokens'), } const NFTHeaderItem: SearchResultOrHeader = { + icon: , type: SEARCH_RESULT_HEADER_KEY, title: i18n.t('explore.search.section.nft'), } @@ -121,7 +127,12 @@ export function SearchResultsSection({ searchQuery }: { searchQuery: string }): const hasVerifiedNFTResults = Boolean(nftCollectionResults?.some((res) => res.isVerified)) - const showWalletSectionFirst = exactUnitagMatch || (exactENSMatch && !prefixTokenMatch) + const isUsernameSearch = useMemo(() => { + return searchQuery.includes('.') + }, [searchQuery]) + + const showWalletSectionFirst = + isUsernameSearch && (exactUnitagMatch || (exactENSMatch && !prefixTokenMatch)) const showNftCollectionsBeforeTokens = hasVerifiedNFTResults && !hasVerifiedTokenResults const sortedSearchResults: SearchResultOrHeader[] = useMemo(() => { @@ -133,18 +144,17 @@ export function SearchResultsSection({ searchQuery }: { searchQuery: string }): const walletsWithHeader = walletSearchResults.length > 0 ? [WalletHeaderItem, ...walletSearchResults] : [] - // Rank token and nft results - const searchResultItems: SearchResultOrHeader[] = showNftCollectionsBeforeTokens - ? [...nftsWithHeader, ...tokensWithHeader] - : [...tokensWithHeader, ...nftsWithHeader] + let searchResultItems: SearchResultOrHeader[] = [] - // Add wallet results at beginning or end - if (walletsWithHeader.length > 0) { - if (showWalletSectionFirst) { - searchResultItems.unshift(...walletsWithHeader) - } else { - searchResultItems.push(...walletsWithHeader) - } + if (showWalletSectionFirst) { + // Wallets first, then tokens, then NFTs + searchResultItems = [...walletsWithHeader, ...tokensWithHeader, ...nftsWithHeader] + } else if (showNftCollectionsBeforeTokens) { + // NFTs, then wallets, then tokens + searchResultItems = [...nftsWithHeader, ...walletsWithHeader, ...tokensWithHeader] + } else { + // Tokens, then NFTs, then wallets + searchResultItems = [...tokensWithHeader, ...nftsWithHeader, ...walletsWithHeader] } // Add etherscan items at end @@ -182,7 +192,7 @@ export function SearchResultsSection({ searchQuery }: { searchQuery: string }): } return ( - + @@ -222,7 +232,6 @@ export function SearchResultsSection({ searchQuery }: { searchQuery: string }): } // Render function for FlatList of SearchResult items - export const renderSearchItem = ({ item: searchResult, searchContext, @@ -233,7 +242,11 @@ export const renderSearchItem = ({ switch (searchResult.type) { case SEARCH_RESULT_HEADER_KEY: return ( - + ) case SearchResultType.Token: return diff --git a/apps/mobile/src/components/explore/search/SearchSectionHeader.tsx b/apps/mobile/src/components/explore/search/SearchSectionHeader.tsx index e8eaf3c7be0..b6f4fe2fbb5 100644 --- a/apps/mobile/src/components/explore/search/SearchSectionHeader.tsx +++ b/apps/mobile/src/components/explore/search/SearchSectionHeader.tsx @@ -12,9 +12,9 @@ export const SectionHeaderText = ({ ...rest }: SectionHeaderTextProps & TextProps): JSX.Element => { return ( - + {icon && icon} - + {title} diff --git a/apps/mobile/src/components/explore/search/items/SearchUnitagItem.tsx b/apps/mobile/src/components/explore/search/items/SearchUnitagItem.tsx index bcfc2baab2e..4549e7761e6 100644 --- a/apps/mobile/src/components/explore/search/items/SearchUnitagItem.tsx +++ b/apps/mobile/src/components/explore/search/items/SearchUnitagItem.tsx @@ -1,6 +1,6 @@ import React from 'react' import { SearchWalletItemBase } from 'src/components/explore/search/items/SearchWalletItemBase' -import { Flex } from 'ui/src' +import { Flex, Text } from 'ui/src' import { imageSizes } from 'ui/src/theme' import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText' @@ -8,6 +8,7 @@ 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 @@ -27,7 +28,16 @@ export function SearchUnitagItem({ - + + + + {sanitizeAddressText(shortenAddress(address))} + + ) diff --git a/apps/mobile/src/components/explore/search/types.tsx b/apps/mobile/src/components/explore/search/types.tsx index 464e2749aa1..333ff70fe21 100644 --- a/apps/mobile/src/components/explore/search/types.tsx +++ b/apps/mobile/src/components/explore/search/types.tsx @@ -5,4 +5,4 @@ import { SEARCH_RESULT_HEADER_KEY } from './constants' export type SearchResultOrHeader = | SearchResult - | { type: typeof SEARCH_RESULT_HEADER_KEY; title: string } + | { type: typeof SEARCH_RESULT_HEADER_KEY; title: string; icon?: JSX.Element } diff --git a/apps/mobile/src/components/home/TokensTab.tsx b/apps/mobile/src/components/home/TokensTab.tsx index 54d9fe9cac5..4b6560b9caf 100644 --- a/apps/mobile/src/components/home/TokensTab.tsx +++ b/apps/mobile/src/components/home/TokensTab.tsx @@ -13,10 +13,10 @@ import { openModal } from 'src/features/modals/modalSlice' import { Screens } from 'src/screens/Screens' import { Flex } from 'ui/src' import { GQLQueries } from 'uniswap/src/data/graphql/uniswap-data-api/queries' +import { CurrencyId } from 'uniswap/src/types/currency' import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard' import { TokenBalanceListRow } from 'wallet/src/features/portfolio/TokenBalanceListContext' import { ModalName } from 'wallet/src/telemetry/constants' -import { CurrencyId } from 'wallet/src/utils/currencyId' export const TOKENS_TAB_DATA_DEPENDENCIES = [GQLQueries.PortfolioBalances] diff --git a/apps/mobile/src/components/loading/index.tsx b/apps/mobile/src/components/loading/index.tsx index e0eafff8773..07071ea921f 100644 --- a/apps/mobile/src/components/loading/index.tsx +++ b/apps/mobile/src/components/loading/index.tsx @@ -49,7 +49,12 @@ function Favorite({ height, contrast }: { height?: number; contrast?: boolean }) return ( {/* surface3 because these only show up on explore modal which has a blurred bg that makes neutral3 look weird */} - + ) } diff --git a/apps/mobile/src/features/balances/hooks.ts b/apps/mobile/src/features/balances/hooks.ts index 59f62242b95..39cbf529289 100644 --- a/apps/mobile/src/features/balances/hooks.ts +++ b/apps/mobile/src/features/balances/hooks.ts @@ -6,14 +6,15 @@ import { useAppDispatch } from 'src/app/hooks' import { ScannerModalState } from 'src/components/QRCodeScanner/constants' import { openModal } from 'src/features/modals/modalSlice' import { useNavigateToSend } from 'src/features/send/hooks' -import { useNavigateToSwap } from 'src/features/swap/hooks' import { sendMobileAnalyticsEvent } from 'src/features/telemetry' import { MobileEventName, ShareableEntity } from 'src/features/telemetry/constants' +import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' +import { CurrencyId } from 'uniswap/src/types/currency' import { logger } from 'utilities/src/logger/logger' import { ONE_SECOND_MS } from 'utilities/src/time/time' import { ChainId } from 'wallet/src/constants/chains' +import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' import { usePortfolioCacheUpdater } from 'wallet/src/features/dataApi/balances' -import { PortfolioBalance } from 'wallet/src/features/dataApi/types' import { toggleTokenVisibility } from 'wallet/src/features/favorites/slice' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' @@ -21,7 +22,6 @@ import { CurrencyField } from 'wallet/src/features/transactions/transactionState import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' import { ModalName } from 'wallet/src/telemetry/constants' import { - CurrencyId, areCurrencyIdsEqual, currencyIdToAddress, currencyIdToChain, @@ -47,7 +47,8 @@ export function useTokenContextMenu({ const { t } = useTranslation() const dispatch = useAppDispatch() const activeAccountAddress = useActiveAccountAddressWithThrow() - const navigateToSwap = useNavigateToSwap() + + const { navigateToSwapFlow } = useWalletNavigation() const navigateToSend = useNavigateToSend() const activeAccountHoldsToken = @@ -73,9 +74,9 @@ export function useTokenContextMenu({ const onPressSwap = useCallback( (currencyField: CurrencyField) => { // Do not show warning modal speed-bump if user is trying to swap tokens they own - navigateToSwap(currencyField, currencyAddress, currencyChainId) + navigateToSwapFlow({ currencyField, currencyAddress, currencyChainId }) }, - [currencyAddress, currencyChainId, navigateToSwap] + [currencyAddress, currencyChainId, navigateToSwapFlow] ) const onPressShare = useCallback(async () => { @@ -124,13 +125,8 @@ export function useTokenContextMenu({ const menuActions = useMemo( (): MenuAction[] => [ { - title: t('common.button.buy'), - systemIcon: 'arrow.down', - onPress: () => onPressSwap(CurrencyField.OUTPUT), - }, - { - title: t('common.button.sell'), - systemIcon: 'arrow.up', + title: t('common.button.swap'), + systemIcon: 'rectangle.2.swap', onPress: () => onPressSwap(CurrencyField.INPUT), }, { diff --git a/apps/mobile/src/features/dataApi/balances.ts b/apps/mobile/src/features/dataApi/balances.ts index e864e8212e5..bfe0f9a3cd9 100644 --- a/apps/mobile/src/features/dataApi/balances.ts +++ b/apps/mobile/src/features/dataApi/balances.ts @@ -1,8 +1,8 @@ import { useMemo } from 'react' +import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' +import { CurrencyId } from 'uniswap/src/types/currency' import { usePortfolioBalances } from 'wallet/src/features/dataApi/balances' -import { PortfolioBalance } from 'wallet/src/features/dataApi/types' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' -import { CurrencyId } from 'wallet/src/utils/currencyId' /** Helper hook to retrieve balances for a set of currencies for the active account. */ export function useBalances(currencies: CurrencyId[] | undefined): PortfolioBalance[] | null { diff --git a/apps/mobile/src/features/deepLinking/constants.ts b/apps/mobile/src/features/deepLinking/constants.ts new file mode 100644 index 00000000000..426c7537814 --- /dev/null +++ b/apps/mobile/src/features/deepLinking/constants.ts @@ -0,0 +1,6 @@ +import { uniswapUrls } from 'uniswap/src/constants/urls' + +export const UNISWAP_URL_SCHEME = 'uniswap://' +export const UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM = 'uniswap://wc?uri=' +export const UNISWAP_URL_SCHEME_SCANTASTIC = 'uniswap://scantastic?' +export const UNISWAP_WALLETCONNECT_URL = uniswapUrls.appBaseUrl + '/wc?uri=' diff --git a/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.ts b/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.ts index 5c7f630f2d6..f82308a29b1 100644 --- a/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.ts +++ b/apps/mobile/src/features/deepLinking/handleDeepLinkSaga.ts @@ -4,10 +4,19 @@ import { Alert } from 'react-native' import { URL } from 'react-native-url-polyfill' import { appSelect } from 'src/app/hooks' import { navigate } from 'src/app/navigation/rootNavigation' +import { + getScantasticQueryParams, + parseScantasticParams, +} from 'src/components/WalletConnect/ScanSheet/util' +import { + UNISWAP_URL_SCHEME, + UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM, + UNISWAP_WALLETCONNECT_URL, +} from 'src/features/deepLinking/constants' import { handleMoonpayReturnLink } from 'src/features/deepLinking/handleMoonpayReturnLinkSaga' import { handleSwapLink } from 'src/features/deepLinking/handleSwapLinkSaga' import { handleTransactionLink } from 'src/features/deepLinking/handleTransactionLinkSaga' -import { openModal } from 'src/features/modals/modalSlice' +import { closeAllModals, openModal } from 'src/features/modals/modalSlice' import { sendMobileAnalyticsEvent } from 'src/features/telemetry' import { MobileEventName, ShareableEntity } from 'src/features/telemetry/constants' import { waitForWcWeb3WalletIsReady } from 'src/features/walletConnect/saga' @@ -15,16 +24,26 @@ import { pairWithWalletConnectURI } from 'src/features/walletConnect/utils' import { setDidOpenFromDeepLink } from 'src/features/walletConnect/walletConnectSlice' import { WidgetType } from 'src/features/widgets/widgets' import { Screens } from 'src/screens/Screens' +import { Statsig } from 'statsig-react-native' import { call, put, takeLatest } from 'typed-redux-saga' -import { UNISWAP_APP_HOSTNAME, uniswapUrls } from 'uniswap/src/constants/urls' +import { UNISWAP_APP_HOSTNAME } from 'uniswap/src/constants/urls' +import { FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/experiments/flags' import i18n from 'uniswap/src/i18n/i18n' import { logger } from 'utilities/src/logger/logger' +import { selectExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/selectors' +import { ExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/slice' import { fromUniswapWebAppLink } from 'wallet/src/features/chains/utils' +import { ScantasticParams } from 'wallet/src/features/scantastic/types' +import { + fetchExtensionEligibityByAddresses, + fetchUnitagByAddresses, +} from 'wallet/src/features/unitags/api' import { selectAccounts, selectActiveAccount, selectActiveAccountAddress, selectNonPendingAccounts, + selectSignerMnemonicAccounts, } from 'wallet/src/features/wallet/selectors' import { setAccountAsActive } from 'wallet/src/features/wallet/slice' import { ModalName } from 'wallet/src/telemetry/constants' @@ -41,10 +60,7 @@ export enum LinkSource { Share = 'Share', } -export const UNISWAP_URL_SCHEME = 'uniswap://' -export const UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM = 'uniswap://wc?uri=' const UNISWAP_URL_SCHEME_WIDGET = 'uniswap://widget/' -export const UNISWAP_WALLETCONNECT_URL = uniswapUrls.appBaseUrl + '/wc?uri=' const WALLETCONNECT_URI_SCHEME = 'wc:' // https://eips.ethereum.org/EIPS/eip-1328 const NFT_ITEM_SHARE_LINK_HASH_REGEX = /^(#\/)?nfts\/asset\/(0x[a-fA-F0-9]{40})\/(\d+)$/ @@ -240,6 +256,13 @@ export function* handleDeepLink(action: ReturnType) { return } + // Handle scantastic deep links in the format uniswap://scantastic?${PARAMS} + const maybeScantasticQueryParams = getScantasticQueryParams(action.payload.url) + if (maybeScantasticQueryParams) { + yield* call(handleScantasticDeepLink, maybeScantasticQueryParams) + return + } + // Skip handling any non-WalletConnect uniswap:// URL scheme deep links for now for security reasons // Currently only used on WalletConnect Universal Link web page fallback button (https://uniswap.org/app/wc) if (action.payload.url.startsWith(UNISWAP_URL_SCHEME)) { @@ -359,3 +382,71 @@ export function* parseAndValidateUserAddress(userAddress: string | null) { return matchingAccount.address } + +export function* handleScantasticDeepLink(scantasticQueryParams: string): Generator { + const params = parseScantasticParams(scantasticQueryParams) + const extensionOnboardingEnabled = Statsig.checkGate( + getFeatureFlagName(FeatureFlags.ExtensionOnboarding) + ) + const scantasticEnabled = Statsig.checkGate(getFeatureFlagName(FeatureFlags.Scantastic)) + + if (!params || !scantasticEnabled) { + Alert.alert( + i18n.t('walletConnect.error.scantastic.title'), + i18n.t('walletConnect.error.scantastic.message'), + [{ text: i18n.t('common.button.ok') }] + ) + return + } + + const extensionOnboardingState = yield* appSelect(selectExtensionOnboardingState) + + if ( + extensionOnboardingEnabled && + extensionOnboardingState !== ExtensionOnboardingState.Undefined + ) { + // User has already passed extension onboarding eligibility check + yield* call(launchScantastic, params) + return + } + + const signerAccounts = yield* appSelect(selectSignerMnemonicAccounts) + + const waitlistPositionResponse = (yield* call( + fetchExtensionEligibityByAddresses, + signerAccounts.map((account) => account.address) + )).data + + if (extensionOnboardingEnabled && waitlistPositionResponse?.isAccepted) { + yield* call(launchScantastic, params) + } else { + const activeAccount = yield* appSelect(selectActiveAccount) + + const activeAccountUnitag = activeAccount + ? (yield* call(fetchUnitagByAddresses, [activeAccount.address])).data?.[activeAccount.address] + ?.username + : undefined + + yield* put(closeAllModals()) + yield* put( + openModal({ + name: ModalName.ExtensionWaitlistModal, + initialState: { + isUserOnWaitlist: activeAccountUnitag !== undefined, + }, + }) + ) + } +} + +function* launchScantastic(params: ScantasticParams): Generator { + yield* put(closeAllModals()) + yield* put( + openModal({ + name: ModalName.Scantastic, + initialState: { + params, + }, + }) + ) +} diff --git a/apps/mobile/src/features/externalProfile/ProfileHeader.tsx b/apps/mobile/src/features/externalProfile/ProfileHeader.tsx index d45cdac0b16..4cbacd1b2e6 100644 --- a/apps/mobile/src/features/externalProfile/ProfileHeader.tsx +++ b/apps/mobile/src/features/externalProfile/ProfileHeader.tsx @@ -20,6 +20,7 @@ import { Text, TouchableArea, getUniconV2Colors, + useExtractedColors, useIsDarkMode, useSporeColors, useUniconColors, @@ -35,7 +36,6 @@ import { CurrencyField } from 'wallet/src/features/transactions/transactionState import { useAvatar, useDisplayName } from 'wallet/src/features/wallet/hooks' import { DisplayNameType } from 'wallet/src/features/wallet/types' import { ElementName, ModalName } from 'wallet/src/telemetry/constants' -import { useExtractedColors } from 'wallet/src/utils/colors' import { openUri } from 'wallet/src/utils/linking' const HEADER_GRADIENT_HEIGHT = 144 diff --git a/apps/mobile/src/features/favorites/hooks.ts b/apps/mobile/src/features/favorites/hooks.ts index 6f0bd1f969e..efb195925a1 100644 --- a/apps/mobile/src/features/favorites/hooks.ts +++ b/apps/mobile/src/features/favorites/hooks.ts @@ -2,6 +2,7 @@ import { useCallback, useMemo } from 'react' import { useAppDispatch, useAppSelector } from 'src/app/hooks' import { sendMobileAnalyticsEvent } from 'src/features/telemetry' import { MobileEventName } from 'src/features/telemetry/constants' +import { CurrencyId } from 'uniswap/src/types/currency' import { makeSelectHasTokenFavorited, selectWatchedAddressSet, @@ -14,7 +15,7 @@ import { } from 'wallet/src/features/favorites/slice' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { useDisplayName } from 'wallet/src/features/wallet/hooks' -import { CurrencyId, currencyIdToAddress, currencyIdToChain } from 'wallet/src/utils/currencyId' +import { currencyIdToAddress, currencyIdToChain } from 'wallet/src/utils/currencyId' export function useToggleFavoriteCallback(id: CurrencyId, isFavoriteToken: boolean): () => void { const dispatch = useAppDispatch() diff --git a/apps/mobile/src/features/fiatOnRamp/FiatOnRampAmountSection.tsx b/apps/mobile/src/features/fiatOnRamp/FiatOnRampAmountSection.tsx index 48d2d916ec1..5bc25893b49 100644 --- a/apps/mobile/src/features/fiatOnRamp/FiatOnRampAmountSection.tsx +++ b/apps/mobile/src/features/fiatOnRamp/FiatOnRampAmountSection.tsx @@ -22,6 +22,7 @@ import { useSporeColors, } from 'ui/src' import { fonts, iconSizes, spacing } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { NumberType } from 'utilities/src/format/types' import { usePrevious } from 'utilities/src/react/hooks' import { DEFAULT_DELAY, useDebounce } from 'utilities/src/time/timing' @@ -29,7 +30,6 @@ import { CurrencyLogo } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' import { AmountInput } from 'wallet/src/components/input/AmountInput' import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader' import { Pill } from 'wallet/src/components/text/Pill' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { FiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { ElementName } from 'wallet/src/telemetry/constants' diff --git a/apps/mobile/src/features/fiatOnRamp/FiatOnRampContext.tsx b/apps/mobile/src/features/fiatOnRamp/FiatOnRampContext.tsx index b728ee1ca44..6c08bc462be 100644 --- a/apps/mobile/src/features/fiatOnRamp/FiatOnRampContext.tsx +++ b/apps/mobile/src/features/fiatOnRamp/FiatOnRampContext.tsx @@ -19,6 +19,8 @@ interface FiatOnRampContextType { setSelectedQuote: (quote: FORQuote | undefined) => void countryCode: string setCountryCode: (countryCode: string) => void + countryState: string | undefined + setCountryState: (countryCode: string | undefined) => void baseCurrencyInfo?: FiatCurrencyInfo setBaseCurrencyInfo: (baseCurrency: FiatCurrencyInfo | undefined) => void quoteCurrency: FiatOnRampCurrency @@ -33,11 +35,13 @@ const initialState: FiatOnRampContextType = { setQuotesSections: () => undefined, setSelectedQuote: () => undefined, setCountryCode: () => undefined, + setCountryState: () => undefined, setBaseCurrencyInfo: () => undefined, setQuoteCurrency: () => undefined, setAmount: () => undefined, setServiceProviders: () => undefined, countryCode: '', + countryState: undefined, quoteCurrency: { currencyInfo: undefined }, } @@ -51,6 +55,7 @@ export function FiatOnRampProvider({ children }: { children: React.ReactNode }): const [quotesSections, setQuotesSections] = useState() const [selectedQuote, setSelectedQuote] = useState() const [countryCode, setCountryCode] = useState(getCountry()) + const [countryState, setCountryState] = useState() const [baseCurrencyInfo, setBaseCurrencyInfo] = useState() const [amount, setAmount] = useState() const [serviceProviders, setServiceProviders] = useState() @@ -72,6 +77,8 @@ export function FiatOnRampProvider({ children }: { children: React.ReactNode }): setQuotesSections, countryCode, setCountryCode, + countryState, + setCountryState, baseCurrencyInfo, setBaseCurrencyInfo, quoteCurrency, diff --git a/apps/mobile/src/features/fiatOnRamp/hooks.ts b/apps/mobile/src/features/fiatOnRamp/hooks.ts index 7c9ce2382bb..57e137ff410 100644 --- a/apps/mobile/src/features/fiatOnRamp/hooks.ts +++ b/apps/mobile/src/features/fiatOnRamp/hooks.ts @@ -7,14 +7,17 @@ import { Delay } from 'src/components/layout/Delayed' import { FiatOnRampCurrency } from 'src/features/fiatOnRamp/types' import { ColorTokens, useSporeColors } from 'ui/src' import { uniswapUrls } from 'uniswap/src/constants/urls' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { isAndroid } from 'uniswap/src/utils/platform' import { logger } from 'utilities/src/logger/logger' import { useDebounce } from 'utilities/src/time/timing' -import { useAllCommonBaseCurrencies } from 'wallet/src/components/TokenSelector/hooks' +import { + useAllCommonBaseCurrencies, + useCurrencies, +} from 'wallet/src/components/TokenSelector/hooks' import { BRIDGED_BASE_ADDRESSES } from 'wallet/src/constants/addresses' import { ChainId } from 'wallet/src/constants/chains' -import { fromMoonpayNetwork } from 'wallet/src/features/chains/utils' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' +import { fromMoonpayNetwork, toSupportedChainId } from 'wallet/src/features/chains/utils' import { useFiatOnRampAggregatorSupportedTokensQuery, useFiatOnRampBuyQuoteQuery, @@ -37,6 +40,7 @@ import { createTransactionId } from 'wallet/src/features/transactions/utils' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' import { areAddressesEqual } from 'wallet/src/utils/addresses' import { getFormattedCurrencyAmount } from 'wallet/src/utils/currency' +import { buildCurrencyId, buildNativeCurrencyId } from 'wallet/src/utils/currencyId' import { ValueType } from 'wallet/src/utils/getCurrencyAmount' const ETH_POLYGON_MOONPAY_CODE = 'eth_polygon' @@ -304,15 +308,18 @@ function useMoonpayError( } function findTokenOptionForFiatOnRampToken( - commonBaseCurrencies: CurrencyInfo[] | undefined = [], + currencies: CurrencyInfo[] | undefined = [], fiatOnRampToken: FORSupportedToken ): Maybe { - return commonBaseCurrencies.find( - (item) => + return currencies.find((item) => { + const symbol = fiatOnRampToken.cryptoCurrencyCode.split('_')?.[0]?.toLowerCase() + return ( item && - fiatOnRampToken.cryptoCurrencyCode.toLowerCase() === item.currency.symbol?.toLowerCase() && + symbol && + symbol === item.currency.symbol?.toLowerCase() && fiatOnRampToken.chainId === item.currency.chainId.toString() - ) + ) + }) } function findTokenOptionForMoonpayCurrency( @@ -353,6 +360,17 @@ function findTokenOptionForMoonpayCurrency( return currencyInfo } +function buildCurrencyIdForFORSupportedToken( + supportedToken: FORSupportedToken +): string | undefined { + const chainId = toSupportedChainId(supportedToken.chainId) + return chainId + ? supportedToken.address + ? buildCurrencyId(chainId, supportedToken.address) + : buildNativeCurrencyId(chainId) + : undefined +} + export function useFiatOnRampSupportedTokens({ sourceCurrencyCode, countryCode, @@ -372,31 +390,39 @@ export function useFiatOnRampSupportedTokens({ refetch: refetchSupportedTokens, } = useFiatOnRampAggregatorSupportedTokensQuery({ fiatCurrency: sourceCurrencyCode, countryCode }) + const currencyIds: string[] = useMemo( + () => + supportedTokensResponse?.supportedTokens + .map(buildCurrencyIdForFORSupportedToken) + .filter((st): st is string => !!st) ?? [], + [supportedTokensResponse] + ) + const { - data: commonBaseCurrencies, - error: commonBaseCurrenciesError, - loading: commonBaseCurrenciesLoading, - refetch: refetchCommonBaseCurrencies, - } = useAllCommonBaseCurrencies() + data: currencies, + error: currenciesError, + loading: currenciesLoading, + refetch: refetchCurrencies, + } = useCurrencies(currencyIds) const list = useMemo( () => (supportedTokensResponse?.supportedTokens || []) .map((fiatOnRampToken) => ({ - currencyInfo: findTokenOptionForFiatOnRampToken(commonBaseCurrencies, fiatOnRampToken), + currencyInfo: findTokenOptionForFiatOnRampToken(currencies, fiatOnRampToken), })) .filter((item) => !!item.currencyInfo), - [commonBaseCurrencies, supportedTokensResponse?.supportedTokens] + [currencies, supportedTokensResponse?.supportedTokens] ) - const loading = supportedTokensLoading || commonBaseCurrenciesLoading - const error = Boolean(supportedTokensError || commonBaseCurrenciesError) + const loading = supportedTokensLoading || currenciesLoading + const error = Boolean(supportedTokensError || currenciesError) const refetch = async (): Promise => { if (supportedTokensError) { await refetchSupportedTokens?.() } - if (commonBaseCurrenciesError) { - refetchCommonBaseCurrencies?.() + if (currenciesError) { + refetchCurrencies?.() } } diff --git a/apps/mobile/src/features/fiatOnRamp/types.ts b/apps/mobile/src/features/fiatOnRamp/types.ts index d8d154f0127..4d0df6994d9 100644 --- a/apps/mobile/src/features/fiatOnRamp/types.ts +++ b/apps/mobile/src/features/fiatOnRamp/types.ts @@ -1,4 +1,4 @@ -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' export type FiatOnRampCurrency = { currencyInfo: Maybe diff --git a/apps/mobile/src/features/modals/ModalsState.ts b/apps/mobile/src/features/modals/ModalsState.ts index e4c78df51e0..24fa0c0697c 100644 --- a/apps/mobile/src/features/modals/ModalsState.ts +++ b/apps/mobile/src/features/modals/ModalsState.ts @@ -1,6 +1,7 @@ import { ExploreModalState } from 'src/app/modals/ExploreModalState' import { ScannerModalState } from 'src/components/QRCodeScanner/constants' import { RemoveWalletModalState } from 'src/components/RemoveWallet/RemoveWalletModalState' +import { ExtensionWaitlistModalState } from 'src/features/scantastic/ExtensionWaitlistModalState' import { ScantasticModalState } from 'src/features/scantastic/ScantasticModalState' import { Screens } from 'src/screens/Screens' import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types' @@ -27,6 +28,7 @@ export interface ModalsState { [ModalName.RemoveWallet]: AppModalState [ModalName.RestoreWallet]: AppModalState [ModalName.Scantastic]: AppModalState + [ModalName.ExtensionWaitlistModal]: AppModalState [ModalName.Send]: AppModalState [ModalName.Swap]: AppModalState [ModalName.UnitagsIntro]: AppModalState<{ diff --git a/apps/mobile/src/features/modals/modalSlice.ts b/apps/mobile/src/features/modals/modalSlice.ts index 255abc21aae..28b94d687d5 100644 --- a/apps/mobile/src/features/modals/modalSlice.ts +++ b/apps/mobile/src/features/modals/modalSlice.ts @@ -3,6 +3,7 @@ import { ExploreModalState } from 'src/app/modals/ExploreModalState' import { ScannerModalState } from 'src/components/QRCodeScanner/constants' import { RemoveWalletModalState } from 'src/components/RemoveWallet/RemoveWalletModalState' import { ExchangeTransferModalState } from 'src/features/fiatOnRamp/ExchangeTransferModalState' +import { ExtensionWaitlistModalState } from 'src/features/scantastic/ExtensionWaitlistModalState' import { ScantasticModalState } from 'src/features/scantastic/ScantasticModalState' import { Screens } from 'src/screens/Screens' import { getKeys } from 'utilities/src/primitives/objects' @@ -27,6 +28,11 @@ type ExploreModalParams = { initialState?: ExploreModalState } +type ExtensionWaitlistModalParams = { + name: typeof ModalName.ExtensionWaitlistModal + initialState: ExtensionWaitlistModalState +} + type FiatCurrencySelectorParams = { name: typeof ModalName.FiatCurrencySelector initialState?: undefined @@ -85,6 +91,7 @@ export type OpenModalParams = | ExchangeTransferModalParams | ExperimentsModalParams | ExploreModalParams + | ExtensionWaitlistModalParams | FiatCurrencySelectorParams | FiatOnRampModalParams | FiatOnRampAggregatorModalParams @@ -126,6 +133,10 @@ export const initialModalsState: ModalsState = { isOpen: false, initialState: undefined, }, + [ModalName.ExtensionWaitlistModal]: { + isOpen: false, + initialState: undefined, + }, [ModalName.Swap]: { isOpen: false, initialState: undefined, diff --git a/apps/mobile/src/features/nfts/collection/NFTCollectionHeader.tsx b/apps/mobile/src/features/nfts/collection/NFTCollectionHeader.tsx index 7c5ba1613de..84df969f5a6 100644 --- a/apps/mobile/src/features/nfts/collection/NFTCollectionHeader.tsx +++ b/apps/mobile/src/features/nfts/collection/NFTCollectionHeader.tsx @@ -5,14 +5,21 @@ import { BackButton } from 'src/components/buttons/BackButton' import { Loader } from 'src/components/loading' import { LongMarkdownText } from 'src/components/text/LongMarkdownText' import { NFTCollectionContextMenu } from 'src/features/nfts/collection/NFTCollectionContextMenu' -import { Flex, FlexProps, Logos, Text, useDeviceInsets, useSporeColors } from 'ui/src' +import { + Flex, + FlexProps, + Logos, + Text, + useDeviceInsets, + useExtractedColors, + useSporeColors, +} from 'ui/src' import VerifiedIcon from 'ui/src/assets/icons/verified.svg' import { iconSizes, spacing } from 'ui/src/theme' import { NumberType } from 'utilities/src/format/types' import { ImageUri } from 'wallet/src/features/images/ImageUri' import { NFTViewer } from 'wallet/src/features/images/NFTViewer' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' -import { useExtractedColors } from 'wallet/src/utils/colors' import { NFTCollectionData } from './types' const PROFILE_IMAGE_SIZE = 72 diff --git a/apps/mobile/src/features/scantastic/ExtensionWaitlistModal.tsx b/apps/mobile/src/features/scantastic/ExtensionWaitlistModal.tsx new file mode 100644 index 00000000000..b692e61512b --- /dev/null +++ b/apps/mobile/src/features/scantastic/ExtensionWaitlistModal.tsx @@ -0,0 +1,80 @@ +import { useTranslation } from 'react-i18next' +import { useAppSelector } from 'src/app/hooks' +import { closeModal } from 'src/features/modals/modalSlice' +import { selectModalState } from 'src/features/modals/selectModalState' +import { Button, Flex, Icons, Text, TouchableArea } from 'ui/src' +import { uniswapUrls } from 'uniswap/src/constants/urls' +import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' +import { useAppDispatch } from 'wallet/src/state' +import { ModalName } from 'wallet/src/telemetry/constants' +import { openUri } from 'wallet/src/utils/linking' + +export function ExtensionWaitlistModal(): JSX.Element { + const { t } = useTranslation() + const dispatch = useAppDispatch() + + const { initialState } = useAppSelector(selectModalState(ModalName.ExtensionWaitlistModal)) + const isUserOnWaitlist = initialState?.isUserOnWaitlist + + const onClose = (): void => { + dispatch(closeModal({ name: ModalName.ExtensionWaitlistModal })) + } + + return ( + + + + + {isUserOnWaitlist ? ( + + ) : ( + + )} + + + + {isUserOnWaitlist + ? t('scantastic.modal.onWaitlist.title') + : t('scantastic.modal.notOnWaitlist.title')} + + + {isUserOnWaitlist + ? t('scantastic.modal.onWaitlist.message') + : t('scantastic.modal.notOnWaitlist.message')} + + {!isUserOnWaitlist && ( + => { + await openUri(uniswapUrls.helpArticleUrls.extensionWaitlist) + }}> + + {t('common.button.learn')} + + + )} + + + + + + + + ) +} diff --git a/apps/mobile/src/features/scantastic/ExtensionWaitlistModalState.ts b/apps/mobile/src/features/scantastic/ExtensionWaitlistModalState.ts new file mode 100644 index 00000000000..0cdb634c5a1 --- /dev/null +++ b/apps/mobile/src/features/scantastic/ExtensionWaitlistModalState.ts @@ -0,0 +1,3 @@ +export interface ExtensionWaitlistModalState { + isUserOnWaitlist: boolean +} diff --git a/apps/mobile/src/features/scantastic/ScantasticModal.tsx b/apps/mobile/src/features/scantastic/ScantasticModal.tsx index 854191ebc6d..ab8d220f9ae 100644 --- a/apps/mobile/src/features/scantastic/ScantasticModal.tsx +++ b/apps/mobile/src/features/scantastic/ScantasticModal.tsx @@ -11,9 +11,13 @@ 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 { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' +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 { useActiveAccount } from 'wallet/src/features/wallet/hooks' +import { useNonPendingSignerAccounts } from 'wallet/src/features/wallet/hooks' import { ModalName } from 'wallet/src/telemetry/constants' import { getOtpDurationString } from 'wallet/src/utils/duration' import { getEncryptedMnemonic } from './ScantasticEncryption' @@ -33,7 +37,14 @@ export function ScantasticModal(): JSX.Element | null { const colors = useSporeColors() const dispatch = useAppDispatch() - const account = useActiveAccount() + // Use the first mnemonic account because zero-balance mnemonic accounts will fail to retrieve the mnemonic from rnEthers + const account = useNonPendingSignerAccounts().sort( + (account1, account2) => account1.derivationIndex - account2.derivationIndex + )[0] + + if (!account) { + throw new Error('This should not be accessed with no mnemonic accounts') + } const { initialState } = useAppSelector(selectModalState(ModalName.Scantastic)) const params = initialState?.params @@ -66,6 +77,7 @@ export function ScantasticModal(): JSX.Element | null { hideDelay: 6 * ONE_SECOND_MS, }) ) + dispatch(setExtensionOnboardingState(ExtensionOnboardingState.Completed)) dispatch(closeAllModals()) } @@ -277,7 +289,7 @@ export function ScantasticModal(): JSX.Element | null { return ( diff --git a/apps/mobile/src/features/swap/hooks.ts b/apps/mobile/src/features/swap/hooks.ts deleted file mode 100644 index a988f1e5533..00000000000 --- a/apps/mobile/src/features/swap/hooks.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { useCallback } from 'react' -import { openModal } from 'src/features/modals/modalSlice' -import { getNativeAddress } from 'wallet/src/constants/addresses' -import { ChainId } from 'wallet/src/constants/chains' -import { AssetType, CurrencyAsset } from 'wallet/src/entities/assets' -import { - CurrencyField, - TransactionState, -} from 'wallet/src/features/transactions/transactionState/types' -import { useAppDispatch } from 'wallet/src/state' -import { ModalName } from 'wallet/src/telemetry/constants' -import { areAddressesEqual } from 'wallet/src/utils/addresses' - -export const useNavigateToSwap: () => ( - currencyField: CurrencyField, - currencyAddress: Address, - currencyChainId: ChainId -) => void = () => { - const dispatch = useAppDispatch() - return useCallback( - (currencyField, currencyAddress, currencyChainId) => { - const nativeTokenAddress = getNativeAddress(currencyChainId) - const nativeToken: CurrencyAsset = { - address: nativeTokenAddress, - chainId: currencyChainId, - type: AssetType.Currency, - } - const chosenToken: CurrencyAsset = { - address: currencyAddress, - chainId: currencyChainId, - type: AssetType.Currency, - } - - const opposedToken = areAddressesEqual(nativeTokenAddress, currencyAddress) - ? null - : nativeToken - - const swapFormState: TransactionState = { - exactCurrencyField: currencyField, - exactAmountToken: '', - [CurrencyField.INPUT]: currencyField === CurrencyField.INPUT ? chosenToken : opposedToken, - [CurrencyField.OUTPUT]: currencyField === CurrencyField.OUTPUT ? chosenToken : opposedToken, - } - dispatch(openModal({ name: ModalName.Swap, initialState: swapFormState })) - }, - [dispatch] - ) -} diff --git a/apps/mobile/src/features/unitags/EditUnitagProfileScreen.tsx b/apps/mobile/src/features/unitags/EditUnitagProfileScreen.tsx index 2ee80414cc9..4fb269135aa 100644 --- a/apps/mobile/src/features/unitags/EditUnitagProfileScreen.tsx +++ b/apps/mobile/src/features/unitags/EditUnitagProfileScreen.tsx @@ -27,6 +27,7 @@ import { useUniconColors, } from 'ui/src' import { borderRadii, fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme' +import { useExtractedColors } from 'ui/src/utils/colors' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' import { useUnitagUpdater } from 'uniswap/src/features/unitags/context' @@ -53,7 +54,6 @@ import { useAppDispatch } from 'wallet/src/state' import { sendWalletAnalyticsEvent } from 'wallet/src/telemetry' import { UnitagEventName } from 'wallet/src/telemetry/constants' import { shortenAddress } from 'wallet/src/utils/addresses' -import { useExtractedColors } from 'wallet/src/utils/colors' const BIO_TEXT_INPUT_LINES = 6 diff --git a/apps/mobile/src/features/widgets/widgets.ts b/apps/mobile/src/features/widgets/widgets.ts index 569b8a0c645..ed40f4a25ca 100644 --- a/apps/mobile/src/features/widgets/widgets.ts +++ b/apps/mobile/src/features/widgets/widgets.ts @@ -3,11 +3,11 @@ import { getItem, reloadAllTimelines, setItem } from 'react-native-widgetkit' import { sendMobileAnalyticsEvent } from 'src/features/telemetry' import { MobileEventName } from 'src/features/telemetry/constants' import { getBuildVariant } from 'src/utils/version' +import { CurrencyId } from 'uniswap/src/types/currency' import { isAndroid } from 'uniswap/src/utils/platform' import { analytics } from 'utilities/src/telemetry/analytics/analytics' import { currencyIdToContractInput } from 'wallet/src/features/dataApi/utils' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' -import { CurrencyId } from 'wallet/src/utils/currencyId' const APP_GROUP = 'group.com.uniswap.widgets' const KEY_WIDGET_EVENTS = getBuildVariant() + '.widgets.configuration.events' diff --git a/apps/mobile/src/index.ts b/apps/mobile/src/index.ts index 89c0480a0e2..14285a59908 100644 --- a/apps/mobile/src/index.ts +++ b/apps/mobile/src/index.ts @@ -1,4 +1,21 @@ // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// +// TODO - remove this declaration after updating react-native-reanimted +// to the never version (3.3.0 has typescript type issues and is missing +// this declaration in the react-native-reanimated.d.ts file) +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace jest { + interface Matchers { + toHaveAnimatedStyle( + style: Record[] | Record, + config?: { + shouldMatchAllProps?: boolean + } + ): R + } + } +} + export {} diff --git a/apps/mobile/src/screens/FiatOnRampConnecting.tsx b/apps/mobile/src/screens/FiatOnRampConnecting.tsx index 908aed95c30..ea741cd6bca 100644 --- a/apps/mobile/src/screens/FiatOnRampConnecting.tsx +++ b/apps/mobile/src/screens/FiatOnRampConnecting.tsx @@ -28,6 +28,7 @@ import { ImageUri } from 'wallet/src/features/images/ImageUri' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' +import { forceFetchFiatOnRampTransactions } from 'wallet/src/features/transactions/slice' import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks' import { sendWalletAnalyticsEvent } from 'wallet/src/telemetry' import { FiatOnRampEventName, ModalName } from 'wallet/src/telemetry/constants' @@ -50,6 +51,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | quotesSections, serviceProviders, countryCode, + countryState, baseCurrencyInfo, quoteCurrency, amount, @@ -120,12 +122,14 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | serviceProvider: serviceProvider.serviceProvider, preselectedServiceProvider: serviceProvider.serviceProvider, countryCode, + countryState, fiatCurrency: baseCurrencyInfo?.code.toLowerCase(), cryptoCurrency: quoteCurrency?.currencyInfo?.currency.symbol?.toLowerCase(), }) } - await openUri(widgetUrl).catch(onError) dispatchAddTransaction() + await openUri(widgetUrl).catch(onError) + dispatch(forceFetchFiatOnRampTransactions()) } if (timeoutElapsed && !widgetLoading && widgetData) { @@ -146,6 +150,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element | quoteCurrency?.currencyInfo?.currency.symbol, quotesSections, countryCode, + countryState, ]) const isDarkMode = useIsDarkMode() diff --git a/apps/mobile/src/screens/FiatOnRampScreen.tsx b/apps/mobile/src/screens/FiatOnRampScreen.tsx index 4c8725f5642..14638c7fb18 100644 --- a/apps/mobile/src/screens/FiatOnRampScreen.tsx +++ b/apps/mobile/src/screens/FiatOnRampScreen.tsx @@ -90,7 +90,6 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { const [showTokenSelector, setShowTokenSelector] = useState(false) const inputRef = useRef(null) const [selectingCountry, setSelectingCountry] = useState(false) - const [countryState, setCountryState] = useState() const { isSheetReady } = useBottomSheetContext() @@ -100,6 +99,8 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element { setQuotesSections, countryCode, setCountryCode, + countryState, + setCountryState, amount, setAmount, setBaseCurrencyInfo, diff --git a/apps/mobile/src/screens/HomeScreen.tsx b/apps/mobile/src/screens/HomeScreen.tsx index 29d53909fb1..e3ee8b48405 100644 --- a/apps/mobile/src/screens/HomeScreen.tsx +++ b/apps/mobile/src/screens/HomeScreen.tsx @@ -19,6 +19,7 @@ import Animated, { import { SvgProps } from 'react-native-svg' import { SceneRendererProps, TabBar } from 'react-native-tab-view' import { useAppDispatch, useAppSelector } from 'src/app/hooks' +import { ExtensionPromoModal } from 'src/app/modals/ExtensionPromoModal' import { UniconsV2Modal } from 'src/app/modals/UniconsV2Modal' import { NavBar, SWAP_BUTTON_HEIGHT } from 'src/app/navigation/NavBar' import { AppStackScreenProp } from 'src/app/navigation/types' @@ -26,6 +27,7 @@ import { ScannerModalState } from 'src/components/QRCodeScanner/constants' import Trace from 'src/components/Trace/Trace' import TraceTabView from 'src/components/Trace/TraceTabView' import { AccountHeader } from 'src/components/accounts/AccountHeader' +import { ExtensionPromoBanner } from 'src/components/banners/ExtensionPromoBanner' import { ACTIVITY_TAB_DATA_DEPENDENCIES, ActivityTab } from 'src/components/home/ActivityTab' import { FEED_TAB_DATA_DEPENDENCIES, FeedTab } from 'src/components/home/FeedTab' import { NFTS_TAB_DATA_DEPENDENCIES, NftsTab } from 'src/components/home/NftsTab' @@ -78,7 +80,10 @@ import { import { useSelectAddressHasNotifications } from 'wallet/src/features/notifications/hooks' import { setNotificationStatus } from 'wallet/src/features/notifications/slice' import { TokenBalanceListRow } from 'wallet/src/features/portfolio/TokenBalanceListContext' -import { useCanActiveAddressClaimUnitag } 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 { PendingAccountActions, @@ -326,6 +331,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX.Elemen const { sync } = useScrollSync(currentTabIndex, scrollPairs, headerConfig) const forAggregatorEnabled = useFeatureFlag(FeatureFlags.ForAggregator) + const cexTransferEnabled = useFeatureFlag(FeatureFlags.CexTransfers) const onPressBuy = useCallback( () => @@ -345,14 +351,14 @@ export function HomeScreen(props?: AppStackScreenProp): JSX.Elemen }, [dispatch]) const onPressSend = useCallback(() => dispatch(openModal({ name: ModalName.Send })), [dispatch]) const onPressReceive = useCallback(() => { - if (forAggregatorEnabled) { + if (cexTransferEnabled) { dispatch(openModal({ name: ModalName.ReceiveCryptoModal })) } else { dispatch( openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }) ) } - }, [dispatch, forAggregatorEnabled]) + }, [dispatch, cexTransferEnabled]) const onPressViewOnlyLabel = useCallback( () => dispatch(openModal({ name: ModalName.ViewOnlyExplainer })), [dispatch] @@ -421,12 +427,35 @@ export function HomeScreen(props?: AppStackScreenProp): JSX.Elemen const shouldShowUniconV2Modal = isUniconsV2Enabled && !hasViewedUniconV2IntroModal && !hasAvatar && !avatarLoading + const { showExtensionPromoBanner } = useShowExtensionPromoBanner() + const [showExtensionPromoModal, setShowExtensionPromoModal] = useState(false) + const viewOnlyLabel = t('home.warning.viewOnly') + + const promoBanner = useMemo(() => { + if (shouldPromptUnitag) { + return ( + + + + ) + } else if (showExtensionPromoBanner) { + return ( + + setShowExtensionPromoModal(true)} + /> + + ) + } + return null + }, [shouldPromptUnitag, showExtensionPromoBanner, activeAccount.address]) + const contentHeader = useMemo(() => { return ( - + - + {isSignerAccount ? ( @@ -446,11 +475,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX.Elemen )} - {shouldPromptUnitag && ( - - - - )} + {promoBanner} ) }, [ @@ -459,7 +484,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX.Elemen actions, onPressViewOnlyLabel, viewOnlyLabel, - shouldPromptUnitag, + promoBanner, ]) const contentContainerStyle = useMemo>( @@ -709,7 +734,7 @@ export function HomeScreen(props?: AppStackScreenProp): JSX.Elemen width="100%" zIndex="$sticky" /> - {shouldShowUniconV2Modal && ( + {shouldShowUniconV2Modal ? ( <> {/* manual scrim so we can highlight unicon above it */} @@ -721,6 +746,10 @@ export function HomeScreen(props?: AppStackScreenProp): JSX.Elemen zIndex="$modalBackdrop" /> + ) : ( + showExtensionPromoModal && ( + setShowExtensionPromoModal(false)} /> + ) )} ) @@ -738,7 +767,7 @@ type QuickAction = { function QuickActions({ actions }: { actions: QuickAction[] }): JSX.Element { return ( - + {actions.map((action) => ( { @@ -252,14 +252,14 @@ function TokenDetails({ dismissWarningCallback() setShowWarningModal(false) if (activeTransactionType !== undefined) { - navigateToSwap(activeTransactionType, currencyAddress, currencyChainId) + navigateToSwapFlow({ currencyField: activeTransactionType, currencyAddress, currencyChainId }) } }, [ activeTransactionType, currencyAddress, currencyChainId, dismissWarningCallback, - navigateToSwap, + navigateToSwapFlow, ]) const inModal = useAppSelector(selectModalState(ModalName.Explore)).isOpen diff --git a/apps/mobile/src/test/fixtures/explore.ts b/apps/mobile/src/test/fixtures/explore.ts new file mode 100644 index 00000000000..9518e0af293 --- /dev/null +++ b/apps/mobile/src/test/fixtures/explore.ts @@ -0,0 +1,27 @@ +import { TokenItemData } from 'src/components/explore/TokenItem' +import { Token } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { ChainId } from 'wallet/src/constants/chains' +import { fromGraphQLChain } from 'wallet/src/features/chains/utils' +import { token } from 'wallet/src/test/fixtures' +import { createFixture } from 'wallet/src/test/utils' + +type TokenItemDataOptions = { + token: Token | null +} + +export const tokenItemData = createFixture({ + token: null, +})(({ token: t }) => { + const defaultToken = token() + const chain = t?.chain ?? defaultToken.chain + + return { + name: t?.name ?? defaultToken.name, + logoUrl: t?.project?.logo?.url ?? defaultToken.project.logo.url, + chainId: fromGraphQLChain(chain) ?? ChainId.Mainnet, + address: t?.address ?? defaultToken.address, + symbol: t?.symbol ?? defaultToken.symbol, + } +}) + +export const TOKEN_ITEM_DATA = tokenItemData() diff --git a/apps/mobile/src/test/fixtures/index.ts b/apps/mobile/src/test/fixtures/index.ts index c7c7b9019fb..ff731339d86 100644 --- a/apps/mobile/src/test/fixtures/index.ts +++ b/apps/mobile/src/test/fixtures/index.ts @@ -1 +1,2 @@ +export * from './explore' export * from './redux' diff --git a/apps/mobile/src/utils/reanimated.test.ts b/apps/mobile/src/utils/reanimated.test.ts index efa7e8bb96a..57304b7909d 100644 --- a/apps/mobile/src/utils/reanimated.test.ts +++ b/apps/mobile/src/utils/reanimated.test.ts @@ -21,15 +21,15 @@ describe('reanimated numberToLocaleStringWorklet', function () { expect(typeof numberToLocaleStringWorklet(num, locale)).toBe('string') }) - it('returns <$0.00000001 if the value is below that amount', function () { - const num = 0.000000001 + it('returns <$<0.0000000000000001 if the value is below that amount', function () { + const num = 0.00000000000000001 expect( numberToLocaleStringWorklet(num, 'en-US', { style: 'currency', currency: 'USD', }) - ).toBe('<$0.00000001') + ).toBe('<$0.0000000000000001') }) it('returns a string with 3 sig figs if it is between 0.00000001 and 1,', function () { diff --git a/apps/mobile/src/utils/reanimated.ts b/apps/mobile/src/utils/reanimated.ts index 323527975e9..23d9e040240 100644 --- a/apps/mobile/src/utils/reanimated.ts +++ b/apps/mobile/src/utils/reanimated.ts @@ -364,8 +364,8 @@ export function numberToLocaleStringWorklet( // if we encounter any coins with a unit price over $1M then add a shorthand case if (value < 0) { sNum = value.toString() - } else if (value < 0.00000001) { - sNum = '<0.00000001' + } else if (value < 0.0000000000000001) { + sNum = '<0.0000000000000001' } else if (value < 1) { sNum = convertSmallSciNotationToDecimal(value) } else { diff --git a/apps/mobile/src/utils/version.ts b/apps/mobile/src/utils/version.ts index 4cd1a9f1479..1528d93f2e9 100644 --- a/apps/mobile/src/utils/version.ts +++ b/apps/mobile/src/utils/version.ts @@ -60,6 +60,13 @@ export function getSentryEnvironment(): SentryEnvironment { return SentryEnvironment.PROD } +export function getSentryTracesSamplingRate(): number { + if (isDevBuild() || isBetaBuild()) { + return 1 + } + return 0.2 +} + enum SentryEnvironment { DEV = 'development', BETA = 'beta', diff --git a/apps/web/.depcheckrc b/apps/web/.depcheckrc index eee71ca1205..15ead466e88 100644 --- a/apps/web/.depcheckrc +++ b/apps/web/.depcheckrc @@ -14,6 +14,7 @@ ignores: [ "swc-loader", "postinstall-postinstall", "process", + "madge", # Dependencies that depcheck thinks are missing but are actually present or never used ## GraphQL "@graphql-codegen/*", diff --git a/apps/web/cypress/e2e/service-worker.test.ts b/apps/web/cypress/e2e/service-worker.test.ts index 3b4ef3d49e5..78b7230c407 100644 --- a/apps/web/cypress/e2e/service-worker.test.ts +++ b/apps/web/cypress/e2e/service-worker.test.ts @@ -1,6 +1,6 @@ import assert from 'assert' -describe('Service Worker', () => { +describe.skip('Service Worker', () => { before(() => { // Fail fast if there is no Service Worker on this build. cy.request({ url: '/service-worker.js', headers: { 'Service-Worker': 'script' } }).then((response) => { diff --git a/apps/web/functions/api/image/nfts/collection/[index].tsx b/apps/web/functions/api/image/nfts/collection/[index].tsx index 2475e48f8cc..20c25a8e55d 100644 --- a/apps/web/functions/api/image/nfts/collection/[index].tsx +++ b/apps/web/functions/api/image/nfts/collection/[index].tsx @@ -2,10 +2,10 @@ import { ImageResponse } from '@vercel/og' import { blocklistedCollections } from '../../../../../src/nft/utils/blocklist' -import { getColor } from '../../../../../src/utils/getColor' import { CHECK_URL, WATERMARK_URL } from '../../../../constants' import getCollection from '../../../../utils/getCollection' import getFont from '../../../../utils/getFont' +import { getRGBColor } from '../../../../utils/getRGBColor' import { getRequest } from '../../../../utils/getRequest' export const onRequest: PagesFunction = async ({ params, request }) => { @@ -30,7 +30,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { return new Response('Collection not found.', { status: 404 }) } - const [fontData, palette] = await Promise.all([getFont(origin), getColor(data.ogImage)]) + const [fontData, palette] = await Promise.all([getFont(origin), getRGBColor(data.ogImage)]) // Split name into words to wrap them since satori does not support inline text wrapping const words = data.name.split(' ') @@ -49,7 +49,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { style={{ display: 'flex', alignItems: 'center', - backgroundColor: `rgba(${palette[0]}, ${palette[1]}, ${palette[2]}, 0.75)`, + backgroundColor: `rgba(${palette.red}, ${palette.green}, ${palette.blue}, 0.75)`, padding: '72px', }} > diff --git a/apps/web/functions/api/image/tokens/[[index]].tsx b/apps/web/functions/api/image/tokens/[[index]].tsx index d02172b2a52..0956acbefb4 100644 --- a/apps/web/functions/api/image/tokens/[[index]].tsx +++ b/apps/web/functions/api/image/tokens/[[index]].tsx @@ -1,10 +1,10 @@ /* eslint-disable import/no-unused-modules */ import { ImageResponse } from '@vercel/og' -import { getColor } from '../../../../src/utils/getColor' import { WATERMARK_URL } from '../../../constants' import getFont from '../../../utils/getFont' import getNetworkLogoUrl from '../../../utils/getNetworkLogoURL' +import { getRGBColor } from '../../../utils/getRGBColor' import { getRequest } from '../../../utils/getRequest' import getToken from '../../../utils/getToken' @@ -27,7 +27,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => { return new Response('Token not found.', { status: 404 }) } - const [fontData, palette] = await Promise.all([getFont(origin), getColor(data.ogImage, true)]) + const [fontData, palette] = await Promise.all([getFont(origin), getRGBColor(data.ogImage)]) const networkLogo = getNetworkLogoUrl(networkName.toUpperCase(), origin) @@ -50,7 +50,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
{ + if (!imageUrl) return DEFAULT_COLOR + + const colors = await getExtractedColors(imageUrl) + const extractedColor = colors?.detail ?? colors?.primary + + return extractedColor ? parseToRgb(extractedColor) : DEFAULT_COLOR +} diff --git a/apps/web/package.json b/apps/web/package.json index f1ab9679be4..710d18364be 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -6,6 +6,7 @@ "scripts": { "ajv": "node scripts/compile-ajv-validators.js", "check:deps:usage": "depcheck", + "check:circular": "../../scripts/check-circular-imports.sh ./src/pages/App.tsx 8", "contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"", "contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/types/v3 \"../../node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"", "contracts": "yarn contracts:compile:abi && yarn contracts:compile:v3", @@ -115,7 +116,7 @@ "@types/uuid": "9.0.1", "@types/wcag-contrast": "3.0.0", "@types/xml2js": "0.4.14", - "@uniswap/default-token-list": "11.11.0", + "@uniswap/default-token-list": "11.16.0", "@uniswap/eslint-config": "workspace:^", "@vanilla-extract/jest-transform": "1.1.1", "@vanilla-extract/webpack-plugin": "2.3.1", @@ -137,11 +138,10 @@ "jest-fail-on-console": "3.1.1", "jest-fetch-mock": "3.0.3", "jest-styled-components": "7.2.0", - "jpeg-js": "0.4.4", "lint-staged": "^14.0.0", + "madge": "6.1.0", "mini-css-extract-plugin": "^2.7.6", "path-browserify": "1.0.1", - "png-ts": "0.0.3", "postinstall-postinstall": "2.1.0", "prettier": "latest", "process": "0.11.10", @@ -197,18 +197,18 @@ "@uniswap/merkle-distributor": "1.0.1", "@uniswap/permit2-sdk": "1.2.0", "@uniswap/redux-multicall": "1.1.8", - "@uniswap/router-sdk": "1.8.0", - "@uniswap/sdk-core": "4.1.2", + "@uniswap/router-sdk": "1.9.0", + "@uniswap/sdk-core": "4.2.0", "@uniswap/smart-order-router": "3.17.3", "@uniswap/token-lists": "1.0.0-beta.33", - "@uniswap/uniswapx-sdk": "1.4.1", - "@uniswap/universal-router-sdk": "1.7.1", + "@uniswap/uniswapx-sdk": "2.0.1-alpha.4", + "@uniswap/universal-router-sdk": "1.8.2", "@uniswap/v2-core": "1.0.1", "@uniswap/v2-periphery": "1.1.0-beta.0", - "@uniswap/v2-sdk": "4.1.0", + "@uniswap/v2-sdk": "4.3.0", "@uniswap/v3-core": "1.0.1", "@uniswap/v3-periphery": "1.4.4", - "@uniswap/v3-sdk": "3.10.2", + "@uniswap/v3-sdk": "3.11.0", "@vanilla-extract/css": "1.14.0", "@vanilla-extract/dynamic": "2.1.0", "@vanilla-extract/sprinkles": "1.6.1", diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx index f6722e84019..2e325d0e384 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/OffchainActivityModal.tsx @@ -144,16 +144,17 @@ export function useOrderAmounts(order?: UniswapXOrderDetails): } function getOrderTitle(order: UniswapXOrderDetails): ReactNode { + const isLimit = order.type === SignatureType.SIGN_LIMIT switch (order.status) { case UniswapXOrderStatus.OPEN: - return order.type === SignatureType.SIGN_LIMIT ? Limit pending : Order pending + return isLimit ? Limit pending : Order pending case UniswapXOrderStatus.EXPIRED: - return order.type === SignatureType.SIGN_LIMIT ? Limit expired : Order expired + return isLimit ? Limit expired : Order expired case UniswapXOrderStatus.INSUFFICIENT_FUNDS: case UniswapXOrderStatus.CANCELLED: - return order.type === SignatureType.SIGN_LIMIT ? Limit cancelled : Order cancelled + return isLimit ? Limit cancelled : Order cancelled case UniswapXOrderStatus.FILLED: - return order.type === SignatureType.SIGN_LIMIT ? Limit executed : Order executed + return isLimit ? Limit executed : Order executed default: return null } @@ -165,7 +166,7 @@ function useCancelOrder(order?: UniswapXOrderDetails): () => Promise { if (!order) return undefined return await cancelMultipleUniswapXOrders({ - encodedOrders: [order.encodedOrder as string], + orders: [{ encodedOrder: order.encodedOrder as string, type: order.type as SignatureType }], chainId: order.chainId, provider, permit2, diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/__snapshots__/parseRemote.test.tsx.snap b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/__snapshots__/parseRemote.test.tsx.snap index 2ec526c2520..6c4fac85673 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/__snapshots__/parseRemote.test.tsx.snap +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/__snapshots__/parseRemote.test.tsx.snap @@ -103,6 +103,7 @@ Object { "offchainOrderDetails": Object { "addedTime": 10000, "chainId": 1, + "encodedOrder": undefined, "id": "someId", "offerer": "someOfferer", "orderHash": "someHash", @@ -119,7 +120,7 @@ Object { "type": 1, }, "txHash": "someHash", - "type": "signUniswapXOrder", + "type": undefined, }, "prefixIconSrc": "bolt.svg", "status": "FAILED", @@ -414,7 +415,6 @@ Object { "type": 1, }, "txHash": "someHash", - "type": "signUniswapXOrder", }, "prefixIconSrc": "bolt.svg", "status": "CONFIRMED", diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseLocal.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseLocal.ts index 80ce82bba31..fe0ee2eecd6 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseLocal.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseLocal.ts @@ -269,6 +269,7 @@ export function signatureToActivity( ): Activity | undefined { switch (signature.type) { case SignatureType.SIGN_UNISWAPX_ORDER: + case SignatureType.SIGN_UNISWAPX_V2_ORDER: case SignatureType.SIGN_LIMIT: { // Only returns Activity items for orders that don't have an on-chain counterpart if (isOnChainOrder(signature.status)) return undefined @@ -289,8 +290,7 @@ export function signatureToActivity( offerer: signature.offerer, txHash: signature.txHash, chainId: signature.chainId, - type: - signature.type === SignatureType.SIGN_LIMIT ? SignatureType.SIGN_LIMIT : SignatureType.SIGN_UNISWAPX_ORDER, + type: signature.type, status: signature.status, swapInfo: signature.swapInfo, addedTime: signature.addedTime, 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 cc592aab2d2..92164dcb5d2 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.test.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.test.tsx @@ -222,7 +222,6 @@ describe('parseRemote', () => { type: 1, }, txHash: '0xHashValue', - type: 'signUniswapXOrder', addedTime: 10000, }) }) diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.tsx index dc8a76f8a4e..7243d32753b 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/parseRemote.tsx @@ -293,7 +293,6 @@ export function offchainOrderDetailsFromGraphQLTransactionActivity( offerer: activity.details.from, txHash: activity.details.hash, chainId, - type: SignatureType.SIGN_UNISWAPX_ORDER, status: UniswapXOrderStatus.FILLED, addedTime: activity.timestamp, swapInfo: { @@ -455,8 +454,9 @@ function swapOrderTypeToSignatureType(swapOrderType: SwapOrderType): SignatureTy case SwapOrderType.Limit: return SignatureType.SIGN_LIMIT case SwapOrderType.Dutch: - default: return SignatureType.SIGN_UNISWAPX_ORDER + case SwapOrderType.DutchV2: + return SignatureType.SIGN_UNISWAPX_V2_ORDER } } @@ -528,8 +528,8 @@ function parseUniswapXOrder({ details, chain, timestamp }: OrderActivity): Activ statusMessage, offchainOrderDetails: { id: details.id, - // TODO(limits): check type from backend and use SignatureType.SIGN_LIMIT here if necessary - type: SignatureType.SIGN_UNISWAPX_ORDER, + type: swapOrderTypeToSignatureType(details.swapOrderType), + encodedOrder: details.encodedOrder, txHash: details.hash, orderHash: details.hash, offerer: details.offerer, @@ -569,6 +569,7 @@ function parseRemoteActivity( } if (assetActivity.details.__typename === 'SwapOrderDetails') { + // UniswapX orders are returned as SwapOrderDetails until they are filled onchain, at which point they are returned as TransactionDetails return parseUniswapXOrder(assetActivity as OrderActivity) } diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/utils.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/utils.ts index a7634011839..139f25c7d45 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/utils.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Activity/utils.ts @@ -2,7 +2,7 @@ import { TransactionRequest } from '@ethersproject/abstract-provider' import { Web3Provider } from '@ethersproject/providers' import { t } from '@lingui/macro' import { ChainId } from '@uniswap/sdk-core' -import { DutchOrder } from '@uniswap/uniswapx-sdk' +import { CosignedV2DutchOrder, DutchOrder } from '@uniswap/uniswapx-sdk' import { getYear, isSameDay, isSameMonth, isSameWeek, isSameYear } from 'date-fns' import PERMIT2_ABI from 'uniswap/src/abis/permit2.json' import { Permit2 } from 'uniswap/src/abis/types' @@ -121,25 +121,32 @@ function getCancelMultipleParams(noncesToCancel: BigNumber[]): { }) } -function getCancelMultipleUniswapXOrdersParams(encodedOrders: string[], chainId: ChainId) { - const nonces = encodedOrders - .map((encodedOrder) => DutchOrder.parse(encodedOrder, chainId)) +function getCancelMultipleUniswapXOrdersParams( + orders: Array<{ encodedOrder: string; type: SignatureType }>, + chainId: ChainId +) { + const nonces = orders + .map(({ encodedOrder, type }) => + type === SignatureType.SIGN_UNISWAPX_V2_ORDER + ? CosignedV2DutchOrder.parse(encodedOrder, chainId) + : DutchOrder.parse(encodedOrder, chainId) + ) .map((order) => order.info.nonce) return getCancelMultipleParams(nonces) } export async function cancelMultipleUniswapXOrders({ - encodedOrders, + orders, chainId, permit2, provider, }: { - encodedOrders: string[] + orders: Array<{ encodedOrder: string; type: SignatureType }> chainId: ChainId permit2: Permit2 | null provider?: Web3Provider }) { - const cancelParams = getCancelMultipleUniswapXOrdersParams(encodedOrders, chainId) + const cancelParams = getCancelMultipleUniswapXOrdersParams(orders, chainId) if (!permit2 || !provider) return try { const transactions: ContractTransaction[] = [] @@ -155,11 +162,11 @@ export async function cancelMultipleUniswapXOrders({ } async function getCancelMultipleUniswapXOrdersTransaction( - encodedOrders: string[], + orders: Array<{ encodedOrder: string; type: SignatureType }>, chainId: ChainId, permit2: Permit2 ): Promise { - const cancelParams = getCancelMultipleUniswapXOrdersParams(encodedOrders, chainId) + const cancelParams = getCancelMultipleUniswapXOrdersParams(orders, chainId) if (!permit2 || cancelParams.length === 0) return try { const tx = await permit2.populateTransaction.invalidateUnorderedNonces(cancelParams[0].word, cancelParams[0].mask) @@ -176,15 +183,21 @@ async function getCancelMultipleUniswapXOrdersTransaction( export function useCreateCancelTransactionRequest( params: | { - encodedOrders?: string[] + orders: Array<{ encodedOrder: string; type: SignatureType }> chainId: ChainId } | undefined ): TransactionRequest | undefined { const permit2 = useContract(PERMIT2_ADDRESS, PERMIT2_ABI, true) const transactionFetcher = useCallback(() => { - if (!params || !params.encodedOrders || params.encodedOrders.filter(Boolean).length === 0 || !permit2) return - return getCancelMultipleUniswapXOrdersTransaction(params.encodedOrders, params.chainId, permit2) + if ( + !params || + !params.orders || + params.orders.filter(({ encodedOrder }) => Boolean(encodedOrder)).length === 0 || + !permit2 + ) + return + return getCancelMultipleUniswapXOrdersTransaction(params.orders, params.chainId, permit2) }, [params, permit2]) return useAsyncData(transactionFetcher).data diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitsMenu.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitsMenu.tsx index 6899463717d..336df29d34f 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitsMenu.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/LimitsMenu.tsx @@ -17,7 +17,7 @@ import { LimitDisclaimer } from 'components/swap/LimitDisclaimer' import { ContractTransaction } from 'ethers/lib/ethers' import { useContract } from 'hooks/useContract' import { useCallback, useMemo, useState } from 'react' -import { UniswapXOrderDetails } from 'state/signatures/types' +import { SignatureType, UniswapXOrderDetails } from 'state/signatures/types' import styled from 'styled-components' import PERMIT2_ABI from 'uniswap/src/abis/permit2.json' import { Permit2 } from 'uniswap/src/abis/types/Permit2' @@ -48,7 +48,9 @@ function useCancelMultipleOrders(orders?: UniswapXOrderDetails[]): () => Promise }) return cancelMultipleUniswapXOrders({ - encodedOrders: orders.map((order) => order.encodedOrder as string), + orders: orders.map((order) => { + return { encodedOrder: order.encodedOrder as string, type: order.type as SignatureType } + }), permit2, provider, chainId: orders?.[0].chainId, diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/hooks/useCancelLimitsGasEstimate.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/hooks/useCancelLimitsGasEstimate.ts index daadcff4ccb..77cb88f8227 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/hooks/useCancelLimitsGasEstimate.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Limits/hooks/useCancelLimitsGasEstimate.ts @@ -1,14 +1,19 @@ import { useCreateCancelTransactionRequest } from 'components/AccountDrawer/MiniPortfolio/Activity/utils' import { GasFeeResult, GasSpeed, useTransactionGasFee } from 'hooks/useTransactionGasFee' import { useMemo } from 'react' -import { UniswapXOrderDetails } from 'state/signatures/types' +import { SignatureType, UniswapXOrderDetails } from 'state/signatures/types' export function useCancelLimitsGasEstimate(orders?: UniswapXOrderDetails[]): GasFeeResult { const cancelTransactionParams = useMemo( () => orders && orders.length > 0 ? { - encodedOrders: orders.map((order) => order.encodedOrder as string), + orders: orders.map((order) => { + return { + encodedOrder: order.encodedOrder as string, + type: order.type as SignatureType, + } + }), chainId: orders[0].chainId, } : undefined, diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/cache.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/cache.ts index fa7bdbf9009..9518c049055 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/cache.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/cache.ts @@ -5,11 +5,11 @@ import { atom, useAtom } from 'jotai' import { atomWithStorage } from 'jotai/utils' import ms from 'ms' import { useCallback } from 'react' -import { deserializeToken, serializeToken } from 'state/user/hooks' import { SerializedToken } from 'state/user/types' import { PositionDetails } from 'types/position' import { buildCurrencyKey, currencyKey } from 'utils/currencyKey' +import { deserializeToken, serializeToken } from 'state/user/utils' import { getTokensAsync } from './getTokensAsync' import { useInterfaceMulticallContracts } from './hooks' diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts index df2c1f28930..84e66731d40 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/hooks.ts @@ -9,9 +9,9 @@ import NFTPositionManagerJSON from '@uniswap/v3-periphery/artifacts/contracts/No import MulticallJSON from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json' import { useWeb3React } from '@web3-react/core' import { isSupportedChain } from 'constants/chains' +import { RPC_PROVIDERS } from 'constants/providers' import { BaseContract } from 'ethers/lib/ethers' import { toContractInput } from 'graphql/data/util' -import { useNetworkProviders } from 'hooks/useNetworkProviders' import useStablecoinPrice from 'hooks/useStablecoinPrice' import { useMemo } from 'react' import { NonfungiblePositionManager, UniswapInterfaceMulticall } from 'uniswap/src/abis/types/v3' @@ -32,7 +32,6 @@ export function useContractMultichain( chainIds?: ChainId[] ): ContractMap { const { chainId: walletChainId, provider: walletProvider } = useWeb3React() - const networkProviders = useNetworkProviders() return useMemo(() => { const relevantChains = @@ -46,14 +45,14 @@ export function useContractMultichain( walletProvider && walletChainId === chainId ? walletProvider : isSupportedChain(chainId) - ? networkProviders[chainId] + ? RPC_PROVIDERS[chainId] : undefined if (provider) { acc[chainId] = getContract(addressMap[chainId] ?? '', ABI, provider) as T } return acc }, {}) - }, [ABI, addressMap, chainIds, networkProviders, walletChainId, walletProvider]) + }, [ABI, addressMap, chainIds, walletChainId, walletProvider]) } export function useV3ManagerContracts(chainIds: ChainId[]): ContractMap { diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions.tsx index 55cb5a24b9a..8a4849adb14 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/Pools/useMultiChainPositions.tsx @@ -48,6 +48,7 @@ const DEFAULT_CHAINS = [ ChainId.BNB, ChainId.AVALANCHE, ChainId.BASE, + ChainId.BLAST, ] type UseMultiChainPositionsData = { positions?: PositionInfo[]; loading: boolean } diff --git a/apps/web/src/components/AccountDrawer/MiniPortfolio/PortfolioLogo.tsx b/apps/web/src/components/AccountDrawer/MiniPortfolio/PortfolioLogo.tsx index 6543362b614..d4d6298aeac 100644 --- a/apps/web/src/components/AccountDrawer/MiniPortfolio/PortfolioLogo.tsx +++ b/apps/web/src/components/AccountDrawer/MiniPortfolio/PortfolioLogo.tsx @@ -9,7 +9,9 @@ import useENSAvatar from 'hooks/useENSAvatar' import React from 'react' import { Loader } from 'react-feather' import styled from 'styled-components' +import { useIsDarkMode } from 'theme/components/ThemeToggle' import { UniconV2 } from 'ui/src' +import { useLogolessColorScheme } from 'ui/src/utils/colors' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' @@ -116,9 +118,17 @@ function DoubleCurrencyLogo({ chainId, currencies, images, size }: DoubleCurrenc if (currencies.length > 1) { return } + return +} + +function LogolessPlaceholder({ currency, size }: { currency?: Currency; size: string }) { + const isDarkMode = useIsDarkMode() + const logolessColorScheme = useLogolessColorScheme(currency?.name ?? currency?.symbol ?? '') + const { foreground, background } = isDarkMode ? logolessColorScheme.dark : logolessColorScheme.light + return ( - - {currencies[0]?.symbol?.toUpperCase().replace('$', '').replace(/\s+/g, '').slice(0, 3)} + + {currency?.symbol?.toUpperCase().replace('$', '').replace(/\s+/g, '').slice(0, 3)} ) } diff --git a/apps/web/src/components/AccountDrawer/UniwalletModal.tsx b/apps/web/src/components/AccountDrawer/UniwalletModal.tsx index eb6c326b0da..79f0ad6c886 100644 --- a/apps/web/src/components/AccountDrawer/UniwalletModal.tsx +++ b/apps/web/src/components/AccountDrawer/UniwalletModal.tsx @@ -13,7 +13,7 @@ import { QRCodeSVG } from 'qrcode.react' import { useEffect, useState } from 'react' import styled, { useTheme } from 'styled-components' import { CloseIcon, ThemedText } from 'theme/components' -import { isAndroid, isIOS } from 'utils/platform' +import { isWebAndroid, isWebIOS } from 'uniswap/src/utils/platform' import uniPng from '../../assets/images/uniwallet_modal_icon.png' import { DownloadButton } from './DownloadButton' @@ -43,7 +43,7 @@ export default function UniwalletModal() { const [uri, setUri] = useState() // Displays the modal if not on iOS/Android, a Uniswap Wallet Connection is pending, & qrcode URI is available - const onLaunchedMobilePlatform = isIOS || isAndroid + const onLaunchedMobilePlatform = isWebIOS || isWebAndroid const open = !onLaunchedMobilePlatform && activationState.status === ActivationStatus.PENDING && diff --git a/apps/web/src/components/AnimatedDropdown/index.tsx b/apps/web/src/components/AnimatedDropdown/index.tsx index 02bc23b8bd8..ad136525b45 100644 --- a/apps/web/src/components/AnimatedDropdown/index.tsx +++ b/apps/web/src/components/AnimatedDropdown/index.tsx @@ -1,4 +1,6 @@ -import { animated, useSpring, UseSpringProps } from 'react-spring' +import { useRef } from 'react' +import { UseSpringProps, animated, easings, useSpring } from 'react-spring' +import { TRANSITION_DURATIONS } from 'theme/styles' import useResizeObserver from 'use-resize-observer' type AnimatedDropdownProps = React.PropsWithChildren<{ open: boolean; springProps?: UseSpringProps }> @@ -8,6 +10,7 @@ type AnimatedDropdownProps = React.PropsWithChildren<{ open: boolean; springProp * @returns Wrapper to smoothly hide and expand content */ export default function AnimatedDropdown({ open, springProps, children }: AnimatedDropdownProps) { + const wasOpen = useRef(open) const { ref, height } = useResizeObserver() const props = useSpring({ @@ -16,11 +19,12 @@ export default function AnimatedDropdown({ open, springProps, children }: Animat // Otherwise, we just animate between actual height (when open) and 0 (when closed). height: open ? height ?? 'auto' : 0, config: { - mass: 1.2, - tension: 300, - friction: 20, - clamp: true, - velocity: 0.01, + easing: open ? easings.easeInCubic : easings.easeOutCubic, + // Avoid animating if `open` is unchanged, so that nested AnimatedDropdowns don't stack and delay animations. + duration: open === wasOpen.current ? 0 : TRANSITION_DURATIONS.medium, + }, + onStart: () => { + wasOpen.current = open }, ...springProps, }) diff --git a/apps/web/src/components/Button/index.tsx b/apps/web/src/components/Button/index.tsx index d3f3f53f1a9..bbb955f58b4 100644 --- a/apps/web/src/components/Button/index.tsx +++ b/apps/web/src/components/Button/index.tsx @@ -460,6 +460,7 @@ function pickThemeButtonPadding({ size }: { size: ButtonSize }) { function pickThemeButtonTextColor({ theme, emphasis }: { theme: DefaultTheme; emphasis: ButtonEmphasis }) { switch (emphasis) { case ButtonEmphasis.high: + return theme.white case ButtonEmphasis.promotional: return theme.accent1 case ButtonEmphasis.highSoft: diff --git a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceButton.tsx b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceButton.tsx index 98ffe459dd3..76842ba7d69 100644 --- a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceButton.tsx +++ b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceButton.tsx @@ -75,6 +75,7 @@ export function LimitPresetPriceButton({ onSelect, }: LimitPriceButtonProps) { const { formatPercent } = useFormatter() + const sign = priceAdjustmentPercentage > 0 ? '+' : '-' return ( ) : ( - +{formatPercent(new Percent(Math.abs(priceAdjustmentPercentage), 100))} + {sign} + {formatPercent(new Percent(Math.abs(priceAdjustmentPercentage), 100))} )} diff --git a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.test.tsx b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.test.tsx index a654bff0652..836ee057ac5 100644 --- a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.test.tsx +++ b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.test.tsx @@ -1,9 +1,10 @@ import { SwapTab } from 'components/swap/constants' import { DAI, USDC_MAINNET } from 'constants/tokens' -import { Expiry, LimitContext } from 'state/limit/LimitContext' -import { SwapAndLimitContext } from 'state/swap/SwapContext' +import { LimitContext } from 'state/limit/LimitContext' import { render, screen } from 'test-utils/render' +import { Expiry } from 'state/limit/types' +import { SwapAndLimitContext } from 'state/swap/types' import { LimitPriceInputPanel } from './LimitPriceInputPanel' const mockSwapAndLimitContextValue = { diff --git a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.tsx b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.tsx index e449f54a1aa..139e1404d00 100644 --- a/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.tsx +++ b/apps/web/src/components/CurrencyInputPanel/LimitPriceInputPanel/LimitPriceInputPanel.tsx @@ -7,7 +7,6 @@ import { parseUnits } from 'ethers/lib/utils' import JSBI from 'jsbi' import { useCallback, useMemo, useState } from 'react' import { useLimitContext, useLimitPrice } from 'state/limit/LimitContext' -import { CurrencyState, useSwapAndLimitContext } from 'state/swap/SwapContext' import styled from 'styled-components' import { ClickableStyle, ThemedText } from 'theme/components' import { NumberType, useFormatter } from 'utils/formatNumbers' @@ -18,6 +17,8 @@ import PrefetchBalancesWrapper from 'components/PrefetchBalancesWrapper/Prefetch import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal' import { ReversedArrowsIcon } from 'nft/components/icons' import { LIMIT_FORM_CURRENCY_SEARCH_FILTERS } from 'pages/Swap/Limit/LimitForm' +import { useSwapAndLimitContext } from 'state/swap/hooks' +import { CurrencyState } from 'state/swap/types' import { formatCurrencySymbol } from '../utils' import { LimitCustomMarketPriceButton, LimitPresetPriceButton } from './LimitPriceButton' import { LimitPriceInputLabel } from './LimitPriceInputLabel' @@ -218,7 +219,7 @@ export function LimitPriceInputPanel({ onCurrencySelect }: LimitPriceInputPanelP if (presets.includes(currentPriceAdjustment)) { return undefined } - return limitPriceInverted ? -currentPriceAdjustment : currentPriceAdjustment + return currentPriceAdjustment })()} disabled={!baseCurrency || !quoteCurrency} selected={Boolean(currentPriceAdjustment !== undefined && !presets.includes(currentPriceAdjustment))} diff --git a/apps/web/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx b/apps/web/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx index 646d858fee4..9dad121c9ca 100644 --- a/apps/web/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx +++ b/apps/web/src/components/CurrencyInputPanel/SwapCurrencyInputPanel.tsx @@ -58,13 +58,15 @@ const Container = styled.div<{ hideInput: boolean }>` width: ${({ hideInput }) => (hideInput ? '100%' : 'initial')}; ` -export const CurrencySelect = styled(ButtonGray)<{ +interface CurrencySelectProps { visible: boolean selected: boolean hideInput?: boolean disabled?: boolean animateShake?: boolean -}>` +} + +export const CurrencySelect = styled(ButtonGray)` align-items: center; background-color: ${({ selected, theme }) => (selected ? theme.surface1 : theme.accent1)}; opacity: ${({ disabled }) => (!disabled ? 1 : 0.4)}; @@ -302,7 +304,7 @@ const SwapCurrencyInputPanel = forwardRef - + {label} diff --git a/apps/web/src/components/Dialog/Dialog.test.tsx b/apps/web/src/components/Dialog/Dialog.test.tsx index 45e06482efb..ef801e49e5e 100644 --- a/apps/web/src/components/Dialog/Dialog.test.tsx +++ b/apps/web/src/components/Dialog/Dialog.test.tsx @@ -2,6 +2,7 @@ import '@testing-library/jest-dom/extend-expect' import { Dialog, DialogButtonType } from 'components/Dialog/Dialog' import { fireEvent, render, screen } from 'test-utils/render' +import { Gap } from 'theme' const mockIcon =
Mock Icon
const mockTitle =
Mock Title
@@ -21,6 +22,7 @@ const mockButtonsConfig = { title:
Right Button
, onClick: mockRightClick, }, + gap: 'md' as Gap, } describe('', () => { diff --git a/apps/web/src/components/Dialog/Dialog.tsx b/apps/web/src/components/Dialog/Dialog.tsx index ce4aad96aba..01210f84da2 100644 --- a/apps/web/src/components/Dialog/Dialog.tsx +++ b/apps/web/src/components/Dialog/Dialog.tsx @@ -5,6 +5,7 @@ import Modal from 'components/Modal' import Row from 'components/Row' import { ReactNode } from 'react' import styled, { DefaultTheme } from 'styled-components' +import { Gap } from 'theme' import { CloseIcon, ThemedText } from 'theme/components' export const Container = styled(ColumnCenter)` @@ -51,12 +52,15 @@ const StyledButton = styled(ThemeButton)<{ $color?: keyof DefaultTheme }>` export enum DialogButtonType { Primary = 'primary', Error = 'error', + Accent = 'accent', } function getButtonEmphasis(type?: DialogButtonType) { switch (type) { case DialogButtonType.Error: return ButtonEmphasis.destructive + case DialogButtonType.Accent: + return ButtonEmphasis.high default: return ButtonEmphasis.medium } @@ -73,6 +77,7 @@ type ButtonConfig = { type ButtonsConfig = { left?: ButtonConfig right?: ButtonConfig + gap?: Gap } export interface DialogProps { @@ -99,7 +104,7 @@ export interface DialogProps { * ------------------ */ export function Dialog({ isVisible, buttonsConfig, icon, title, onCancel, description, body }: DialogProps) { - const { left, right } = buttonsConfig ?? {} + const { left, right, gap } = buttonsConfig ?? {} return ( @@ -113,7 +118,7 @@ export function Dialog({ isVisible, buttonsConfig, icon, title, onCancel, descri {description} {body} - + {left && ( - @@ -250,6 +249,7 @@ export default function FeatureFlagModal() { + diff --git a/apps/web/src/components/FeeSelector/shared.tsx b/apps/web/src/components/FeeSelector/shared.tsx index b2ca763bc30..838b9214a28 100644 --- a/apps/web/src/components/FeeSelector/shared.tsx +++ b/apps/web/src/components/FeeSelector/shared.tsx @@ -21,6 +21,7 @@ export const FEE_AMOUNT_DETAIL: Record< ChainId.POLYGON_MUMBAI, ChainId.AVALANCHE, ChainId.BASE, + ChainId.BLAST, ], }, [FeeAmount.LOW]: { diff --git a/apps/web/src/components/Logo/AssetLogo.tsx b/apps/web/src/components/Logo/AssetLogo.tsx index 35ba6eb8995..30ac8a15097 100644 --- a/apps/web/src/components/Logo/AssetLogo.tsx +++ b/apps/web/src/components/Logo/AssetLogo.tsx @@ -3,17 +3,17 @@ import { PortfolioLogo } from 'components/AccountDrawer/MiniPortfolio/PortfolioL import React from 'react' import styled from 'styled-components' -export const MissingImageLogo = styled.div<{ size?: string }>` - --size: ${({ size }) => size}; +export const MissingImageLogo = styled.div<{ $size?: string; $textColor: string; $backgroundColor: string }>` + --size: ${({ $size }) => $size}; border-radius: 100px; - color: ${({ theme }) => theme.neutral1}; - background-color: ${({ theme }) => theme.surface3}; + color: ${({ $textColor }) => $textColor}; + background-color: ${({ $backgroundColor }) => $backgroundColor}; font-size: calc(var(--size) / 3); font-weight: 535; - height: ${({ size }) => size ?? '24px'}; - line-height: ${({ size }) => size ?? '24px'}; + height: ${({ $size }) => $size ?? '24px'}; + line-height: ${({ $size }) => $size ?? '24px'}; text-align: center; - width: ${({ size }) => size ?? '24px'}; + width: ${({ $size }) => $size ?? '24px'}; display: flex; align-items: center; justify-content: center; diff --git a/apps/web/src/components/Logo/ChainLogo.tsx b/apps/web/src/components/Logo/ChainLogo.tsx index 07350508983..e3ba7f300b1 100644 --- a/apps/web/src/components/Logo/ChainLogo.tsx +++ b/apps/web/src/components/Logo/ChainLogo.tsx @@ -8,6 +8,8 @@ import { useIsDarkMode } from 'theme/components/ThemeToggle' import { ReactComponent as arbitrum } from './ChainSymbols/arbitrum.svg' import { ReactComponent as avax } from './ChainSymbols/avax.svg' import { ReactComponent as base } from './ChainSymbols/base.svg' +import { ReactComponent as blast } from './ChainSymbols/blast.svg' +import { ReactComponent as blastLight } from './ChainSymbols/blast_light.svg' import { ReactComponent as bnb } from './ChainSymbols/bnb.svg' import { ReactComponent as celo } from './ChainSymbols/celo.svg' import { ReactComponent as celoLight } from './ChainSymbols/celo_light.svg' @@ -81,6 +83,18 @@ export function getChainUI(chainId: ChainId, darkMode: boolean): ChainUI | undef bgColor: '#0052FF33', textColor: '#0052FF', } + case ChainId.BLAST: + return darkMode + ? { + Symbol: blast, + bgColor: 'rgba(252, 252, 3, 0.12)', + textColor: 'rgba(252, 252, 3, 1) ', + } + : { + Symbol: blastLight, + bgColor: 'rgba(252, 252, 3, 0.16)', + textColor: 'rgba(17, 20, 12, 1)', + } default: return undefined } diff --git a/apps/web/src/components/Logo/ChainSymbols/blast.svg b/apps/web/src/components/Logo/ChainSymbols/blast.svg new file mode 100644 index 00000000000..3c45c4c1e2b --- /dev/null +++ b/apps/web/src/components/Logo/ChainSymbols/blast.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/src/components/Logo/ChainSymbols/blast_light.svg b/apps/web/src/components/Logo/ChainSymbols/blast_light.svg new file mode 100644 index 00000000000..1c428485dae --- /dev/null +++ b/apps/web/src/components/Logo/ChainSymbols/blast_light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/web/src/components/NavBar/SearchBar.css.ts b/apps/web/src/components/NavBar/SearchBar.css.ts index 46820fa96c2..91cd7499072 100644 --- a/apps/web/src/components/NavBar/SearchBar.css.ts +++ b/apps/web/src/components/NavBar/SearchBar.css.ts @@ -1,5 +1,5 @@ import { style } from '@vanilla-extract/css' -import { subhead, subheadSmall } from 'nft/css/common.css' +import { subheadSmall } from 'nft/css/common.css' import { breakpoints, sprinkles, vars } from '../../nft/css/sprinkles.css' @@ -119,81 +119,6 @@ export const searchBarScrollable = sprinkles({ overflowY: 'auto', }) -export const suggestionRow = style([ - sprinkles({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'space-between', - paddingY: '8', - paddingX: '16', - cursor: 'pointer', - }), - { - ':hover': { - background: vars.color.lightGrayOverlay, - }, - textDecoration: 'none', - }, -]) - -export const suggestionImage = sprinkles({ - width: '36', - height: '36', - borderRadius: 'round', - marginRight: '8', -}) - -export const suggestionPrimaryContainer = style([ - sprinkles({ - alignItems: 'flex-start', - }), - { - width: '90%', - }, -]) - -export const suggestionSecondaryContainer = sprinkles({ - textAlign: 'right', - alignItems: 'flex-end', -}) - -export const primaryText = style([ - subhead, - sprinkles({ - overflow: 'hidden', - whiteSpace: 'nowrap', - textOverflow: 'ellipsis', - color: 'neutral1', - }), - { - lineHeight: '24px', - }, -]) - -export const secondaryText = style([ - subheadSmall, - sprinkles({ - color: 'neutral2', - }), - { - lineHeight: '20px', - }, -]) - -export const imageHolder = style([ - suggestionImage, - sprinkles({ - background: 'surface2', - flexShrink: '0', - }), -]) - -export const suggestionIcon = sprinkles({ - display: 'flex', - flexShrink: '0', -}) - export const sectionHeader = style([ subheadSmall, sprinkles({ diff --git a/apps/web/src/components/NavBar/SearchBarDropdown.tsx b/apps/web/src/components/NavBar/SearchBarDropdown.tsx index 49c3dd0d6f8..314bdf63159 100644 --- a/apps/web/src/components/NavBar/SearchBarDropdown.tsx +++ b/apps/web/src/components/NavBar/SearchBarDropdown.tsx @@ -15,7 +15,7 @@ import { useIsNftPage } from 'hooks/useIsNftPage' import { Box } from 'nft/components/Box' import { Column, Row } from 'nft/components/Flex' import { subheadSmall } from 'nft/css/common.css' -import { GenieCollection, TrendingCollection } from 'nft/types' +import { GenieCollection } from 'nft/types' import { useEffect, useMemo, useState } from 'react' import { useLocation } from 'react-router-dom' import styled from 'styled-components' @@ -27,11 +27,7 @@ import { SuspendConditionally } from '../Suspense/SuspendConditionally' import { SuspenseWithPreviousRenderAsFallback } from '../Suspense/SuspenseWithPreviousRenderAsFallback' import { useRecentlySearchedAssets } from './RecentlySearchedAssets' import * as styles from './SearchBar.css' -import { CollectionRow, SkeletonRow, TokenRow } from './SuggestionRow' - -function isCollection(suggestion: GenieCollection | SearchToken | TrendingCollection) { - return (suggestion as SearchToken).decimals === undefined -} +import { SkeletonRow, SuggestionRow } from './SuggestionRow' interface SearchBarDropdownSectionProps { toggleOpen: () => void @@ -66,25 +62,10 @@ const SearchBarDropdownSection = ({ {suggestions.map((suggestion, index) => isLoading || !suggestion ? ( - ) : isCollection(suggestion) ? ( - ) : ( - ` + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + text-decoration: none; + padding: 8px 16px; + :hover { + background: ${({ theme }) => theme.surface2}; + } + ${({ $isFocused, theme }) => + $isFocused && + ` + background: ${theme.surface2}; +`} +` +const StyledLink = styled(Link)` + ${SuggestionRowStyles} +` +const SkeletonSuggestionRow = styled.div` + ${SuggestionRowStyles} +` +const PrimaryContainer = styled(Column)` + align-items: flex-start; + width: 90%; +` +const CollectionImageStyles = css` + width: 36px; + height: 36px; + border-radius: 9999px; + background: ${({ theme }) => theme.surface3}; + flex-shrink: 0; +` +const CollectionImage = styled.img` + ${CollectionImageStyles} +` +const BrokenCollectionImage = styled.div` + ${CollectionImageStyles} +` +const PrimaryText = styled(ThemedText.SubHeader)` + ${EllipsisStyle} +` +const SecondaryContainer = styled(Column)` + text-align: right; + align-items: flex-end; +` -interface CollectionRowProps { - collection: GenieCollection +interface SuggestionRowProps { + suggestion: GenieCollection | SearchToken isHovered: boolean setHoveredIndex: (index: number | undefined) => void toggleOpen: () => void @@ -39,33 +84,43 @@ interface CollectionRowProps { eventProperties: Record } -export const CollectionRow = ({ - collection, +function suggestionIsToken(suggestion: GenieCollection | SearchToken): suggestion is SearchToken { + return (suggestion as SearchToken).decimals !== undefined +} + +export const SuggestionRow = ({ + suggestion, isHovered, setHoveredIndex, toggleOpen, index, eventProperties, -}: CollectionRowProps) => { - const { formatNumberOrString } = useFormatter() - - const [brokenImage, setBrokenImage] = useState(false) - const [loaded, setLoaded] = useState(false) - +}: SuggestionRowProps) => { + const isToken = suggestionIsToken(suggestion) const addRecentlySearchedAsset = useAddRecentlySearchedAsset() const navigate = useNavigate() + const { formatFiatPrice, formatDelta, formatNumberOrString } = useFormatter() + const [brokenCollectionImage, setBrokenCollectionImage] = useState(false) const handleClick = useCallback(() => { - addRecentlySearchedAsset({ ...collection, isNft: true, chain: Chain.Ethereum }) + const address = + !suggestion.address && suggestion.standard === TokenStandard.Native ? NATIVE_CHAIN_ID : suggestion.address + const asset = isToken + ? address && { address, chain: suggestion.chain } + : { ...suggestion, isNft: true, chain: Chain.Ethereum } + asset && addRecentlySearchedAsset(asset) + toggleOpen() sendAnalyticsEvent(InterfaceEventName.NAVBAR_RESULT_SELECTED, { ...eventProperties }) - }, [addRecentlySearchedAsset, collection, toggleOpen, eventProperties]) + }, [suggestion, isToken, addRecentlySearchedAsset, toggleOpen, eventProperties]) + const path = isToken ? getTokenDetailsURL({ ...suggestion }) : `/nfts/collection/${suggestion.address}` + // Close the modal on escape useEffect(() => { const keyDownHandler = (event: KeyboardEvent) => { if (event.key === 'Enter' && isHovered) { event.preventDefault() - navigate(`/nfts/collection/${collection.address}`) + navigate(path) handleClick() } } @@ -73,157 +128,107 @@ export const CollectionRow = ({ return () => { document.removeEventListener('keydown', keyDownHandler) } - }, [toggleOpen, isHovered, collection, navigate, handleClick]) + }, [toggleOpen, isHovered, suggestion, navigate, handleClick, path]) return ( - !isHovered && setHoveredIndex(index)} onMouseLeave={() => isHovered && setHoveredIndex(undefined)} - className={styles.suggestionRow} - style={{ background: isHovered ? vars.color.lightGrayOverlay : 'none' }} + data-testid={isToken ? `searchbar-token-row-${suggestion.chain}-${suggestion.address}` : ''} > - - {!brokenImage && collection.imageUrl ? ( - setBrokenImage(true)} - onLoad={() => setLoaded(true)} + + {isToken ? ( + + ) : brokenCollectionImage ? ( + ) : ( - + setBrokenCollectionImage(true)} + /> )} - - - {collection.name} - {collection.isVerified && } - - - {formatNumberOrString({ input: collection?.stats?.total_supply, type: NumberType.WholeNumber })} items - - - - {collection.stats?.floor_price ? ( - - - - {formatNumberOrString({ input: collection.stats?.floor_price, type: NumberType.NFTToken })} ETH - - - Floor - - ) : null} - - ) -} - -interface TokenRowProps { - token: SearchToken - isHovered: boolean - setHoveredIndex: (index: number | undefined) => void - toggleOpen: () => void - index: number - eventProperties: Record -} - -export const TokenRow = ({ token, isHovered, setHoveredIndex, toggleOpen, index, eventProperties }: TokenRowProps) => { - const addRecentlySearchedAsset = useAddRecentlySearchedAsset() - const navigate = useNavigate() - const { formatFiatPrice, formatDelta } = useFormatter() - - const handleClick = useCallback(() => { - const address = !token.address && token.standard === TokenStandard.Native ? NATIVE_CHAIN_ID : token.address - address && addRecentlySearchedAsset({ address, chain: token.chain }) - - toggleOpen() - sendAnalyticsEvent(InterfaceEventName.NAVBAR_RESULT_SELECTED, { ...eventProperties }) - }, [addRecentlySearchedAsset, token, toggleOpen, eventProperties]) - - const tokenDetailsPath = getTokenDetailsURL({ ...token }) - // Close the modal on escape - useEffect(() => { - const keyDownHandler = (event: KeyboardEvent) => { - if (event.key === 'Enter' && isHovered) { - event.preventDefault() - navigate(tokenDetailsPath) - handleClick() - } - } - document.addEventListener('keydown', keyDownHandler) - return () => { - document.removeEventListener('keydown', keyDownHandler) - } - }, [toggleOpen, isHovered, token, navigate, handleClick, tokenDetailsPath]) - - return ( - !isHovered && setHoveredIndex(index)} - onMouseLeave={() => isHovered && setHoveredIndex(undefined)} - className={styles.suggestionRow} - style={{ background: isHovered ? vars.color.lightGrayOverlay : 'none' }} - > - - - - - {token.name} - + + + {suggestion.name} + {isToken ? ( + + ) : ( + suggestion.isVerified && + )} - {token.symbol} - + + {isToken ? ( + suggestion.symbol + ) : ( + <> + {formatNumberOrString({ input: suggestion?.stats?.total_supply, type: NumberType.WholeNumber })}  + items + + )} + + - - {!!token.market?.price?.value && ( - <> - - {formatFiatPrice({ price: token.market.price.value })} - - - + + + + {isToken + ? formatFiatPrice({ price: suggestion.market?.price?.value }) + : `${formatNumberOrString({ input: suggestion.stats?.floor_price, type: NumberType.NFTToken })} ETH`} + + + + + {isToken ? ( + <> + - - {formatDelta(Math.abs(token.market?.pricePercentChange?.value ?? 0))} + + {formatDelta(Math.abs(suggestion.market?.pricePercentChange?.value ?? 0))} - - - )} - - + + ) : ( + + Floor + + )} + + + ) } +const SkeletonContent = styled(Column)` + width: 100%; +` + export const SkeletonRow = () => { return ( - - - - - - - + + + + + + + - - - + + + - + - + ) } diff --git a/apps/web/src/components/NavBar/__snapshots__/ChainSelectorRow.test.tsx.snap b/apps/web/src/components/NavBar/__snapshots__/ChainSelectorRow.test.tsx.snap index 4f6c9ae1baf..08657853ddc 100644 --- a/apps/web/src/components/NavBar/__snapshots__/ChainSelectorRow.test.tsx.snap +++ b/apps/web/src/components/NavBar/__snapshots__/ChainSelectorRow.test.tsx.snap @@ -352,6 +352,76 @@ exports[`ChainSelectorRow should match snapshot for chainId 10 1`] = `
`; +exports[`ChainSelectorRow should match snapshot for chainId 30 1`] = ` +.c0 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + background: none; + border: none; + border-radius: 12px; + color: #222222; + cursor: pointer; + display: grid; + grid-template-columns: min-content 1fr min-content; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + line-height: 20px; + opacity: 1; + padding: 10px 8px; + text-align: left; + -webkit-transition: 250ms ease background-color; + transition: 250ms ease background-color; + width: 240px; +} + +.c0:hover { + background-color: #22222212; +} + +.c1 { + grid-column: 3; + grid-row: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 20px; +} + +@media only screen and (max-width:396px) { + .c0 { + width: 100%; + } +} + +
+ + + + + +
+`; + exports[`ChainSelectorRow should match snapshot for chainId 56 1`] = ` .c0 { -webkit-align-items: center; @@ -1360,6 +1430,118 @@ exports[`ChainSelectorRow should match snapshot for chainId 80001 1`] = ` `; +exports[`ChainSelectorRow should match snapshot for chainId 81457 1`] = ` +.c0 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + background: none; + border: none; + border-radius: 12px; + color: #222222; + cursor: pointer; + display: grid; + grid-template-columns: min-content 1fr min-content; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + line-height: 20px; + opacity: 1; + padding: 10px 8px; + text-align: left; + -webkit-transition: 250ms ease background-color; + transition: 250ms ease background-color; + width: 240px; +} + +.c0:hover { + background-color: #22222212; +} + +.c1 { + grid-column: 2; + grid-row: 1; + font-size: 16px; + font-weight: 485; +} + +.c2 { + grid-column: 3; + grid-row: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 20px; +} + +@media only screen and (max-width:396px) { + .c0 { + width: 100%; + } +} + +
+ + + + + +
+`; + exports[`ChainSelectorRow should match snapshot for chainId 84531 1`] = ` .c0 { -webkit-align-items: center; @@ -1612,6 +1794,76 @@ exports[`ChainSelectorRow should match snapshot for chainId 421614 1`] = ` `; +exports[`ChainSelectorRow should match snapshot for chainId 7777777 1`] = ` +.c0 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + background: none; + border: none; + border-radius: 12px; + color: #222222; + cursor: pointer; + display: grid; + grid-template-columns: min-content 1fr min-content; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + line-height: 20px; + opacity: 1; + padding: 10px 8px; + text-align: left; + -webkit-transition: 250ms ease background-color; + transition: 250ms ease background-color; + width: 240px; +} + +.c0:hover { + background-color: #22222212; +} + +.c1 { + grid-column: 3; + grid-row: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 20px; +} + +@media only screen and (max-width:396px) { + .c0 { + width: 100%; + } +} + +
+ + + + + +
+`; + exports[`ChainSelectorRow should match snapshot for chainId 11155111 1`] = ` .c0 { -webkit-align-items: center; @@ -1793,3 +2045,73 @@ exports[`ChainSelectorRow should match snapshot for chainId 11155420 1`] = ` `; + +exports[`ChainSelectorRow should match snapshot for chainId 999999999 1`] = ` +.c0 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + background: none; + border: none; + border-radius: 12px; + color: #222222; + cursor: pointer; + display: grid; + grid-template-columns: min-content 1fr min-content; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + line-height: 20px; + opacity: 1; + padding: 10px 8px; + text-align: left; + -webkit-transition: 250ms ease background-color; + transition: 250ms ease background-color; + width: 240px; +} + +.c0:hover { + background-color: #22222212; +} + +.c1 { + grid-column: 3; + grid-row: 1; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + width: 20px; +} + +@media only screen and (max-width:396px) { + .c0 { + width: 100%; + } +} + +
+ + + + + +
+`; diff --git a/apps/web/src/components/NavBar/__snapshots__/SearchBar.test.tsx.snap b/apps/web/src/components/NavBar/__snapshots__/SearchBar.test.tsx.snap index 96bffcbe8f8..cb5480f8635 100644 --- a/apps/web/src/components/NavBar/__snapshots__/SearchBar.test.tsx.snap +++ b/apps/web/src/components/NavBar/__snapshots__/SearchBar.test.tsx.snap @@ -39,7 +39,7 @@ exports[`disable nft on searchbar should render text with nfts 1`] = ` class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44r sprinkles_flexDirection_row_sm__rgw6ez46x sprinkles_alignItems_center_sm__rgw6ez3x sprinkles_borderRadius_16_default__rgw6ez815 sprinkles_borderBottomWidth_1px_default__rgw6ez89x sprinkles_backgroundColor_surface1_default__rgw6ez6rr sprinkles_gap_12_sm__rgw6ez3tf SearchBar_nftSearchBar__1fbf9sz9 SearchBar_baseSearchNftStyle__1fbf9sz2 SearchBar_baseSearchStyle__1fbf9sz1 SearchBar__1fbf9sz0 sprinkles_paddingTop_8_sm__rgw6ez2of sprinkles_paddingBottom_8_sm__rgw6ez27x sprinkles_width_viewWidth_sm__rgw6ez17l sprinkles_borderStyle_solid_default__rgw6ez7zp sprinkles_borderWidth_1px_default__rgw6ez895 sprinkles_borderColor_surface3_default__rgw6ez55f SearchBar__1fbf9sz8 sprinkles_paddingLeft_12_sm__rgw6ez2dr sprinkles_paddingRight_12_sm__rgw6ez2j9 sprinkles_color_neutral2_default__rgw6ez4bf common_magicalGradientOnHover__127l8hdb common_magicalGradient__127l8hda" >
@@ -137,7 +137,7 @@ exports[`disable nft on searchbar should render text without nfts 1`] = ` class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44r sprinkles_flexDirection_row_sm__rgw6ez46x sprinkles_alignItems_center_sm__rgw6ez3x sprinkles_borderRadius_16_default__rgw6ez815 sprinkles_borderBottomWidth_1px_default__rgw6ez89x sprinkles_backgroundColor_surface1_default__rgw6ez6rr sprinkles_gap_12_sm__rgw6ez3tf SearchBar_nftSearchBar__1fbf9sz9 SearchBar_baseSearchNftStyle__1fbf9sz2 SearchBar_baseSearchStyle__1fbf9sz1 SearchBar__1fbf9sz0 sprinkles_paddingTop_8_sm__rgw6ez2of sprinkles_paddingBottom_8_sm__rgw6ez27x sprinkles_width_viewWidth_sm__rgw6ez17l sprinkles_borderStyle_solid_default__rgw6ez7zp sprinkles_borderWidth_1px_default__rgw6ez895 sprinkles_borderColor_surface3_default__rgw6ez55f SearchBar__1fbf9sz8 sprinkles_paddingLeft_12_sm__rgw6ez2dr sprinkles_paddingRight_12_sm__rgw6ez2j9 sprinkles_color_neutral2_default__rgw6ez4bf common_magicalGradientOnHover__127l8hdb common_magicalGradient__127l8hda" >
diff --git a/apps/web/src/components/NavBar/__snapshots__/SearchBarDropdown.test.tsx.snap b/apps/web/src/components/NavBar/__snapshots__/SearchBarDropdown.test.tsx.snap index b741d79be51..06beae6eb54 100644 --- a/apps/web/src/components/NavBar/__snapshots__/SearchBarDropdown.test.tsx.snap +++ b/apps/web/src/components/NavBar/__snapshots__/SearchBarDropdown.test.tsx.snap @@ -1,6 +1,160 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`disable nft on searchbar dropdown should not render popular nft collections 1`] = ` +.c1 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c2 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c6 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.c4 { + 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: 8px; +} + +.c7 { + border-radius: 12px; + border-radius: 12px; + height: 20px; + width: 50%; + width: 180px; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c8 { + border-radius: 12px; + border-radius: 12px; + height: 20px; + width: 50%; + width: 48px; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c9 { + border-radius: 12px; + border-radius: 12px; + height: 16px; + width: 50%; + width: 120px; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c10 { + border-radius: 12px; + border-radius: 12px; + height: 16px; + width: 50%; + width: 48px; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-text-decoration: none; + text-decoration: none; + padding: 8px 16px; +} + +.c0:hover { + background: #F9F9F9; +} + +.c3 { + width: 36px; + height: 36px; + border-radius: 9999px; + background: #22222212; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.c5 { + width: 100%; +} +
@@ -129,6 +297,160 @@ exports[`disable nft on searchbar dropdown should not render popular nft collect `; exports[`disable nft on searchbar dropdown should render popular nft collections 1`] = ` +.c1 { + box-sizing: border-box; + margin: 0; + min-width: 0; +} + +.c2 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: start; + -webkit-justify-content: flex-start; + -ms-flex-pack: start; + justify-content: flex-start; +} + +.c6 { + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 0; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; +} + +.c4 { + 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: 8px; +} + +.c7 { + border-radius: 12px; + border-radius: 12px; + height: 20px; + width: 50%; + width: 180px; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c8 { + border-radius: 12px; + border-radius: 12px; + height: 20px; + width: 50%; + width: 48px; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c9 { + border-radius: 12px; + border-radius: 12px; + height: 16px; + width: 50%; + width: 120px; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c10 { + border-radius: 12px; + border-radius: 12px; + height: 16px; + width: 50%; + width: 48px; + -webkit-animation: fAQEyV 1.5s infinite; + animation: fAQEyV 1.5s infinite; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; + background: linear-gradient( to left, #22222212 25%, rgba(53,53,53,0.07) 50%, #22222212 75% ); + will-change: background-position; + background-size: 400%; +} + +.c0 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-text-decoration: none; + text-decoration: none; + padding: 8px 16px; +} + +.c0:hover { + background: #F9F9F9; +} + +.c3 { + width: 36px; + height: 36px; + border-radius: 9999px; + background: #22222212; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.c5 { + width: 100%; +} +
@@ -278,72 +614,86 @@ exports[`disable nft on searchbar dropdown should render popular nft collections class="reset_base__1klryar0 sprinkles_display_flex_sm__rgw6ez44r sprinkles_flexDirection_column_sm__rgw6ez473 sprinkles_gap_4_sm__rgw6ez3sr" >
diff --git a/apps/web/src/components/Pools/PoolDetails/__snapshots__/PoolDetailsStatsButtons.test.tsx.snap b/apps/web/src/components/Pools/PoolDetails/__snapshots__/PoolDetailsStatsButtons.test.tsx.snap index 848aad74bf3..6a584c559f8 100644 --- a/apps/web/src/components/Pools/PoolDetails/__snapshots__/PoolDetailsStatsButtons.test.tsx.snap +++ b/apps/web/src/components/Pools/PoolDetails/__snapshots__/PoolDetailsStatsButtons.test.tsx.snap @@ -875,7 +875,7 @@ exports[`PoolDetailsStatsButton renders both buttons correctly 1`] = ` } .c58 { - color: #FC72FF; + color: #222222; font-weight: 535; } @@ -1201,7 +1201,7 @@ exports[`PoolDetailsStatsButton renders both buttons correctly 1`] = ` class="c27" > You pay @@ -1330,7 +1330,7 @@ exports[`PoolDetailsStatsButton renders both buttons correctly 1`] = ` class="c27" > You receive diff --git a/apps/web/src/components/PositionPreview/index.tsx b/apps/web/src/components/PositionPreview/index.tsx index 337929899b4..362559c5797 100644 --- a/apps/web/src/components/PositionPreview/index.tsx +++ b/apps/web/src/components/PositionPreview/index.tsx @@ -11,6 +11,7 @@ import RateToggle from 'components/RateToggle' import { RowBetween, RowFixed } from 'components/Row' import { BIPS_BASE } from 'constants/misc' import JSBI from 'jsbi' +import { BlastRebasingAlert } from 'pages/AddLiquidity/blastAlerts' import { ReactNode, useCallback, useState } from 'react' import { Bound } from 'state/mint/v3/actions' import { useTheme } from 'styled-components' @@ -24,12 +25,14 @@ export const PositionPreview = ({ inRange, baseCurrencyDefault, ticksAtLimit, + showBlastAlert, }: { position: Position title?: ReactNode inRange: boolean baseCurrencyDefault?: Currency ticksAtLimit: { [bound: string]: boolean | undefined } + showBlastAlert?: boolean }) => { const theme = useTheme() const { formatCurrencyAmount, formatDelta, formatPrice, formatTickPrice } = useFormatter() @@ -78,7 +81,7 @@ export const PositionPreview = ({ - + {showBlastAlert && } @@ -114,7 +117,6 @@ export const PositionPreview = ({ - {title ? {title} :
} diff --git a/apps/web/src/components/SearchModal/CurrencySearchModal.tsx b/apps/web/src/components/SearchModal/CurrencySearchModal.tsx index 88857c8ba12..c8a68f81602 100644 --- a/apps/web/src/components/SearchModal/CurrencySearchModal.tsx +++ b/apps/web/src/components/SearchModal/CurrencySearchModal.tsx @@ -1,8 +1,8 @@ import { Currency, Token } from '@uniswap/sdk-core' import TokenSafety from 'components/TokenSafety' import { memo, useCallback, useEffect, useState } from 'react' -import { useUserAddedTokens } from 'state/user/hooks' +import { useUserAddedTokens } from 'state/user/userAddedTokens' import useLast from '../../hooks/useLast' import { useWindowSize } from '../../hooks/useWindowSize' import Modal from '../Modal' diff --git a/apps/web/src/components/SearchModal/useCurrencySearchResults.ts b/apps/web/src/components/SearchModal/useCurrencySearchResults.ts index 97ee98823a6..9c81d3d87fc 100644 --- a/apps/web/src/components/SearchModal/useCurrencySearchResults.ts +++ b/apps/web/src/components/SearchModal/useCurrencySearchResults.ts @@ -3,7 +3,6 @@ import { ChainId, Currency } from '@uniswap/sdk-core' import { useWeb3React } from '@web3-react/core' import { CurrencyListRow, CurrencyListSectionTitle } from 'components/SearchModal/CurrencyList' import { CurrencySearchFilters } from 'components/SearchModal/CurrencySearch' -import { useSearchTokens } from 'graphql/data/SearchTokens' import { gqlTokenToCurrencyInfo } from 'graphql/data/types' import { chainIdToBackendName } from 'graphql/data/util' import { useDefaultActiveTokens, useSearchInactiveTokenLists, useTokenListToken } from 'hooks/Tokens' @@ -11,12 +10,16 @@ import { useTokenBalances } from 'hooks/useTokenBalances' import { getTokenFilter } from 'lib/hooks/useTokenList/filtering' import { getSortedPortfolioTokens, tokenQuerySortComparator } from 'lib/hooks/useTokenList/sorting' import { useMemo } from 'react' +import { useUserAddedTokens } from 'state/user/userAddedTokens' import { UserAddedToken } from 'types/tokens' import { + Chain, Token as GqlToken, TokenSortableField, - useSearchPopularTokensWebQuery, + useSearchTokensWebQuery, + useTopTokensQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { toGraphQLChain } from 'uniswap/src/features/chains/utils' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' @@ -56,14 +59,19 @@ export function useCurrencySearchResults({ /** * GraphQL queries for tokens and search results */ - const { data: searchResults, loading: searchResultsLoading } = useSearchTokens( - gqlTokenListsEnabled ? searchQuery : undefined, // skip if gql token lists are disabled - chainId ?? ChainId.MAINNET - ) - const { data: popularTokens, loading: popularTokensLoading } = useSearchPopularTokensWebQuery({ + const { data: searchResults, loading: searchResultsLoading } = useSearchTokensWebQuery({ + variables: { + searchQuery: searchQuery ?? '', + chains: [chainIdToBackendName(chainId) ?? Chain.Ethereum], + }, + skip: !searchQuery || !gqlTokenListsEnabled, + }) + const { data: popularTokens, loading: popularTokensLoading } = useTopTokensQuery({ variables: { - chain: chainIdToBackendName(chainId), + chain: toGraphQLChain(chainId ?? ChainId.MAINNET) ?? undefined, orderBy: TokenSortableField.Popularity, + page: 1, + pageSize: 100, }, skip: !gqlTokenListsEnabled, }) @@ -76,6 +84,7 @@ export function useCurrencySearchResults({ // Queries for a single token directly by address, if the query is an address. const searchToken = useTokenListToken(searchQuery) const defaultAndUserAddedTokens = useDefaultActiveTokens(chainId) + const userAddedTokens = useUserAddedTokens() /** * Results processing: sorting, filtering, and merging data sources into the final list. @@ -85,9 +94,15 @@ export function useCurrencySearchResults({ if (!gqlTokenListsEnabled) { return Object.values(defaultAndUserAddedTokens) } else if (!isEmpty(searchQuery)) { - return (searchResults?.map(gqlCurrencyMapper).filter(Boolean) as Currency[]) ?? [] + return [ + ...((searchResults?.searchTokens?.map(gqlCurrencyMapper).filter(Boolean) as Currency[]) ?? []), + ...userAddedTokens, + ] } else { - return (popularTokens?.topTokens?.map(gqlCurrencyMapper).filter(Boolean) as Currency[]) ?? [] + return [ + ...((popularTokens?.topTokens?.map(gqlCurrencyMapper).filter(Boolean) as Currency[]) ?? []), + ...userAddedTokens, + ] } })() @@ -163,14 +178,15 @@ export function useCurrencySearchResults({ portfolioTokens: portfolioTokens.filter(currencyFilter), } }, [ + gqlTokenListsEnabled, + searchQuery, balancesLoading, balanceList, balanceMap, chainId, - searchQuery, - gqlTokenListsEnabled, defaultAndUserAddedTokens, searchResults, + userAddedTokens, popularTokens?.topTokens, filters?.onlyShowCurrenciesWithBalance, filters?.disableNonToken, diff --git a/apps/web/src/components/TokenSafety/TokenSafetyMessage.tsx b/apps/web/src/components/TokenSafety/TokenSafetyMessage.tsx index 316c871cb1f..ebce156129c 100644 --- a/apps/web/src/components/TokenSafety/TokenSafetyMessage.tsx +++ b/apps/web/src/components/TokenSafety/TokenSafetyMessage.tsx @@ -36,8 +36,7 @@ const DetailsRow = styled.div` ` const StyledLink = styled(ExternalLink)` - color: ${({ theme }) => theme.accent1}; - + color: ${({ theme }) => theme.neutral1}; font-weight: 535; ` diff --git a/apps/web/src/components/Tokens/TokenDetails/index.tsx b/apps/web/src/components/Tokens/TokenDetails/index.tsx index bd425a6545d..56b8b9ad71f 100644 --- a/apps/web/src/components/Tokens/TokenDetails/index.tsx +++ b/apps/web/src/components/Tokens/TokenDetails/index.tsx @@ -20,7 +20,7 @@ import { useTDPContext } from 'pages/TokenDetails/TDPContext' import { PropsWithChildren, useCallback, useMemo, useState } from 'react' import { ChevronRight } from 'react-feather' import { useNavigate } from 'react-router-dom' -import { CurrencyState } from 'state/swap/SwapContext' +import { CurrencyState } from 'state/swap/types' import styled from 'styled-components' import { addressesAreEquivalent } from 'utils/addressesAreEquivalent' import { ActivitySection } from './ActivitySection' diff --git a/apps/web/src/components/WalletModal/index.tsx b/apps/web/src/components/WalletModal/index.tsx index d250eda4d89..384b82bf777 100644 --- a/apps/web/src/components/WalletModal/index.tsx +++ b/apps/web/src/components/WalletModal/index.tsx @@ -5,15 +5,13 @@ import { useShowMoonpayText } from 'components/AccountDrawer/MiniPortfolio/hooks import Column from 'components/Column' import { Settings } from 'components/Icons/Settings' import Row, { AutoRow } from 'components/Row' -import { deprecatedNetworkConnection, networkConnection } from 'connection' +import { networkConnection } from 'connection' import { ActivationStatus, useActivationState } from 'connection/activate' import { isSupportedChain } from 'constants/chains' import { useEffect } from 'react' import styled from 'styled-components' import { ThemedText } from 'theme/components' import { flexColumnNoWrap } from 'theme/styles' -import { FeatureFlags } from 'uniswap/src/features/experiments/flags' -import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' import ConnectionErrorView from './ConnectionErrorView' import { DeprecatedInjectorMessage } from './Option' import PrivacyPolicyNotice from './PrivacyPolicyNotice' @@ -52,17 +50,12 @@ export default function WalletModal({ openSettings }: { openSettings: () => void const showMoonpayText = useShowMoonpayText() const { activationState } = useActivationState() - const fallbackProviderEnabled = useFeatureFlag(FeatureFlags.FallbackProvider) // Keep the network connector in sync with any active user connector to prevent chain-switching on wallet disconnection. useEffect(() => { if (chainId && isSupportedChain(chainId) && connector !== networkConnection.connector) { - if (fallbackProviderEnabled) { - networkConnection.connector.activate(chainId) - } else { - deprecatedNetworkConnection.connector.activate(chainId) - } + networkConnection.connector.activate(chainId) } - }, [chainId, connector, fallbackProviderEnabled]) + }, [chainId, connector]) const { orderedConnections, showDeprecatedMessage } = useOrderedConnections() diff --git a/apps/web/src/components/Web3Provider/index.tsx b/apps/web/src/components/Web3Provider/index.tsx index 1a1c176ce25..5d3ee96abd8 100644 --- a/apps/web/src/components/Web3Provider/index.tsx +++ b/apps/web/src/components/Web3Provider/index.tsx @@ -4,7 +4,7 @@ import { Connector } from '@web3-react/types' import { sendAnalyticsEvent, useTrace, user } from 'analytics' import { connections, getConnection } from 'connection' import { isSupportedChain } from 'constants/chains' -import { useNetworkProviders } from 'hooks/useNetworkProviders' +import { RPC_PROVIDERS } from 'constants/providers' import usePrevious from 'hooks/usePrevious' import { ReactNode, useEffect } from 'react' import { useLocation } from 'react-router-dom' @@ -31,11 +31,9 @@ function Updater() { const { pathname } = useLocation() const currentPage = getCurrentPageFromLocation(pathname) const analyticsContext = useTrace() - - const providers = useNetworkProviders() + const networkProvider = isSupportedChain(chainId) ? RPC_PROVIDERS[chainId] : undefined // Trace RPC calls (for debugging). - const networkProvider = isSupportedChain(chainId) ? providers[chainId] : undefined const shouldTrace = useFeatureFlag(FeatureFlags.TraceJsonRpc) useEffect(() => { if (shouldTrace) { diff --git a/apps/web/src/components/addLiquidity/OutOfSyncWarning.tsx b/apps/web/src/components/addLiquidity/OutOfSyncWarning.tsx new file mode 100644 index 00000000000..ebeb97189f6 --- /dev/null +++ b/apps/web/src/components/addLiquidity/OutOfSyncWarning.tsx @@ -0,0 +1,49 @@ +import { Trans } from '@lingui/macro' +import Column from 'components/Column' +import Row from 'components/Row' +import styled from 'styled-components' +import { Icons, Text } from 'ui/src' +import { iconSizes } from 'ui/src/theme' + +const Container = styled.div` + height: 100%; + width: 100%; + max-width: 550px; + padding: 12px; + border-radius: 20px; + border: 1px solid ${({ theme }) => theme.surface3}; +` +const StyledColumn = styled(Column)` + height: 100%; +` +const IconContainer = styled.div` + height: 40px; + width: 40px; + padding: 10px; + border-radius: 12px; + background: ${({ theme }) => theme.surface3}; +` +export function OutOfSyncWarning() { + return ( + + + + + + + + + + Pool out of sync + + + + This pool is out of sync with market prices. Adding liquidity at the suggested ratios may result in loss + of funds. + + + + + + ) +} diff --git a/apps/web/src/components/swap/SwapHeader.test.tsx b/apps/web/src/components/swap/SwapHeader.test.tsx index 16e718c65c1..3c6043db263 100644 --- a/apps/web/src/components/swap/SwapHeader.test.tsx +++ b/apps/web/src/components/swap/SwapHeader.test.tsx @@ -1,6 +1,6 @@ import { ChainId } from '@uniswap/sdk-core' import { Dispatch, PropsWithChildren, SetStateAction } from 'react' -import { CurrencyState, EMPTY_DERIVED_SWAP_INFO, SwapAndLimitContext, SwapContext } from 'state/swap/SwapContext' +import { CurrencyState, EMPTY_DERIVED_SWAP_INFO, SwapAndLimitContext, SwapContext } from 'state/swap/types' import { mocked } from 'test-utils/mocked' import { act, render, screen } from 'test-utils/render' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' diff --git a/apps/web/src/components/swap/SwapHeader.tsx b/apps/web/src/components/swap/SwapHeader.tsx index 58b6f47cd1b..8a2d31e052c 100644 --- a/apps/web/src/components/swap/SwapHeader.tsx +++ b/apps/web/src/components/swap/SwapHeader.tsx @@ -1,13 +1,14 @@ import { Trans } from '@lingui/macro' import { ChainId } from '@uniswap/sdk-core' +import { useSwapAndLimitContext, useSwapContext } from 'state/swap/hooks' +import styled from 'styled-components' +import { isIFramed } from 'utils/isIFramed' + import { sendAnalyticsEvent } from 'analytics' import { useCallback, useEffect } from 'react' import { useLocation, useNavigate } from 'react-router-dom' -import { useSwapAndLimitContext, useSwapContext } from 'state/swap/SwapContext' -import styled from 'styled-components' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' -import { isIFramed } from 'utils/isIFramed' import { RowBetween, RowFixed } from '../Row' import SettingsTab from '../Settings' import SwapBuyFiatButton from './SwapBuyFiatButton' diff --git a/apps/web/src/components/swap/SwapLineItem.tsx b/apps/web/src/components/swap/SwapLineItem.tsx index 0dd0785ff59..6d45244e63e 100644 --- a/apps/web/src/components/swap/SwapLineItem.tsx +++ b/apps/web/src/components/swap/SwapLineItem.tsx @@ -43,11 +43,13 @@ const ColorWrapper = styled.span<{ textColor?: keyof DefaultTheme }>` ` const AutoBadge = styled(ThemedText.LabelMicro).attrs({ fontWeight: 535 })` + display: flex; background: ${({ theme }) => theme.surface3}; border-radius: 8px; color: ${({ theme }) => theme.neutral2}; height: 20px; padding: 0 6px; + align-items: center; ::after { content: '${t`Auto`}'; 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 f844ccc40fd..180d01c34ff 100644 --- a/apps/web/src/components/swap/__snapshots__/SwapDetails.test.tsx.snap +++ b/apps/web/src/components/swap/__snapshots__/SwapDetails.test.tsx.snap @@ -298,11 +298,19 @@ exports[`SwapDetails.tsx matches base snapshot, test trade exact input 1`] = ` } .c15 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c15::after { @@ -945,11 +953,19 @@ exports[`SwapDetails.tsx renders a preview trade while disabling submission 1`] } .c17 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c17::after { 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 2f68ea170ef..97ce20d5cab 100644 --- a/apps/web/src/components/swap/__snapshots__/SwapDetailsDropdown.test.tsx.snap +++ b/apps/web/src/components/swap/__snapshots__/SwapDetailsDropdown.test.tsx.snap @@ -198,11 +198,19 @@ exports[`SwapDetailsDropdown.tsx renders a trade 1`] = ` } .c21 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c21::after { diff --git a/apps/web/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap b/apps/web/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap index e88c7931ab1..8570176ffee 100644 --- a/apps/web/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap +++ b/apps/web/src/components/swap/__snapshots__/SwapLineItem.test.tsx.snap @@ -271,11 +271,19 @@ exports[`SwapLineItem.tsx dutch order eth input 1`] = ` } .c18 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c18::after { @@ -1219,11 +1227,19 @@ exports[`SwapLineItem.tsx exact input 1`] = ` } .c17 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c17::after { @@ -2261,11 +2277,19 @@ exports[`SwapLineItem.tsx exact input api 1`] = ` } .c17 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c17::after { @@ -3303,11 +3327,19 @@ exports[`SwapLineItem.tsx exact output 1`] = ` } .c17 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c17::after { @@ -4345,11 +4377,19 @@ exports[`SwapLineItem.tsx fee on buy 1`] = ` } .c17 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c17::after { @@ -5440,11 +5480,19 @@ exports[`SwapLineItem.tsx fee on sell 1`] = ` } .c17 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c17::after { @@ -6266,11 +6314,19 @@ exports[`SwapLineItem.tsx preview exact in 1`] = ` } .c14 { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; background: #22222212; border-radius: 8px; color: #7D7D7D; height: 20px; padding: 0 6px; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .c14::after { diff --git a/apps/web/src/connection/WalletConnectV2.ts b/apps/web/src/connection/WalletConnectV2.ts index 98d8cc1bdcd..1782a27229c 100644 --- a/apps/web/src/connection/WalletConnectV2.ts +++ b/apps/web/src/connection/WalletConnectV2.ts @@ -4,7 +4,7 @@ import { sendAnalyticsEvent } from 'analytics' import { L1_CHAIN_IDS, L2_CHAIN_IDS } from 'constants/chains' import { APP_RPC_URLS } from 'constants/networks' import { Z_INDEX } from 'theme/zIndex' -import { isAndroid, isIOS } from 'utils/platform' +import { isWebAndroid, isWebIOS } from 'uniswap/src/utils/platform' // Avoid testing for the best URL by only passing a single URL per chain. // Otherwise, WC will not initialize until all URLs have been tested (see getBestUrl in web3-react). @@ -88,7 +88,7 @@ export class UniwalletConnect extends WalletConnectV2 { this.events.emit(UniwalletConnect.UNI_URI_AVAILABLE, `https://uniswap.org/app/wc?uri=${uri}`) // Opens deeplink to Uniswap Wallet if on iOS - if (isIOS || isAndroid) { + if (isWebIOS || isWebAndroid) { // Using window.location.href to open the deep link ensures smooth navigation and leverages OS handling for installed apps, // avoiding potential popup blockers or inconsistent behavior associated with window.open window.location.href = `uniswap://wc?uri=${encodeURIComponent(uri)}` diff --git a/apps/web/src/connection/eagerlyConnect.ts b/apps/web/src/connection/eagerlyConnect.ts index 2b8bd77855d..4eb23edf9d7 100644 --- a/apps/web/src/connection/eagerlyConnect.ts +++ b/apps/web/src/connection/eagerlyConnect.ts @@ -4,7 +4,7 @@ import store from 'state' import { clearRecentConnectionMeta } from 'state/user/reducer' import { trace } from 'tracing/trace' -import { deprecatedNetworkConnection, eip6963Connection, getConnection, gnosisSafeConnection } from './index' +import { eip6963Connection, getConnection, gnosisSafeConnection, networkConnection } from './index' import { getRecentConnectionMeta } from './meta' import { ConnectionType } from './types' @@ -47,7 +47,7 @@ function connect(connector: Connector, type: ConnectionType) { if (window !== window.parent) { connect(gnosisSafeConnection.connector, ConnectionType.GNOSIS_SAFE) } -connect(deprecatedNetworkConnection.connector, ConnectionType.DEPRECATED_NETWORK) +connect(networkConnection.connector, ConnectionType.NETWORK) // Get the persisted wallet type from the last session. const recentConnectionMeta = getRecentConnectionMeta() diff --git a/apps/web/src/connection/index.ts b/apps/web/src/connection/index.ts index 6512d78933b..e85b56a5e96 100644 --- a/apps/web/src/connection/index.ts +++ b/apps/web/src/connection/index.ts @@ -15,7 +15,7 @@ import { useSyncExternalStore } from 'react' import { isMobile, isTouchable, isWebAndroid, isWebIOS } from 'uniswap/src/utils/platform' import { APP_RPC_URLS } from '../constants/networks' -import { DEPRECATED_RPC_PROVIDERS, RPC_PROVIDERS } from '../constants/providers' +import { RPC_PROVIDERS } from '../constants/providers' import { EIP6963 } from './eip6963' import { Connection, ConnectionType, ProviderInfo } from './types' import { getDeprecatedInjection, getIsCoinbaseWallet, getIsInjected, getIsMetaMaskWallet } from './utils' @@ -69,22 +69,6 @@ export const networkConnection: Connection = { shouldDisplay: () => false, } -const [deprecatedWeb3Network, deprecatedWeb3NetworkHooks] = initializeConnector( - (actions) => - new Network({ - actions, - urlMap: DEPRECATED_RPC_PROVIDERS, - defaultChainId: 1, - }) -) -export const deprecatedNetworkConnection: Connection = { - getProviderInfo: () => ({ name: 'Network' }), - connector: deprecatedWeb3Network, - hooks: deprecatedWeb3NetworkHooks, - type: ConnectionType.NETWORK, - shouldDisplay: () => false, -} - const getIsCoinbaseWalletBrowser = () => isMobile && getIsCoinbaseWallet() const getIsMetaMaskBrowser = () => isMobile && getIsMetaMaskWallet() const getIsInjectedMobileBrowser = () => getIsCoinbaseWalletBrowser() || getIsMetaMaskBrowser() @@ -235,7 +219,6 @@ export const connections = [ eip6963Connection, // network connector should be last in the list, as it should be the fallback if no other connector is active networkConnection, - deprecatedNetworkConnection, ] export function getConnection(c: Connector | ConnectionType) { @@ -257,8 +240,6 @@ export function getConnection(c: Connector | ConnectionType) { return uniwalletWCV2ConnectConnection case ConnectionType.NETWORK: return networkConnection - case ConnectionType.DEPRECATED_NETWORK: - return deprecatedNetworkConnection case ConnectionType.GNOSIS_SAFE: return gnosisSafeConnection case ConnectionType.EIP_6963_INJECTED: diff --git a/apps/web/src/connection/types.ts b/apps/web/src/connection/types.ts index fa74be105fc..5813c584a87 100644 --- a/apps/web/src/connection/types.ts +++ b/apps/web/src/connection/types.ts @@ -9,7 +9,6 @@ export enum ConnectionType { WALLET_CONNECT_V2 = 'WALLET_CONNECT_V2', NETWORK = 'NETWORK', GNOSIS_SAFE = 'GNOSIS_SAFE', - DEPRECATED_NETWORK = 'DEPRECATED_NETWORK', EIP_6963_INJECTED = 'EIP_6963_INJECTED', } diff --git a/apps/web/src/constants/chainInfo.ts b/apps/web/src/constants/chainInfo.ts index dcdfe5a15c7..028c11d1e28 100644 --- a/apps/web/src/constants/chainInfo.ts +++ b/apps/web/src/constants/chainInfo.ts @@ -242,6 +242,17 @@ const CHAIN_INFO: ChainInfoMap = { nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, color: darkTheme.chain_84531, }, + [ChainId.BLAST]: { + networkType: NetworkType.L2, + bridge: 'https://blast.io/bridge', + defaultListUrl: '', + docs: 'https://docs.blast.io', + explorer: 'https://blastscan.io/', + infoLink: 'https://info.uniswap.org/#/blast/', + label: 'Blast', + nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 }, + color: darkTheme.chain_81457, + }, } as const export function getChainInfo( diff --git a/apps/web/src/constants/chains.test.ts b/apps/web/src/constants/chains.test.ts index 04d4201c070..78da34d67d8 100644 --- a/apps/web/src/constants/chains.test.ts +++ b/apps/web/src/constants/chains.test.ts @@ -18,6 +18,7 @@ const chainPriorityTestCases: [ChainId, number][] = [ [ChainId.AVALANCHE, 6], [ChainId.CELO, 7], [ChainId.CELO_ALFAJORES, 7], + [ChainId.BLAST, 8], ] test.each(chainPriorityTestCases)( diff --git a/apps/web/src/constants/chains.ts b/apps/web/src/constants/chains.ts index 660c5acd947..57e96024b95 100644 --- a/apps/web/src/constants/chains.ts +++ b/apps/web/src/constants/chains.ts @@ -15,6 +15,7 @@ export const CHAIN_IDS_TO_NAMES = { [ChainId.BNB]: 'bnb', [ChainId.AVALANCHE]: 'avalanche', [ChainId.BASE]: 'base', + [ChainId.BLAST]: 'blast', } as const // Include ChainIds in this array if they are not supported by the UX yet, but are already in the SDK. @@ -22,12 +23,20 @@ const NOT_YET_UX_SUPPORTED_CHAIN_IDS: number[] = [ ChainId.BASE_GOERLI, ChainId.ARBITRUM_SEPOLIA, ChainId.OPTIMISM_SEPOLIA, + ChainId.ROOTSTOCK, + ChainId.ZORA, + ChainId.ZORA_SEPOLIA, ] // TODO: include BASE_GOERLI, OPTIMISM_SEPOLIA, or ARBITRUM_SEPOLIA when routing is implemented export type SupportedInterfaceChain = Exclude< SupportedChainsType, - ChainId.BASE_GOERLI | ChainId.ARBITRUM_SEPOLIA | ChainId.OPTIMISM_SEPOLIA + | ChainId.BASE_GOERLI + | ChainId.ARBITRUM_SEPOLIA + | ChainId.OPTIMISM_SEPOLIA + | ChainId.ROOTSTOCK + | ChainId.ZORA + | ChainId.ZORA_SEPOLIA > export function isSupportedChain( @@ -60,6 +69,7 @@ export const SUPPORTED_GAS_ESTIMATE_CHAIN_IDS = [ ChainId.BNB, ChainId.AVALANCHE, ChainId.BASE, + ChainId.BLAST, ] as const /** @@ -104,6 +114,7 @@ export const L2_CHAIN_IDS = [ ChainId.OPTIMISM, ChainId.OPTIMISM_GOERLI, ChainId.BASE, + ChainId.BLAST, ] as const export type SupportedL2ChainId = (typeof L2_CHAIN_IDS)[number] @@ -137,8 +148,10 @@ export function getChainPriority(chainId: ChainId): number { case ChainId.CELO: case ChainId.CELO_ALFAJORES: return 7 - default: + case ChainId.BLAST: return 8 + default: + return Infinity } } diff --git a/apps/web/src/constants/networks.ts b/apps/web/src/constants/networks.ts index 49719591ae1..d49bb7e69fe 100644 --- a/apps/web/src/constants/networks.ts +++ b/apps/web/src/constants/networks.ts @@ -124,6 +124,14 @@ export const PUBLIC_RPC_URLS: Record = { 'https://1rpc.io/base', 'https://base.meowrpc.com', ], + [ChainId.BLAST]: [ + // "Safe" URLs + 'https://rpc.blast.io/', + 'https://rpc.ankr.com/blast', + 'https://blast.din.dev/rpc', + 'https://blastl2-mainnet.public.blastapi.io', + 'https://blast.blockpi.network/v1/rpc/public', + ], } /** @@ -145,6 +153,7 @@ export const APP_RPC_URLS: Record = { [ChainId.BNB]: [QUICKNODE_BNB_RPC_URL], [ChainId.AVALANCHE]: [`https://avalanche-mainnet.infura.io/v3/${INFURA_KEY}`], [ChainId.BASE]: [`https://base-mainnet.infura.io/v3/${INFURA_KEY}`], + [ChainId.BLAST]: [`https://blast-mainnet.infura.io/v3/${INFURA_KEY}`], } export const INFURA_PREFIX_TO_CHAIN_ID: { [prefix: string]: ChainId } = { diff --git a/apps/web/src/constants/providers.ts b/apps/web/src/constants/providers.ts index bdef5247890..62152de2691 100644 --- a/apps/web/src/constants/providers.ts +++ b/apps/web/src/constants/providers.ts @@ -5,9 +5,6 @@ import ConfiguredJsonRpcProvider from 'rpc/ConfiguredJsonRpcProvider' import { CHAIN_IDS_TO_NAMES, SupportedInterfaceChain } from './chains' import { APP_RPC_URLS } from './networks' -const providerFactory = (chainId: SupportedInterfaceChain, i = 0) => - new ConfiguredJsonRpcProvider(APP_RPC_URLS[chainId][i], { chainId, name: CHAIN_IDS_TO_NAMES[chainId] }) - function getAppProvider(chainId: SupportedInterfaceChain) { return new AppJsonRpcProvider( APP_RPC_URLS[chainId].map( @@ -32,21 +29,5 @@ export const RPC_PROVIDERS = { [ChainId.BNB]: getAppProvider(ChainId.BNB), [ChainId.AVALANCHE]: getAppProvider(ChainId.AVALANCHE), [ChainId.BASE]: getAppProvider(ChainId.BASE), + [ChainId.BLAST]: getAppProvider(ChainId.BLAST), } satisfies Record - -export const DEPRECATED_RPC_PROVIDERS = { - [ChainId.MAINNET]: providerFactory(ChainId.MAINNET), - [ChainId.GOERLI]: providerFactory(ChainId.GOERLI), - [ChainId.SEPOLIA]: providerFactory(ChainId.SEPOLIA), - [ChainId.OPTIMISM]: providerFactory(ChainId.OPTIMISM), - [ChainId.OPTIMISM_GOERLI]: providerFactory(ChainId.OPTIMISM_GOERLI), - [ChainId.ARBITRUM_ONE]: providerFactory(ChainId.ARBITRUM_ONE), - [ChainId.ARBITRUM_GOERLI]: providerFactory(ChainId.ARBITRUM_GOERLI), - [ChainId.POLYGON]: providerFactory(ChainId.POLYGON), - [ChainId.POLYGON_MUMBAI]: providerFactory(ChainId.POLYGON_MUMBAI), - [ChainId.CELO]: providerFactory(ChainId.CELO), - [ChainId.CELO_ALFAJORES]: providerFactory(ChainId.CELO_ALFAJORES), - [ChainId.BNB]: providerFactory(ChainId.BNB), - [ChainId.AVALANCHE]: providerFactory(ChainId.AVALANCHE), - [ChainId.BASE]: providerFactory(ChainId.BASE), -} satisfies Record diff --git a/apps/web/src/constants/routing.ts b/apps/web/src/constants/routing.ts index 8ec41a7c0f2..81b8368ff25 100644 --- a/apps/web/src/constants/routing.ts +++ b/apps/web/src/constants/routing.ts @@ -103,6 +103,7 @@ export const COMMON_BASES: ChainCurrencyList = { [ChainId.OPTIMISM_GOERLI]: [nativeOnChain(ChainId.OPTIMISM_GOERLI), USDC_OPTIMISM_GOERLI], [ChainId.BASE]: [nativeOnChain(ChainId.BASE), WRAPPED_NATIVE_CURRENCY[ChainId.BASE] as Token, USDC_BASE], + [ChainId.BLAST]: [nativeOnChain(ChainId.BLAST), WRAPPED_NATIVE_CURRENCY[ChainId.BLAST] as Token], [ChainId.POLYGON]: [ nativeOnChain(ChainId.POLYGON), diff --git a/apps/web/src/constants/tokenColors.ts b/apps/web/src/constants/tokenColors.ts deleted file mode 100644 index b0289c38b87..00000000000 --- a/apps/web/src/constants/tokenColors.ts +++ /dev/null @@ -1,73 +0,0 @@ -export const DEFAULT_COLOR = [35, 43, 43] - -export const predefinedTokenColors: { [key: string]: number[] } = { - // old WBTC - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599/logo.png': - [240, 146, 65], - // new WBTC - 'https://assets.coingecko.com/coins/images/7598/large/wrapped_bitcoin_wbtc.png?1548822744': [240, 146, 65], - // DAI - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png': - [250, 176, 27], - // UNI - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png': - [230, 53, 140], - // BUSD - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4Fabb145d64652a948d72533023f6E7A623C7C53/logo.png': - [239, 186, 9], - // AI-X - 'https://s2.coinmarketcap.com/static/img/coins/64x64/26984.png': [41, 161, 241], - // ETH - 'https://token-icons.s3.amazonaws.com/eth.png': [73, 112, 213], - // HARRYPOTTERSHIBAINUBITCOIN - 'https://assets.coingecko.com/coins/images/30323/large/hpos10i_logo_casino_night-dexview.png?1684117567': [ - 222, 49, 16, - ], - // PEPE - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6982508145454Ce325dDbE47a25d4ec3d2311933/logo.png': - [62, 174, 20], - // Unibot V2 - 'https://s2.coinmarketcap.com/static/img/coins/64x64/25436.png': [74, 10, 79], - // UNIBOT v1 - 'https://assets.coingecko.com/coins/images/30462/small/logonoline_%281%29.png?1687510315': [74, 10, 79], - // USDC - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png': - [0, 102, 217], - // HEX - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png': - [249, 63, 140], - // MONG - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C/logo.png': - [169, 109, 255], - // ARB - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1/logo.png': - [41, 161, 241], - // PSYOP - 'https://s2.coinmarketcap.com/static/img/coins/64x64/25422.png': [232, 143, 0], - // MATIC - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png': - [169, 109, 255], - // TURBO - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA35923162C49cF95e6BF26623385eb431ad920D3/logo.png': - [189, 110, 41], - // AIDOGE - 'https://assets.coingecko.com/coins/images/29852/large/photo_2023-04-18_14-25-28.jpg?1681799160': [41, 161, 241], - // SIMPSON - 'https://assets.coingecko.com/coins/images/30243/large/1111.png?1683692033': [232, 143, 0], - // OX - 'https://assets.coingecko.com/coins/images/30604/large/Logo2.png?1685522119': [41, 89, 217], - // ANGLE - 'https://assets.coingecko.com/coins/images/19060/large/ANGLE_Token-light.png?1666774221': [255, 85, 85], - // APE - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4d224452801ACEd8B2F0aebE155379bb5D594381/logo.png': - [5, 74, 169], - // GUSD - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd/logo.png': - [0, 164, 189], - // OGN - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png': - [5, 74, 169], - // RPL - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xD33526068D116cE69F19A9ee46F0bd304F21A51f/logo.png': - [255, 123, 79], -} diff --git a/apps/web/src/constants/tokenSafetyLookup.ts b/apps/web/src/constants/tokenSafetyLookup.ts index 18fc115dde0..20db4a13f54 100644 --- a/apps/web/src/constants/tokenSafetyLookup.ts +++ b/apps/web/src/constants/tokenSafetyLookup.ts @@ -1,6 +1,6 @@ import { TokenInfo } from '@uniswap/token-lists' -import { ListsState } from 'state/lists/reducer' +import { ListsState } from 'state/lists/types' import store from '../state' import { UNI_EXTENDED_LIST, UNI_LIST, UNSUPPORTED_LIST_URLS } from './lists' import { COMMON_BASES } from './routing' diff --git a/apps/web/src/constants/tokens.ts b/apps/web/src/constants/tokens.ts index 0badde1131b..af3d8955e84 100644 --- a/apps/web/src/constants/tokens.ts +++ b/apps/web/src/constants/tokens.ts @@ -218,6 +218,8 @@ export const BTC_BSC = new Token(ChainId.BNB, '0x7130d2A12B9BCbFAe4f2634d864A1Ee export const BUSD_BSC = new Token(ChainId.BNB, '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56', 18, 'BUSD', 'BUSD') export const DAI_BSC = new Token(ChainId.BNB, '0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3', 18, 'DAI', 'DAI') +export const USDB_BLAST = new Token(ChainId.BLAST, '0x4300000000000000000000000000000000000003', 18, 'USDB', 'USDB') + export const USDC_AVALANCHE = new Token( ChainId.AVALANCHE, '0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E', @@ -341,6 +343,7 @@ export const WRAPPED_NATIVE_CURRENCY: { [chainId: number]: Token | undefined } = 'WAVAX', 'Wrapped AVAX' ), + [ChainId.BLAST]: new Token(ChainId.BLAST, '0x4300000000000000000000000000000000000004', 18, 'WETH', 'Wrapped Ether'), } export function isCelo(chainId: number): chainId is ChainId.CELO | ChainId.CELO_ALFAJORES { @@ -503,6 +506,10 @@ const STABLECOINS: { [chainId in ChainId]: Token[] } = { [ChainId.BASE_GOERLI]: [], [ChainId.OPTIMISM_SEPOLIA]: [USDC_SEPOLIA], [ChainId.ARBITRUM_SEPOLIA]: [], + [ChainId.ZORA_SEPOLIA]: [], + [ChainId.ZORA]: [], + [ChainId.ROOTSTOCK]: [], + [ChainId.BLAST]: [USDB_BLAST], } export function isStablecoin(currency?: Currency): boolean { diff --git a/apps/web/src/featureFlags/flags/outageBanner.ts b/apps/web/src/featureFlags/flags/outageBanner.ts index 2717a87d592..4e85de6a885 100644 --- a/apps/web/src/featureFlags/flags/outageBanner.ts +++ b/apps/web/src/featureFlags/flags/outageBanner.ts @@ -49,5 +49,9 @@ export function useOutageBanners(): Record { [ChainId.AVALANCHE]: false, [ChainId.BASE_GOERLI]: false, [ChainId.BASE]: false, + [ChainId.ZORA_SEPOLIA]: false, + [ChainId.ZORA]: false, + [ChainId.ROOTSTOCK]: false, + [ChainId.BLAST]: false, } } diff --git a/apps/web/src/graphql/data/types.ts b/apps/web/src/graphql/data/types.ts index 47d239ec36c..a416d8c293c 100644 --- a/apps/web/src/graphql/data/types.ts +++ b/apps/web/src/graphql/data/types.ts @@ -1,21 +1,11 @@ -import { Currency } from '@uniswap/sdk-core' import { 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 { currencyId } from 'utils/currencyId' -// TODO: use shared versions of these types/utils when they are moved to the packages/uniswap package // TODO(WEB-3839): replace all usage of Currency in the web app with CurrencyInfo -// eslint-disable-next-line import/no-unused-modules -export type CurrencyInfo = { - currency: Currency - currencyId: string - safetyLevel: Maybe - logoUrl: Maybe - isSpam?: Maybe -} - -// eslint-disable-next-line import/no-unused-modules +// TODO: remove this function once we have it in the shared package export function gqlTokenToCurrencyInfo(token: GqlToken): CurrencyInfo | undefined { const currency = gqlToCurrency(token) diff --git a/apps/web/src/graphql/data/util.tsx b/apps/web/src/graphql/data/util.tsx index 10fa1374cbd..8200b8751e1 100644 --- a/apps/web/src/graphql/data/util.tsx +++ b/apps/web/src/graphql/data/util.tsx @@ -1,4 +1,5 @@ import { OperationVariables, QueryResult } from '@apollo/client' +import { DeepPartial } from '@apollo/client/utilities' import * as Sentry from '@sentry/react' import { ChainId, Currency, Token } from '@uniswap/sdk-core' import { AVERAGE_L1_BLOCK_TIME } from 'constants/chainInfo' @@ -11,6 +12,7 @@ import { ThemeColors } from 'theme/colors' import { Chain, ContractInput, + Token as GqlToken, HistoryDuration, PriceSource, TokenStandard, @@ -77,6 +79,7 @@ export const GQL_MAINNET_CHAINS_MUTABLE = [ Chain.Bnb, Chain.Avalanche, Chain.Base, + Chain.Blast, ] const GQL_MAINNET_CHAINS = [ @@ -88,6 +91,7 @@ const GQL_MAINNET_CHAINS = [ Chain.Bnb, Chain.Avalanche, Chain.Base, + Chain.Blast, ] as const const GQL_TESTNET_CHAINS = [Chain.EthereumGoerli, Chain.EthereumSepolia] as const @@ -110,6 +114,7 @@ export const CHAIN_ID_TO_BACKEND_NAME: { [key: number]: InterfaceGqlChain } = { [ChainId.BNB]: Chain.Bnb, [ChainId.AVALANCHE]: Chain.Avalanche, [ChainId.BASE]: Chain.Base, + [ChainId.BLAST]: Chain.Blast, } export function chainIdToBackendName(chainId: number | undefined) { @@ -130,20 +135,20 @@ export function toContractInput(currency: Currency): ContractInput { return { chain, address: currency.isToken ? currency.address : getNativeTokenDBAddress(chain) } } -export function gqlToCurrency(token: { - address?: string - chain: Chain - standard?: TokenStandard - decimals?: number - name?: string - symbol?: string -}): Currency | undefined { +export function gqlToCurrency(token: DeepPartial): Currency | undefined { + if (!token.chain) return undefined const chainId = supportedChainIdFromGQLChain(token.chain) if (!chainId) return undefined if (token.standard === TokenStandard.Native || token.address === NATIVE_CHAIN_ID || !token.address) return nativeOnChain(chainId) else - return new Token(chainId, token.address, token.decimals ?? 18, token.symbol ?? undefined, token.name ?? undefined) + return new Token( + chainId, + token.address, + token.decimals ?? 18, + token.symbol ?? undefined, + token.name ?? token.project?.name ?? undefined + ) } const URL_CHAIN_PARAM_TO_BACKEND: { [key: string]: InterfaceGqlChain } = { @@ -155,6 +160,7 @@ const URL_CHAIN_PARAM_TO_BACKEND: { [key: string]: InterfaceGqlChain } = { bnb: Chain.Bnb, avalanche: Chain.Avalanche, base: Chain.Base, + blast: Chain.Blast, } /** @@ -197,6 +203,7 @@ const CHAIN_NAME_TO_CHAIN_ID: { [key in InterfaceGqlChain]: ChainId } = { [Chain.Bnb]: ChainId.BNB, [Chain.Avalanche]: ChainId.AVALANCHE, [Chain.Base]: ChainId.BASE, + [Chain.Blast]: ChainId.BLAST, } export function isSupportedGQLChain(chain: Chain): chain is InterfaceGqlChain { @@ -233,6 +240,7 @@ export const BACKEND_SUPPORTED_CHAINS = [ Chain.Base, Chain.Bnb, Chain.Celo, + Chain.Blast, ] as const export const BACKEND_NOT_YET_SUPPORTED_CHAIN_IDS = [ChainId.AVALANCHE] as const diff --git a/apps/web/src/graphql/thegraph/apollo.ts b/apps/web/src/graphql/thegraph/apollo.ts index f7b37097175..d334c4cef6f 100644 --- a/apps/web/src/graphql/thegraph/apollo.ts +++ b/apps/web/src/graphql/thegraph/apollo.ts @@ -12,6 +12,8 @@ const CHAIN_SUBGRAPH_URL: Record = { [ChainId.BNB]: 'https://api.thegraph.com/subgraphs/name/ianlapham/uniswap-v3-bsc?source=uniswap', [ChainId.AVALANCHE]: 'https://api.thegraph.com/subgraphs/name/lynnshaoyu/uniswap-v3-avax?source=uniswap', [ChainId.BASE]: 'https://api.studio.thegraph.com/query/48211/uniswap-v3-base/version/latest?source=uniswap', + [ChainId.BLAST]: + 'https://gateway-arbitrum.network.thegraph.com/api/0ae45f0bf40ae2e73119b44ccd755967/subgraphs/id/2LHovKznvo8YmKC9ZprPjsYAZDCc4K5q4AYz8s3cnQn1', } const httpLink = new HttpLink({ uri: CHAIN_SUBGRAPH_URL[ChainId.MAINNET] }) diff --git a/apps/web/src/hooks/Tokens.ts b/apps/web/src/hooks/Tokens.ts index 51bdb9878a3..8fb7496267a 100644 --- a/apps/web/src/hooks/Tokens.ts +++ b/apps/web/src/hooks/Tokens.ts @@ -18,10 +18,12 @@ import { chainIdToBackendName } from 'graphql/data/util' import { useBytes32TokenContract, useTokenContract } from 'hooks/useContract' import { NEVER_RELOAD, useSingleCallResult } from 'lib/hooks/multicall' import useNativeCurrency from 'lib/hooks/useNativeCurrency' +import { deserializeToken } from 'state/user/utils' import { Token as GqlToken, useSimpleTokenQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' import { isAddress } from 'utilities/src/addresses' @@ -29,7 +31,7 @@ import { DEFAULT_ERC20_DECIMALS } from 'utilities/src/tokens/constants' import { getNativeTokenDBAddress } from 'utils/nativeTokens' import { useAllLists, useCombinedActiveList, useCombinedTokenMapFromUrls } from '../state/lists/hooks' import { TokenFromList } from '../state/lists/tokenFromList' -import { deserializeToken, useUserAddedTokens } from '../state/user/hooks' +import { useUserAddedTokens } from '../state/user/userAddedTokens' import { useUnsupportedTokenList } from './../state/lists/hooks' type Maybe = T | undefined @@ -331,9 +333,20 @@ function useTokenListCurrency(currencyId: Maybe, chainId?: ChainId): Cur } export function useCurrency(address?: string, chainId?: ChainId, skip?: boolean): Maybe { - const { chainId: connectedChainId } = useWeb3React() + const currencyInfo = useCurrencyInfo(address, chainId, skip) const gqlTokenListsEnabled = useFeatureFlag(FeatureFlags.GqlTokenLists) const tokenListCurrency = useTokenListCurrency(address, chainId) + return gqlTokenListsEnabled ? currencyInfo?.currency : tokenListCurrency +} + +/** + * Returns a CurrencyInfo from the tokenAddress+chainId pair. This should only + * be used directly if the gqlTokenListsEnabled flag is enabled, otherwise it + * will return undefined every time. + */ +function useCurrencyInfo(address?: string, chainId?: ChainId, skip?: boolean): Maybe { + const { chainId: connectedChainId } = useWeb3React() + const gqlTokenListsEnabled = useFeatureFlag(FeatureFlags.GqlTokenLists) const backendChainName = chainIdToBackendName(chainId ?? connectedChainId) const isNative = @@ -343,21 +356,21 @@ export function useCurrency(address?: string, chainId?: ChainId, skip?: boolean) chain: backendChainName, address: isNative ? getNativeTokenDBAddress(backendChainName) : address ?? '', }, - skip: (!address && !isNative) || skip, + skip: (!address && !isNative) || skip || !gqlTokenListsEnabled, fetchPolicy: 'cache-first', }) return useMemo(() => { if (!gqlTokenListsEnabled) { - return tokenListCurrency + return undefined } if (!data?.token || !address || skip) { return } - return gqlTokenToCurrencyInfo(data.token as GqlToken)?.currency - }, [gqlTokenListsEnabled, data?.token, address, skip, tokenListCurrency]) + return gqlTokenToCurrencyInfo(data.token as GqlToken) + }, [gqlTokenListsEnabled, data?.token, address, skip]) } export function useToken(tokenAddress?: string, chainId?: ChainId): Maybe { diff --git a/apps/web/src/hooks/useColor.ts b/apps/web/src/hooks/useColor.ts index 927f62cf129..7042f4f169d 100644 --- a/apps/web/src/hooks/useColor.ts +++ b/apps/web/src/hooks/useColor.ts @@ -1,65 +1,8 @@ -import { ChainId, Currency } from '@uniswap/sdk-core' -import { DEFAULT_COLOR } from 'constants/tokenColors' +import { Currency } from '@uniswap/sdk-core' import useTokenLogoSource from 'hooks/useAssetLogoSource' -import { rgb } from 'polished' -import { useEffect, useMemo, useRef, useState } from 'react' -import { TokenFromList } from 'state/lists/tokenFromList' +import { useMemo } from 'react' import { useTheme } from 'styled-components' -import { getAccessibleColor } from 'theme/utils' -import { getColor } from 'utils/getColor' - -function URIForEthToken(address: string) { - return `https://raw.githubusercontent.com/uniswap/assets/master/blockchains/ethereum/assets/${address}/logo.png` -} - -const COLOR_CACHE: Record = {} - -/** - * Retrieves the average color from a token's symbol using various sources. - * - * @param {Currency} currency - The currency for which to fetch the color. - * @param {string} primarySrc - Primary source URL for color retrieval (optional). - * - * @returns {Promise} A promise that resolves to a color string or null if color cannot be determined. - */ -async function getColorFromToken(srcs: string[]): Promise { - let color: string | null = null - try { - for (const src of srcs) { - const cachedColor = COLOR_CACHE[src] - if (cachedColor) return cachedColor - - const colorArray = await getColor(src) - color = colorArray === DEFAULT_COLOR ? null : convertColorArrayToString(colorArray) - if (color) { - COLOR_CACHE[src] = color - return color - } - } - return undefined - } catch (error) { - console.warn(`Unable to extract color from logo sources: ${srcs}`) - return undefined - } -} - -function getBackupCurrencySrcs(currency?: Currency) { - const srcs = [] - const wrappedToken = currency?.wrapped as TokenFromList - - if (wrappedToken?.logoURI) { - srcs.push(wrappedToken.logoURI) - } - if (currency?.chainId === ChainId.MAINNET) { - srcs.push(URIForEthToken(wrappedToken.address)) - } - - return srcs -} - -function convertColorArrayToString([red, green, blue]: number[]): string { - return rgb({ red, green, blue }) -} +import { useExtractedTokenColor } from 'ui/src/utils/colors' type ContrastSettings = { backgroundColor: string; darkMode: boolean } @@ -71,38 +14,16 @@ export function useColor(currency?: Currency, contrastSettings?: ContrastSetting isNative: currency?.isNative, }) - const srcs = useMemo(() => { - const backupSrcs = getBackupCurrencySrcs(currency) - return src ? [src, ...backupSrcs] : backupSrcs - }, [currency, src]) - - return useSrcColor(srcs, contrastSettings) ?? theme.accent1 + return useSrcColor(src, currency?.name, contrastSettings?.backgroundColor).tokenColor ?? theme.accent1 } -export function useSrcColor(src?: string[] | string, contrastSettings?: ContrastSettings) { +export function useSrcColor(src?: string, currencyName?: string, backgroundColor?: string) { const theme = useTheme() - const [color, setColor] = useState() - - const prevSrcRef = useRef(src) - prevSrcRef.current = src - useEffect(() => { - async function fetchColor() { - if (src) { - let color = await getColorFromToken(Array.isArray(src) ? src : [src]) - // Update the color if the src has not changed since before fetch started - if (color && prevSrcRef.current === src) { - if (contrastSettings) { - color = getAccessibleColor(color, contrastSettings.backgroundColor, contrastSettings.darkMode) - } - setColor(color) - } else { - setColor(undefined) - } - } - } - fetchColor() - }, [contrastSettings, src, theme.accent1]) + const extractSrc = useMemo( + () => (src?.includes('coingecko') ? 'https://corsproxy.io/?' + encodeURIComponent(src) : src), + [src] + ) - return color + return useExtractedTokenColor(extractSrc, currencyName, backgroundColor ?? theme.surface1, theme.accent1) } diff --git a/apps/web/src/hooks/useContract.ts b/apps/web/src/hooks/useContract.ts index 1f9e3d68e39..189cacd3497 100644 --- a/apps/web/src/hooks/useContract.ts +++ b/apps/web/src/hooks/useContract.ts @@ -16,8 +16,8 @@ import V3MigratorJson from '@uniswap/v3-periphery/artifacts/contracts/V3Migrator import UniswapInterfaceMulticallJson from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json' import { useWeb3React } from '@web3-react/core' import { sendAnalyticsEvent } from 'analytics' +import { RPC_PROVIDERS } from 'constants/providers' import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens' -import { useNetworkProviders } from 'hooks/useNetworkProviders' import { useEffect, useMemo } from 'react' import ARGENT_WALLET_DETECTOR_ABI from 'uniswap/src/abis/argent-wallet-detector.json' import EIP_2612 from 'uniswap/src/abis/eip_2612.json' @@ -74,19 +74,18 @@ function useMainnetContract(address: string | und const { chainId } = useWeb3React() const isMainnet = chainId === ChainId.MAINNET const contract = useContract(isMainnet ? address : undefined, ABI, false) - const providers = useNetworkProviders() return useMemo(() => { if (isMainnet) return contract if (!address) return null - const provider = providers[ChainId.MAINNET] + const provider = RPC_PROVIDERS[ChainId.MAINNET] try { return getContract(address, ABI, provider) } catch (error) { console.error('Failed to get mainnet contract', error) return null } - }, [isMainnet, contract, address, providers, ABI]) as T + }, [isMainnet, contract, address, ABI]) as T } export function useV2MigratorContract() { diff --git a/apps/web/src/hooks/useFetchListCallback.ts b/apps/web/src/hooks/useFetchListCallback.ts index b42a8c69a9b..0a9a0882153 100644 --- a/apps/web/src/hooks/useFetchListCallback.ts +++ b/apps/web/src/hooks/useFetchListCallback.ts @@ -1,7 +1,7 @@ import { nanoid } from '@reduxjs/toolkit' import { ChainId } from '@uniswap/sdk-core' import { TokenList } from '@uniswap/token-lists' -import { useNetworkProviders } from 'hooks/useNetworkProviders' +import { RPC_PROVIDERS } from 'constants/providers' import getTokenList from 'lib/hooks/useTokenList/fetchTokenList' import resolveENSContentHash from 'lib/utils/resolveENSContentHash' import { useCallback } from 'react' @@ -10,7 +10,6 @@ import { fetchTokenList } from '../state/lists/actions' export function useFetchListCallback(): (listUrl: string, skipValidation?: boolean) => Promise { const dispatch = useAppDispatch() - const providers = useNetworkProviders() return useCallback( async (listUrl: string, skipValidation?: boolean) => { @@ -18,7 +17,7 @@ export function useFetchListCallback(): (listUrl: string, skipValidation?: boole dispatch(fetchTokenList.pending({ requestId, url: listUrl })) return getTokenList( listUrl, - (ensName: string) => resolveENSContentHash(ensName, providers[ChainId.MAINNET]), + (ensName: string) => resolveENSContentHash(ensName, RPC_PROVIDERS[ChainId.MAINNET]), skipValidation ) .then((tokenList) => { @@ -31,6 +30,6 @@ export function useFetchListCallback(): (listUrl: string, skipValidation?: boole throw error }) }, - [dispatch, providers] + [dispatch] ) } diff --git a/apps/web/src/hooks/useIsPoolOutOfSync.ts b/apps/web/src/hooks/useIsPoolOutOfSync.ts new file mode 100644 index 00000000000..ad920f2e940 --- /dev/null +++ b/apps/web/src/hooks/useIsPoolOutOfSync.ts @@ -0,0 +1,77 @@ +import { Currency, CurrencyAmount, Fraction, Price, Token } from '@uniswap/sdk-core' +import { parseUnits } from 'ethers/lib/utils' +import JSBI from 'jsbi' +import { useStablecoinAmountFromFiatValue } from './useStablecoinPrice' +import { useUSDPrice } from './useUSDPrice' + +// Show warning if the price diverges by more than 5% +const WARNING_THRESHOLD = new Fraction(5, 100) +// Use this to scale decimal values before operating on them +const DECIMAL_SCALAR = JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(18)) + +function useMarketPrice(baseCurrency?: Currency, quoteCurrency?: Currency) { + const baseCurrencyUSDPrice = useUSDPrice( + baseCurrency + ? CurrencyAmount.fromRawAmount(baseCurrency, JSBI.BigInt(parseUnits('1', baseCurrency?.decimals))) + : undefined, + baseCurrency + ) + const baseCurrencyStableCoinAmount = useStablecoinAmountFromFiatValue(baseCurrencyUSDPrice.data) + + const quoteCurrencyUSDPrice = useUSDPrice( + quoteCurrency + ? CurrencyAmount.fromRawAmount(quoteCurrency, JSBI.BigInt(parseUnits('1', quoteCurrency?.decimals))) + : undefined, + quoteCurrency + ) + const quoteCurrencyStableCoinAmount = useStablecoinAmountFromFiatValue(quoteCurrencyUSDPrice.data) + + if (!baseCurrencyStableCoinAmount || !quoteCurrencyStableCoinAmount) { + return undefined + } + + const marketPrice = new Fraction( + baseCurrencyStableCoinAmount.multiply(DECIMAL_SCALAR).toFixed(0), + quoteCurrencyStableCoinAmount.multiply(DECIMAL_SCALAR).toFixed(0) + ) + + return marketPrice +} + +/** + * In Uniswap v3, the current price is quoted as the exchange from token0 to token1. However, depending + * on liquidity conditions, the price in a particular pool can diverge from the rest of the market (i.e. other pools). + * This hook computes the market exchange rate between two currencies and compares it to the given pool price. + * If these prices diverge by more than WARNING_THRESHOLD, return true. Otherwise, return false. + * @param baseCurrency The pool's base currency (a.k.a. token0) + * @param quoteCurrency The pool's quote currency (a.k.a. token1) + * @param poolPrice The exchange rate between token0 and token1 + */ +export function useIsPoolOutOfSync(poolPrice?: Price) { + const marketPrice = useMarketPrice(poolPrice?.baseCurrency, poolPrice?.quoteCurrency) + + if (!poolPrice || !marketPrice) { + return false + } + + const scaledMarketPrice = JSBI.BigInt(marketPrice.multiply(DECIMAL_SCALAR).toFixed(0)) + const scaledPoolPrice = JSBI.BigInt( + poolPrice + .quote( + CurrencyAmount.fromRawAmount( + poolPrice.baseCurrency?.wrapped, + JSBI.BigInt(parseUnits('1', poolPrice.baseCurrency?.decimals)) + ) + ) + .multiply(DECIMAL_SCALAR) + .toFixed(0) + ) + + const difference = JSBI.lessThan(scaledMarketPrice, scaledPoolPrice) + ? JSBI.subtract(scaledPoolPrice, scaledMarketPrice) + : JSBI.subtract(scaledMarketPrice, scaledPoolPrice) + + const divergence = new Fraction(difference, scaledMarketPrice) + + return divergence.greaterThan(WARNING_THRESHOLD) +} diff --git a/apps/web/src/hooks/useNetworkProviders.ts b/apps/web/src/hooks/useNetworkProviders.ts deleted file mode 100644 index 639492e01e5..00000000000 --- a/apps/web/src/hooks/useNetworkProviders.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { DEPRECATED_RPC_PROVIDERS, RPC_PROVIDERS } from 'constants/providers' -import { FeatureFlags } from 'uniswap/src/features/experiments/flags' -import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' - -export function useNetworkProviders() { - return useFeatureFlag(FeatureFlags.FallbackProvider) ? RPC_PROVIDERS : DEPRECATED_RPC_PROVIDERS -} diff --git a/apps/web/src/hooks/useStablecoinPrice.ts b/apps/web/src/hooks/useStablecoinPrice.ts index af924179166..b181a0c688f 100644 --- a/apps/web/src/hooks/useStablecoinPrice.ts +++ b/apps/web/src/hooks/useStablecoinPrice.ts @@ -10,6 +10,7 @@ import { CUSD_CELO, CUSD_CELO_ALFAJORES, DAI_OPTIMISM, + USDB_BLAST, USDC_ARBITRUM, USDC_ARBITRUM_GOERLI, USDC_AVALANCHE, @@ -40,6 +41,7 @@ export const STABLECOIN_AMOUNT_OUT: { [key in SupportedInterfaceChain]: Currency [ChainId.ARBITRUM_GOERLI]: CurrencyAmount.fromRawAmount(USDC_ARBITRUM_GOERLI, 10_000e6), [ChainId.POLYGON_MUMBAI]: CurrencyAmount.fromRawAmount(USDC_POLYGON_MUMBAI, 10_000e6), [ChainId.CELO_ALFAJORES]: CurrencyAmount.fromRawAmount(CUSD_CELO_ALFAJORES, 10_000e6), + [ChainId.BLAST]: CurrencyAmount.fromRawAmount(USDB_BLAST, 10_000e18), } /** diff --git a/apps/web/src/hooks/useSwapCallback.tsx b/apps/web/src/hooks/useSwapCallback.tsx index 476ba51b6d9..1d9535da912 100644 --- a/apps/web/src/hooks/useSwapCallback.tsx +++ b/apps/web/src/hooks/useSwapCallback.tsx @@ -5,7 +5,7 @@ import { useWeb3React } from '@web3-react/core' import { BigNumber } from 'ethers/lib/ethers' import { PermitSignature } from 'hooks/usePermitAllowance' import { useCallback } from 'react' -import { InterfaceTrade, TradeFillType } from 'state/routing/types' +import { InterfaceTrade, OffchainOrderType, TradeFillType } from 'state/routing/types' import { isClassicTrade, isUniswapXTrade } from 'state/routing/utils' import { useAddOrder } from 'state/signatures/hooks' import { UniswapXOrderDetails } from 'state/signatures/types' @@ -99,7 +99,7 @@ export function useSwapCallback( result.response.deadline, swapInfo as UniswapXOrderDetails['swapInfo'], result.response.encodedOrder, - isUniswapXTrade(trade) ? trade.offchainOrderType : undefined + isUniswapXTrade(trade) ? trade.offchainOrderType : OffchainOrderType.DUTCH_AUCTION // satisfying type-checker; isUniswapXTrade should always be true ) } else { addTransaction(result.response, swapInfo, result.deadline?.toNumber()) diff --git a/apps/web/src/hooks/useSwitchChain.ts b/apps/web/src/hooks/useSwitchChain.ts index b0ffa9d63b2..9a14bb91ff2 100644 --- a/apps/web/src/hooks/useSwitchChain.ts +++ b/apps/web/src/hooks/useSwitchChain.ts @@ -1,11 +1,6 @@ import { ChainId } from '@uniswap/sdk-core' import { Connector } from '@web3-react/types' -import { - deprecatedNetworkConnection, - networkConnection, - uniwalletWCV2ConnectConnection, - walletConnectV2Connection, -} from 'connection' +import { networkConnection, uniwalletWCV2ConnectConnection, walletConnectV2Connection } from 'connection' import { getChainInfo } from 'constants/chainInfo' import { CHAIN_IDS_TO_NAMES, isSupportedChain } from 'constants/chains' import { PUBLIC_RPC_URLS } from 'constants/networks' @@ -30,7 +25,6 @@ export function useSwitchChain() { walletConnectV2Connection.connector, uniwalletWCV2ConnectConnection.connector, networkConnection.connector, - deprecatedNetworkConnection.connector, ].includes(connector) ) { await connector.activate(chainId) diff --git a/apps/web/src/hooks/useUniswapXSwapCallback.ts b/apps/web/src/hooks/useUniswapXSwapCallback.ts index bc84033253d..b0d55ce0368 100644 --- a/apps/web/src/hooks/useUniswapXSwapCallback.ts +++ b/apps/web/src/hooks/useUniswapXSwapCallback.ts @@ -140,6 +140,7 @@ export function useUniswapXSwapCallback({ method: 'POST', body: JSON.stringify({ encodedOrder, + orderType: trade.offchainOrderType, signature, chainId: updatedOrder.chainId, quoteId: trade.quoteId, @@ -182,7 +183,7 @@ export function useUniswapXSwapCallback({ trace.setStatus('cancelled') throw error } else if (error instanceof SignatureExpiredError) { - trace.setStatus('deadline_exceeded') + trace.setStatus('unknown_error') throw error } else { trace.setError(error) diff --git a/apps/web/src/hooks/useUniversalRouter.ts b/apps/web/src/hooks/useUniversalRouter.ts index f33d28ba47c..7380a8c8ff0 100644 --- a/apps/web/src/hooks/useUniversalRouter.ts +++ b/apps/web/src/hooks/useUniversalRouter.ts @@ -92,27 +92,22 @@ export function useUniversalRouterSwapCallback( ...(value && !isZero(value) ? { value: toHex(value) } : {}), } - const gasEstimate = await trace.child( - { name: 'Estimate gas', op: 'wallet.estimate_gas' }, - async (gasTrace) => { - try { - return await provider.estimateGas(tx) - } catch (gasError) { - gasTrace.setError(gasError, 'failed_precondition') - sendAnalyticsEvent(SwapEventName.SWAP_ESTIMATE_GAS_CALL_FAILED, { - ...formatCommonPropertiesForTrade(trade, options.slippageTolerance), - ...analyticsContext, - client_block_number: blockNumber, - tx, - isAutoSlippage, - }) - console.warn(gasError) - throw new GasEstimationError() - } - } - ) - const gasLimit = calculateGasMargin(gasEstimate) - trace.setData('gasLimit', gasLimit.toNumber()) + let gasLimit: BigNumber + try { + const gasEstimate = await provider.estimateGas(tx) + gasLimit = calculateGasMargin(gasEstimate) + trace.setData('gasLimit', gasLimit.toNumber()) + } catch (gasError) { + sendAnalyticsEvent(SwapEventName.SWAP_ESTIMATE_GAS_CALL_FAILED, { + ...formatCommonPropertiesForTrade(trade, options.slippageTolerance), + ...analyticsContext, + client_block_number: blockNumber, + tx, + isAutoSlippage, + }) + console.warn(gasError) + throw new GasEstimationError() + } const response = await trace.child( { name: 'Send transaction', op: 'wallet.send_transaction' }, @@ -156,13 +151,12 @@ export function useUniversalRouterSwapCallback( return { type: TradeFillType.Classic as const, response, deadline } } catch (error: unknown) { if (error instanceof GasEstimationError) { - trace.setStatus('failed_precondition') throw error } else if (error instanceof UserRejectedRequestError) { trace.setStatus('cancelled') throw error } else if (error instanceof ModifiedSwapError) { - trace.setStatus('data_loss') + trace.setError(error, 'data_loss') throw error } else { trace.setError(error) diff --git a/apps/web/src/index.tsx b/apps/web/src/index.tsx index 260597777e5..b62270299e9 100644 --- a/apps/web/src/index.tsx +++ b/apps/web/src/index.tsx @@ -96,5 +96,5 @@ createRoot(container).render( ) if (process.env.REACT_APP_SERVICE_WORKER !== 'false') { - serviceWorkerRegistration.register() + serviceWorkerRegistration.unregister() } diff --git a/apps/web/src/lib/hooks/orders/updater.tsx b/apps/web/src/lib/hooks/orders/updater.tsx index bf09c8473b1..d546fdb5af3 100644 --- a/apps/web/src/lib/hooks/orders/updater.tsx +++ b/apps/web/src/lib/hooks/orders/updater.tsx @@ -35,7 +35,7 @@ async function fetchLimitStatuses(account: string, orders: UniswapXOrderDetails[ async function fetchOrderStatuses(account: string, orders: UniswapXOrderDetails[]): Promise { return fetchStatuses( orders, - (order) => order.type === SignatureType.SIGN_UNISWAPX_ORDER, + (order) => order.type === SignatureType.SIGN_UNISWAPX_ORDER || order.type === SignatureType.SIGN_UNISWAPX_V2_ORDER, (hashes) => `/orders?swapper=${account}&orderHashes=${hashes}` ) } diff --git a/apps/web/src/lib/hooks/routing/clientSideSmartOrderRouter.ts b/apps/web/src/lib/hooks/routing/clientSideSmartOrderRouter.ts index 17a51339283..f3c453142e6 100644 --- a/apps/web/src/lib/hooks/routing/clientSideSmartOrderRouter.ts +++ b/apps/web/src/lib/hooks/routing/clientSideSmartOrderRouter.ts @@ -3,20 +3,36 @@ import { BigintIsh, ChainId, CurrencyAmount, Token, TradeType } from '@uniswap/s // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { AlphaRouter, AlphaRouterConfig } from '@uniswap/smart-order-router' import { asSupportedChain } from 'constants/chains' -import { DEPRECATED_RPC_PROVIDERS } from 'constants/providers' +import { RPC_PROVIDERS } from 'constants/providers' import { nativeOnChain } from 'constants/tokens' import JSBI from 'jsbi' import { GetQuoteArgs, QuoteResult, QuoteState, SwapRouterNativeAssets } from 'state/routing/types' import { transformSwapRouteToGetQuoteResult } from 'utils/transformSwapRouteToGetQuoteResult' +const CLIENT_SIDE_ROUTING_ALLOW_LIST = [ + ChainId.MAINNET, + ChainId.OPTIMISM, + ChainId.OPTIMISM_GOERLI, + ChainId.ARBITRUM_ONE, + ChainId.ARBITRUM_GOERLI, + ChainId.POLYGON, + ChainId.POLYGON_MUMBAI, + ChainId.GOERLI, + ChainId.SEPOLIA, + ChainId.CELO_ALFAJORES, + ChainId.CELO, + ChainId.BNB, + ChainId.AVALANCHE, + ChainId.BASE, +] const routers = new Map() export function getRouter(chainId: ChainId): AlphaRouter { const router = routers.get(chainId) if (router) return router const supportedChainId = asSupportedChain(chainId) - if (supportedChainId) { - const provider = DEPRECATED_RPC_PROVIDERS[supportedChainId] + if (supportedChainId && CLIENT_SIDE_ROUTING_ALLOW_LIST.includes(chainId)) { + const provider = RPC_PROVIDERS[supportedChainId] const router = new AlphaRouter({ chainId, provider }) routers.set(chainId, router) return router diff --git a/apps/web/src/lib/hooks/transactions/updater.tsx b/apps/web/src/lib/hooks/transactions/updater.tsx index 1d323189126..44c464c7c8d 100644 --- a/apps/web/src/lib/hooks/transactions/updater.tsx +++ b/apps/web/src/lib/hooks/transactions/updater.tsx @@ -41,6 +41,7 @@ const RETRY_OPTIONS_BY_CHAIN_ID: { [chainId: number]: RetryOptions } = { [ChainId.OPTIMISM]: { n: 10, minWait: 250, maxWait: 1000 }, [ChainId.OPTIMISM_GOERLI]: { n: 10, minWait: 250, maxWait: 1000 }, [ChainId.BASE]: { n: 10, minWait: 250, maxWait: 1000 }, + [ChainId.BLAST]: { n: 10, minWait: 250, maxWait: 1000 }, } const DEFAULT_RETRY_OPTIONS: RetryOptions = { n: 1, minWait: 0, maxWait: 0 } diff --git a/apps/web/src/lib/hooks/useBlockNumber.tsx b/apps/web/src/lib/hooks/useBlockNumber.tsx index 360a9c35fed..c7cb475a07c 100644 --- a/apps/web/src/lib/hooks/useBlockNumber.tsx +++ b/apps/web/src/lib/hooks/useBlockNumber.tsx @@ -1,11 +1,11 @@ import { ChainId } from '@uniswap/sdk-core' import { useWeb3React } from '@web3-react/core' +import { RPC_PROVIDERS } from 'constants/providers' import useIsWindowVisible from 'hooks/useIsWindowVisible' -import { useNetworkProviders } from 'hooks/useNetworkProviders' -import { ReactNode, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react' +import { PropsWithChildren, createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react' const MISSING_PROVIDER = Symbol() -const BlockNumberContext = createContext< +export const BlockNumberContext = createContext< | { fastForward(block: number): void block?: number @@ -35,7 +35,7 @@ export function useMainnetBlockNumber(): number | undefined { return useBlockNumberContext().mainnetBlock } -export function BlockNumberProvider({ children }: { children: ReactNode }) { +export function BlockNumberProvider({ children }: PropsWithChildren) { const { chainId: activeChainId, provider } = useWeb3React() const [{ chainId, block, mainnetBlock }, setChainBlock] = useState<{ chainId?: number @@ -82,14 +82,13 @@ export function BlockNumberProvider({ children }: { children: ReactNode }) { }, [activeChainId, provider, windowVisible, onChainBlock]) // Poll once for the mainnet block number using the network provider. - const networkProviders = useNetworkProviders() useEffect(() => { - networkProviders[ChainId.MAINNET] + RPC_PROVIDERS[ChainId.MAINNET] .getBlockNumber() .then((block) => onChainBlock(ChainId.MAINNET, block)) // swallow errors - it's ok if this fails, as we'll try again if we activate mainnet .catch(() => undefined) - }, [networkProviders, onChainBlock]) + }, [onChainBlock]) const value = useMemo( () => ({ diff --git a/apps/web/src/lib/hooks/useCurrencyLogoURIs.ts b/apps/web/src/lib/hooks/useCurrencyLogoURIs.ts index 3cbbe057cfd..a8ab11632ad 100644 --- a/apps/web/src/lib/hooks/useCurrencyLogoURIs.ts +++ b/apps/web/src/lib/hooks/useCurrencyLogoURIs.ts @@ -10,7 +10,16 @@ import CeloLogo from '../../assets/svg/celo_logo.svg' import MaticLogo from '../../assets/svg/matic-token-icon.svg' import { NATIVE_CHAIN_ID, isCelo, nativeOnChain } from '../../constants/tokens' -type Network = 'ethereum' | 'arbitrum' | 'optimism' | 'polygon' | 'smartchain' | 'celo' | 'avalanchec' | 'base' +type Network = + | 'ethereum' + | 'arbitrum' + | 'optimism' + | 'polygon' + | 'smartchain' + | 'celo' + | 'avalanchec' + | 'base' + | 'blast' export function chainIdToNetworkName(networkId: ChainId): Network { switch (networkId) { @@ -30,6 +39,8 @@ export function chainIdToNetworkName(networkId: ChainId): Network { return 'avalanchec' case ChainId.BASE: return 'base' + case ChainId.BLAST: + return 'blast' default: return 'ethereum' } diff --git a/apps/web/src/pages/AddLiquidity/blastAlerts.tsx b/apps/web/src/pages/AddLiquidity/blastAlerts.tsx new file mode 100644 index 00000000000..198939ffd24 --- /dev/null +++ b/apps/web/src/pages/AddLiquidity/blastAlerts.tsx @@ -0,0 +1,151 @@ +import { Trans, t } from '@lingui/macro' +import Column from 'components/Column' +import { Dialog, DialogButtonType } from 'components/Dialog/Dialog' +import AlertTriangleFilled from 'components/Icons/AlertTriangleFilled' +import Row from 'components/Row' +import { useCallback, useState } from 'react' +import { ChevronDown } from 'react-feather' +import { useNavigate } from 'react-router-dom' +import styled from 'styled-components' +import { ButtonText, ExternalLink, ThemedText } from 'theme/components' + +const StyledAlertIcon = styled(AlertTriangleFilled)` + path { + fill: ${({ theme }) => theme.neutral2}; + } +` + +const AlertContainer = styled(Row)` + background-color: ${({ theme }) => theme.surface2}; + padding: 12px; + border-radius: 20px; + gap: 12px; + justify-content: space-between; + align-items: flex-start; +` + +const AlertIconContainer = styled.div` + display: flex; + flex-shrink: 0; + background-color: ${({ theme }) => theme.surface3}; + width: 40px; + height: 40px; + justify-content: center; + align-items: center; + border-radius: 12px; +` + +const StyledLearnMore = styled(ExternalLink)` + display: inline-block; +` + +interface BlastRebasingModalProps { + currencyIdA?: string + currencyIdB?: string + onContinue: () => void +} + +export function BlastRebasingModal({ currencyIdA, currencyIdB, onContinue }: BlastRebasingModalProps) { + const navigate = useNavigate() + + return ( + } + title={ + + Rebasing is unavailable on v3 + + } + description={ + + On Blast, USDB and WETH are rebasing tokens that automatically earn yield. Due to incompatibility with Uniswap + v3, LP positions with USDB or WETH won't earn rebasing yield, but will in Uniswap v2. + + } + body={ + + + Learn more + + + } + onCancel={onContinue} + buttonsConfig={{ + left: { + title: Continue on v3, + onClick: onContinue, + }, + right: { + title: Switch to v2, + onClick: () => navigate(`/add/v2/${currencyIdA ?? 'ETH'}/${currencyIdB ?? ''}`), + type: DialogButtonType.Accent, + }, + gap: 'sm', + }} + /> + ) +} + +const TextWrapper = styled(ThemedText.SubHeaderSmall)<{ $expanded: boolean }>` + display: -webkit-box; + -webkit-line-clamp: ${({ $expanded }) => ($expanded ? 'none' : 2)}; + -webkit-box-orient: vertical; + overflow: hidden; +` + +const ReadMore = styled(ButtonText)` + display: flex; + color: ${({ theme }) => theme.neutral2}; + justify-content: flex-start; + gap: 4px; + align-items: center; + + :focus { + text-decoration: none; + } +` + +const StyledChevronDown = styled(ChevronDown)<{ $expanded: boolean }>` + transform: ${({ $expanded }) => $expanded && 'rotate(-180deg)'}; + transition: ${({ + theme: { + transition: { duration, timing }, + }, + }) => `transform ${duration.fast} ${timing.inOut}`}; +` + +export function BlastRebasingAlert() { + const [expanded, setExpanded] = useState(false) + const handleSetExpanded = useCallback(() => setExpanded(!expanded), [expanded]) + + return ( + + + + + + + Rebasing unavailable on v3 + + + + On Blast, USDB and WETH are rebasing tokens that automatically earn yield. Due to incompatibility with + Uniswap v3, LP positions with USDB or WETH won't earn rebasing yield, but will in Uniswap v2. + {' '} + + + Learn more + + + + + + {expanded ? t`Read less` : t`Read more`} + + + + + + ) +} diff --git a/apps/web/src/pages/AddLiquidity/index.tsx b/apps/web/src/pages/AddLiquidity/index.tsx index 2852ac64818..2e2a694f6c0 100644 --- a/apps/web/src/pages/AddLiquidity/index.tsx +++ b/apps/web/src/pages/AddLiquidity/index.tsx @@ -2,7 +2,7 @@ import { BigNumber } from '@ethersproject/bignumber' import type { TransactionResponse } from '@ethersproject/providers' import { Trans } from '@lingui/macro' import { BrowserEvent, InterfaceElementName, InterfaceEventName, LiquidityEventName } from '@uniswap/analytics-events' -import { Currency, CurrencyAmount, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, Percent } from '@uniswap/sdk-core' +import { ChainId, Currency, CurrencyAmount, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, Percent } from '@uniswap/sdk-core' import { FeeAmount, NonfungiblePositionManager } from '@uniswap/v3-sdk' import { useWeb3React } from '@web3-react/core' import { TraceEvent, sendAnalyticsEvent, useTrace } from 'analytics' @@ -30,6 +30,10 @@ import { addressesAreEquivalent } from 'utils/addressesAreEquivalent' import { WrongChainError } from 'utils/errors' import { NumberType, useFormatter } from 'utils/formatNumbers' +import { OutOfSyncWarning } from 'components/addLiquidity/OutOfSyncWarning' +import { useIsPoolOutOfSync } from 'hooks/useIsPoolOutOfSync' +import { atomWithStorage, useAtomValue, useUpdateAtom } from 'jotai/utils' +import { BlastRebasingAlert, BlastRebasingModal } from 'pages/AddLiquidity/blastAlerts' import { ButtonError, ButtonLight, ButtonPrimary, ButtonText } from '../../components/Button' import { BlueCard, OutlineCard, YellowCard } from '../../components/Card' import { AutoColumn } from '../../components/Column' @@ -69,12 +73,19 @@ import { Review } from './Review' import { DynamicSection, MediumOnly, ResponsiveTwoColumns, ScrollablePage, StyledInput, Wrapper } from './styled' const DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE = new Percent(50, 10_000) +const blastRebasingAlertAtom = atomWithStorage('shouldShowBlastRebasingAlert', true) const StyledBodyWrapper = styled(BodyWrapper)<{ $hasExistingPosition: boolean }>` padding: ${({ $hasExistingPosition }) => ($hasExistingPosition ? '10px' : 0)}; max-width: 640px; ` +const BLAST_REBASING_TOKENS = [ + 'ETH', + '0x4300000000000000000000000000000000000004', + '0x4300000000000000000000000000000000000003', +] + export default function AddLiquidityWrapper() { const { chainId } = useWeb3React() if (isSupportedChain(chainId)) { @@ -572,6 +583,18 @@ function AddLiquidity() { const ownsNFT = addressesAreEquivalent(owner, account) || addressesAreEquivalent(existingPositionDetails?.operator, account) const showOwnershipWarning = Boolean(hasExistingPosition && account && !ownsNFT) + const showBlastRebasingWarning = + chainId === ChainId.BLAST && + ((!!currencyIdA && BLAST_REBASING_TOKENS.includes(currencyIdA)) || + (!!currencyIdB && BLAST_REBASING_TOKENS.includes(currencyIdB))) + + const showBlastRebasingModal = useAtomValue(blastRebasingAlertAtom) && showBlastRebasingWarning + const updateShowBlastRebasingModal = useUpdateAtom(blastRebasingAlertAtom) + const handleBlastModalContinue = useCallback(() => { + updateShowBlastRebasingModal(false) + }, [updateShowBlastRebasingModal]) + + const outOfSync = useIsPoolOutOfSync(price) return ( <> @@ -632,6 +655,11 @@ function AddLiquidity() { {!hasExistingPosition && ( <> + {outOfSync && ( + + + + )} Select pair @@ -664,7 +692,7 @@ function AddLiquidity() { id="add-liquidity-input-tokenb" /> - + {showBlastRebasingWarning && } Selected range} inRange={!outOfRange} ticksAtLimit={ticksAtLimit} + showBlastAlert={showBlastRebasingWarning} /> )} @@ -909,6 +938,9 @@ function AddLiquidity() { )} + {showBlastRebasingModal && ( + + )} ) } diff --git a/apps/web/src/pages/App.tsx b/apps/web/src/pages/App.tsx index 7e48af2528a..72a85273e19 100644 --- a/apps/web/src/pages/App.tsx +++ b/apps/web/src/pages/App.tsx @@ -270,7 +270,21 @@ function UserPropertyUpdater() { const isServiceWorkerHit = Boolean((window as any).__isDocumentCached) const serviceWorkerProperty = isServiceWorkerInstalled ? (isServiceWorkerHit ? 'hit' : 'miss') : 'uninstalled' - const pageLoadProperties = { service_worker: serviceWorkerProperty } + let cache = 'unknown' + try { + const timing = performance + .getEntriesByType('resource') + .find((timing) => timing.name.match(/\/static\/js\/main\.\w{8}\.js$/)) as PerformanceResourceTiming + if (timing.transferSize === 0) { + cache = 'hit' + } else { + cache = 'miss' + } + } catch { + // ignore + } + + const pageLoadProperties = { service_worker: serviceWorkerProperty, cache } sendInitializationEvent(SharedEventName.APP_LOADED, pageLoadProperties) const sendWebVital = (metric: string) => diff --git a/apps/web/src/pages/Landing/sections/Hero.tsx b/apps/web/src/pages/Landing/sections/Hero.tsx index 09cecb9378f..e3059e5f9a2 100644 --- a/apps/web/src/pages/Landing/sections/Hero.tsx +++ b/apps/web/src/pages/Landing/sections/Hero.tsx @@ -22,9 +22,6 @@ const LandingSwapContainer = styled(Box)` padding: 8px; border-radius: 24px; background: ${({ theme }) => theme.surface1}; - @media (max-width: 768px) { - width: 100%; - } ` const LandingSwap = styled(Swap)` position: relative; diff --git a/apps/web/src/pages/Swap/Limit/LimitExpirySection.test.tsx b/apps/web/src/pages/Swap/Limit/LimitExpirySection.test.tsx index 534a206681d..323c35f0b73 100644 --- a/apps/web/src/pages/Swap/Limit/LimitExpirySection.test.tsx +++ b/apps/web/src/pages/Swap/Limit/LimitExpirySection.test.tsx @@ -1,6 +1,7 @@ -import { Expiry, LimitContext } from 'state/limit/LimitContext' +import { LimitContext } from 'state/limit/LimitContext' import { render } from 'test-utils/render' +import { Expiry } from 'state/limit/types' import { LimitExpirySection } from './LimitExpirySection' const mockLimitContextValue = { diff --git a/apps/web/src/pages/Swap/Limit/LimitExpirySection.tsx b/apps/web/src/pages/Swap/Limit/LimitExpirySection.tsx index 446ef01eab3..063ffa4c354 100644 --- a/apps/web/src/pages/Swap/Limit/LimitExpirySection.tsx +++ b/apps/web/src/pages/Swap/Limit/LimitExpirySection.tsx @@ -1,7 +1,8 @@ import { t, Trans } from '@lingui/macro' import { sendAnalyticsEvent } from 'analytics' import Row from 'components/Row' -import { Expiry, useLimitContext } from 'state/limit/LimitContext' +import { useLimitContext } from 'state/limit/LimitContext' +import { Expiry } from 'state/limit/types' import styled from 'styled-components' import { ClickableStyle, ThemedText } from 'theme/components' diff --git a/apps/web/src/pages/Swap/Limit/LimitForm.tsx b/apps/web/src/pages/Swap/Limit/LimitForm.tsx index 1a83dea83b0..0d97223ae44 100644 --- a/apps/web/src/pages/Swap/Limit/LimitForm.tsx +++ b/apps/web/src/pages/Swap/Limit/LimitForm.tsx @@ -27,10 +27,10 @@ import { useUSDPrice } from 'hooks/useUSDPrice' import { useCallback, useEffect, useMemo, useState } from 'react' import { ArrowDown } from 'react-feather' import { Text } from 'rebass' -import { LimitContextProvider, LimitState, useLimitContext } from 'state/limit/LimitContext' +import { LimitContextProvider, useLimitContext } from 'state/limit/LimitContext' +import { LimitState } from 'state/limit/types' import { LimitOrderTrade, TradeFillType } from 'state/routing/types' -import { useSwapActionHandlers } from 'state/swap/hooks' -import { CurrencyState, useSwapAndLimitContext } from 'state/swap/SwapContext' +import { useSwapActionHandlers, useSwapAndLimitContext } from 'state/swap/hooks' import styled, { useTheme } from 'styled-components' import { NumberType, useFormatter } from 'utils/formatNumbers' import { maxAmountSpend } from 'utils/maxAmountSpend' @@ -43,6 +43,7 @@ import { CurrencySearchFilters } from 'components/SearchModal/CurrencySearch' import { useAtom } from 'jotai' import { LimitPriceError } from 'pages/Swap/Limit/LimitPriceError' import { getDefaultPriceInverted } from 'state/limit/hooks' +import { CurrencyState } from 'state/swap/types' import { ExternalLink, ThemedText } from 'theme/components' import { AlertTriangle } from 'ui/src/components/icons' import { LimitExpirySection } from './LimitExpirySection' diff --git a/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.test.tsx b/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.test.tsx index 9e6d3eeff5f..66d2697491f 100644 --- a/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.test.tsx +++ b/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.test.tsx @@ -1,7 +1,7 @@ import { SwapTab } from 'components/swap/constants' import { DAI } from 'constants/tokens' import { SendContext, SendContextType } from 'state/send/SendContext' -import { SwapAndLimitContext } from 'state/swap/SwapContext' +import { SwapAndLimitContext } from 'state/swap/types' import { render, screen, waitFor } from 'test-utils/render' import SendCurrencyInputForm from './SendCurrencyInputForm' diff --git a/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.tsx b/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.tsx index c2f5cd50dd1..e1b92281921 100644 --- a/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.tsx +++ b/apps/web/src/pages/Swap/Send/SendCurrencyInputForm.tsx @@ -16,13 +16,14 @@ import { useUSDPrice } from 'hooks/useUSDPrice' import { useCallback, useLayoutEffect, useMemo, useState } from 'react' import { SendInputError } from 'state/send/hooks' import { useSendContext } from 'state/send/SendContext' -import { CurrencyState, useSwapAndLimitContext } from 'state/swap/SwapContext' import styled, { css } from 'styled-components' import { ClickableStyle, ThemedText } from 'theme/components' import { NumberType, useFormatter } from 'utils/formatNumbers' import { maxAmountSpend } from 'utils/maxAmountSpend' import { asSupportedChain, SupportedInterfaceChain } from 'constants/chains' +import { useSwapAndLimitContext } from 'state/swap/hooks' +import { CurrencyState } from 'state/swap/types' import useResizeObserver from 'use-resize-observer' import { ReactComponent as DropDown } from '../../../assets/images/dropdown.svg' diff --git a/apps/web/src/pages/Swap/Send/SendForm.tsx b/apps/web/src/pages/Swap/Send/SendForm.tsx index dcd5775e8db..aea94715875 100644 --- a/apps/web/src/pages/Swap/Send/SendForm.tsx +++ b/apps/web/src/pages/Swap/Send/SendForm.tsx @@ -12,11 +12,12 @@ import { useSendCallback } from 'hooks/useSendCallback' import { useSwitchChain } from 'hooks/useSwitchChain' import { useCallback, useEffect, useMemo, useState } from 'react' import { SendContextProvider, useSendContext } from 'state/send/SendContext' -import { CurrencyState, useSwapAndLimitContext } from 'state/swap/SwapContext' import { didUserReject } from 'utils/swapErrorToUserReadableMessage' import { useIsSmartContractAddress } from 'utils/transfer' import { Trace } from 'analytics' +import { useSwapAndLimitContext } from 'state/swap/hooks' +import { CurrencyState } from 'state/swap/types' import { NewAddressSpeedBumpModal } from './NewAddressSpeedBump' import SendCurrencyInputForm from './SendCurrencyInputForm' import { SendRecipientForm } from './SendRecipientForm' diff --git a/apps/web/src/pages/Swap/Send/SendRecipientForm.test.tsx b/apps/web/src/pages/Swap/Send/SendRecipientForm.test.tsx index 0fb910b5425..b95d838e5fa 100644 --- a/apps/web/src/pages/Swap/Send/SendRecipientForm.test.tsx +++ b/apps/web/src/pages/Swap/Send/SendRecipientForm.test.tsx @@ -1,7 +1,7 @@ import { SwapTab } from 'components/swap/constants' import { DAI } from 'constants/tokens' import { SendContext, SendContextType } from 'state/send/SendContext' -import { SwapAndLimitContext } from 'state/swap/SwapContext' +import { SwapAndLimitContext } from 'state/swap/types' import { render, screen } from 'test-utils/render' import { shortenAddress } from 'utilities/src/addresses' import { SendRecipientForm } from './SendRecipientForm' diff --git a/apps/web/src/pages/Swap/Send/SendReviewModal.test.tsx b/apps/web/src/pages/Swap/Send/SendReviewModal.test.tsx index fc2ffbdcd6f..aa03c2279b8 100644 --- a/apps/web/src/pages/Swap/Send/SendReviewModal.test.tsx +++ b/apps/web/src/pages/Swap/Send/SendReviewModal.test.tsx @@ -2,7 +2,7 @@ import { SwapTab } from 'components/swap/constants' import { DAI } from 'constants/tokens' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { SendContext, SendContextType } from 'state/send/SendContext' -import { SwapAndLimitContext } from 'state/swap/SwapContext' +import { SwapAndLimitContext } from 'state/swap/types' import { render, screen } from 'test-utils/render' import { shortenAddress } from 'utilities/src/addresses' import { SendReviewModal } from './SendReviewModal' diff --git a/apps/web/src/pages/Swap/SwapForm.tsx b/apps/web/src/pages/Swap/SwapForm.tsx index 1ee14b35c12..990d5b6caf3 100644 --- a/apps/web/src/pages/Swap/SwapForm.tsx +++ b/apps/web/src/pages/Swap/SwapForm.tsx @@ -47,8 +47,12 @@ import { Text } from 'rebass' import { useAppSelector } from 'state/hooks' import { InterfaceTrade, TradeState } from 'state/routing/types' import { isClassicTrade } from 'state/routing/utils' -import { queryParametersToCurrencyState, useSwapActionHandlers } from 'state/swap/hooks' -import { CurrencyState, useSwapAndLimitContext, useSwapContext } from 'state/swap/SwapContext' +import { + queryParametersToCurrencyState, + useSwapActionHandlers, + useSwapAndLimitContext, + useSwapContext, +} from 'state/swap/hooks' import { useTheme } from 'styled-components' import { ThemedText } from 'theme/components' import { maybeLogFirstSwapAction } from 'tracing/swapFlowLoggers' @@ -60,6 +64,7 @@ import { largerPercentValue } from 'utils/percent' import { computeRealizedPriceImpact, warningSeverity } from 'utils/prices' import { didUserReject } from 'utils/swapErrorToUserReadableMessage' +import { CurrencyState } from 'state/swap/types' import { getIsReviewableQuote } from '.' import { OutputTaxTooltipBody } from './TaxTooltipBody' diff --git a/apps/web/src/pages/Swap/index.tsx b/apps/web/src/pages/Swap/index.tsx index 9bd6fdb61dc..c3b51baafa7 100644 --- a/apps/web/src/pages/Swap/index.tsx +++ b/apps/web/src/pages/Swap/index.tsx @@ -3,26 +3,22 @@ import { ChainId, Currency } from '@uniswap/sdk-core' import { useWeb3React } from '@web3-react/core' import { Trace } from 'analytics' import { NetworkAlert } from 'components/NetworkAlert/NetworkAlert' +import { SwitchLocaleLink } from 'components/SwitchLocaleLink' +import SwapHeader from 'components/swap/SwapHeader' import { SwapTab } from 'components/swap/constants' import { PageWrapper, SwapWrapper } from 'components/swap/styled' -import SwapHeader from 'components/swap/SwapHeader' -import { SwitchLocaleLink } from 'components/SwitchLocaleLink' import { asSupportedChain } from 'constants/chains' import { useCurrency } from 'hooks/Tokens' import useParsedQueryString from 'hooks/useParsedQueryString' +import { useScreenSize } from 'hooks/useScreenSize' import { SendForm } from 'pages/Swap/Send/SendForm' import { ReactNode, useMemo } from 'react' import { useLocation } from 'react-router-dom' import { InterfaceTrade, TradeState } from 'state/routing/types' import { isPreviewTrade } from 'state/routing/utils' +import { SwapAndLimitContextProvider, SwapContextProvider } from 'state/swap/SwapContext' import { queryParametersToCurrencyState } from 'state/swap/hooks' -import { - CurrencyState, - SwapAndLimitContext, - SwapAndLimitContextProvider, - SwapContextProvider, -} from 'state/swap/SwapContext' - +import { CurrencyState, SwapAndLimitContext } from 'state/swap/types' import { useIsDarkMode } from '../../theme/components/ThemeToggle' import { LimitFormWrapper } from './Limit/LimitForm' import { SwapForm } from './SwapForm' @@ -99,6 +95,7 @@ export function Swap({ syncTabToUrl: boolean }) { const isDark = useIsDarkMode() + const screenSize = useScreenSize() return ( ( - + {currentTab === SwapTab.Swap && ( )} diff --git a/apps/web/src/pages/TokenDetails/TDPContext.tsx b/apps/web/src/pages/TokenDetails/TDPContext.tsx index 911ef54c0a7..0b8896c2a91 100644 --- a/apps/web/src/pages/TokenDetails/TDPContext.tsx +++ b/apps/web/src/pages/TokenDetails/TDPContext.tsx @@ -30,7 +30,7 @@ type BaseTDPContext = { multiChainMap: MultiChainMap - extractedAccent1?: string + tokenColor?: string warning?: Warning } /** Token details context with an unresolved currency field */ diff --git a/apps/web/src/pages/TokenDetails/index.tsx b/apps/web/src/pages/TokenDetails/index.tsx index f48ff6028b2..8aba8638847 100644 --- a/apps/web/src/pages/TokenDetails/index.tsx +++ b/apps/web/src/pages/TokenDetails/index.tsx @@ -110,7 +110,8 @@ function useCreateTDPContext(): PendingTDPContext | LoadedTDPContext { const theme = useTheme() const { preloadedLogoSrc } = (useLocation().state as { preloadedLogoSrc?: string }) ?? {} const extractedColorSrc = tokenQuery.data?.token?.project?.logoUrl ?? preloadedLogoSrc - const extractedAccent1 = useSrcColor(extractedColorSrc, { backgroundColor: theme.surface2, darkMode: theme.darkMode }) + const tokenColor = + useSrcColor(extractedColorSrc, tokenQuery.data?.token?.name, theme.surface2).tokenColor ?? undefined return useMemo(() => { return { @@ -124,14 +125,14 @@ function useCreateTDPContext(): PendingTDPContext | LoadedTDPContext { chartState, warning, multiChainMap, - extractedAccent1, + tokenColor, } }, [ currency, currencyChain, currencyChainId, currencyWasFetchedOnChain, - extractedAccent1, + tokenColor, multiChainMap, warning, tokenAddress, @@ -146,7 +147,7 @@ export default function TokenDetailsPage() { return ( - + {getTokenPageTitle(contextValue?.currency)} diff --git a/apps/web/src/setupTests.ts b/apps/web/src/setupTests.ts index b6d683ff878..e3f2536f6c5 100644 --- a/apps/web/src/setupTests.ts +++ b/apps/web/src/setupTests.ts @@ -41,6 +41,7 @@ globalThis.origin = 'https://app.uniswap.org' }) globalThis.performance.measure = jest.fn() + globalThis.performance.mark = jest.fn() globalThis.React = React } diff --git a/apps/web/src/state/limit/LimitContext.tsx b/apps/web/src/state/limit/LimitContext.tsx index 644cb8a2d2d..2970035a3c3 100644 --- a/apps/web/src/state/limit/LimitContext.tsx +++ b/apps/web/src/state/limit/LimitContext.tsx @@ -1,30 +1,8 @@ import { createContext, Dispatch, PropsWithChildren, SetStateAction, useContext, useState } from 'react' +import { Expiry, LimitState } from 'state/limit/types' import { LimitInfo, useDerivedLimitInfo } from './hooks' -export enum Expiry { - Day = 'Day', - Week = 'Week', - Month = 'Month', - Year = 'Year', -} - -export interface LimitState { - readonly inputAmount: string - readonly outputAmount: string - readonly expiry: Expiry - readonly limitPrice: string - readonly limitPriceInverted: boolean - - // The form should autofill in the market price between two currencies unless the user has - // already manually edited the price for that currency pair - readonly limitPriceEdited: boolean - - // The limit form has 3 fields, but only two of them can be independent at a time. - // Always prefer `marketPrice` be independent, so either derive the input amount or the output amount - readonly isInputAmountFixed: boolean -} - type LimitContextType = { limitState: LimitState derivedLimitInfo: LimitInfo diff --git a/apps/web/src/state/limit/hooks.ts b/apps/web/src/state/limit/hooks.ts index 9126b78be0f..bb0902b3ef7 100644 --- a/apps/web/src/state/limit/hooks.ts +++ b/apps/web/src/state/limit/hooks.ts @@ -7,14 +7,14 @@ import { useCurrencyBalances } from 'lib/hooks/useCurrencyBalance' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import ms from 'ms' import { Dispatch, SetStateAction, useEffect, useMemo, useState } from 'react' +import { Expiry, LimitState } from 'state/limit/types' import { getWrapInfo } from 'state/routing/gas' import { LimitOrderTrade, RouterPreference, SubmittableTrade, SwapFeeInfo, WrapInfo } from 'state/routing/types' import { useRoutingAPITrade } from 'state/routing/useRoutingAPITrade' import { getUSDCostPerGas, isClassicTrade } from 'state/routing/utils' -import { useSwapAndLimitContext } from 'state/swap/SwapContext' +import { useSwapAndLimitContext } from 'state/swap/hooks' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' -import { Expiry, LimitState } from './LimitContext' export type LimitInfo = { currencyBalances: { [field in Field]?: CurrencyAmount } diff --git a/apps/web/src/state/limit/types.ts b/apps/web/src/state/limit/types.ts new file mode 100644 index 00000000000..d389704e6da --- /dev/null +++ b/apps/web/src/state/limit/types.ts @@ -0,0 +1,22 @@ +export enum Expiry { + Day = 'Day', + Week = 'Week', + Month = 'Month', + Year = 'Year', +} + +export interface LimitState { + readonly inputAmount: string + readonly outputAmount: string + readonly expiry: Expiry + readonly limitPrice: string + readonly limitPriceInverted: boolean + + // The form should autofill in the market price between two currencies unless the user has + // already manually edited the price for that currency pair + readonly limitPriceEdited: boolean + + // The limit form has 3 fields, but only two of them can be independent at a time. + // Always prefer `marketPrice` be independent, so either derive the input amount or the output amount + readonly isInputAmountFixed: boolean +} diff --git a/apps/web/src/state/lists/reducer.test.ts b/apps/web/src/state/lists/reducer.test.ts index 1bd11e72d4d..5cbad8447e0 100644 --- a/apps/web/src/state/lists/reducer.test.ts +++ b/apps/web/src/state/lists/reducer.test.ts @@ -2,9 +2,10 @@ import tokenSafetyLookup from 'constants/tokenSafetyLookup' import { createStore, Store } from 'redux' import { updateVersion } from 'state/global/actions' +import { ListsState } from 'state/lists/types' import { DEFAULT_LIST_OF_LISTS } from '../../constants/lists' import { acceptListUpdate, addList, fetchTokenList, removeList } from './actions' -import reducer, { ListsState } from './reducer' +import reducer from './reducer' const STUB_TOKEN_LIST = { name: '', diff --git a/apps/web/src/state/lists/reducer.ts b/apps/web/src/state/lists/reducer.ts index 6669f472c3a..3057df419c7 100644 --- a/apps/web/src/state/lists/reducer.ts +++ b/apps/web/src/state/lists/reducer.ts @@ -1,24 +1,12 @@ import { createReducer } from '@reduxjs/toolkit' -import { getVersionUpgrade, TokenList, VersionUpgrade } from '@uniswap/token-lists' +import { getVersionUpgrade, VersionUpgrade } from '@uniswap/token-lists' import tokenSafetyLookup from 'constants/tokenSafetyLookup' +import { ListsState } from 'state/lists/types' import { DEFAULT_LIST_OF_LISTS } from '../../constants/lists' import { updateVersion } from '../global/actions' import { acceptListUpdate, addList, fetchTokenList, removeList } from './actions' -export interface ListsState { - readonly byUrl: { - readonly [url: string]: { - readonly current: TokenList | null - readonly pendingUpdate: TokenList | null - readonly loadingRequestId: string | null - readonly error: string | null - } - } - // this contains the default list of lists from the last time the updateVersion was called, i.e. the app was reloaded - readonly lastInitializedDefaultListOfLists?: string[] -} - type ListState = ListsState['byUrl'][string] const NEW_LIST_STATE: ListState = { diff --git a/apps/web/src/state/lists/types.ts b/apps/web/src/state/lists/types.ts new file mode 100644 index 00000000000..7afec00fe43 --- /dev/null +++ b/apps/web/src/state/lists/types.ts @@ -0,0 +1,14 @@ +import { TokenList } from '@uniswap/token-lists' + +export interface ListsState { + readonly byUrl: { + readonly [url: string]: { + readonly current: TokenList | null + readonly pendingUpdate: TokenList | null + readonly loadingRequestId: string | null + readonly error: string | null + } + } + // this contains the default list of lists from the last time the updateVersion was called, i.e. the app was reloaded + readonly lastInitializedDefaultListOfLists?: string[] +} diff --git a/apps/web/src/state/migrations.test.ts b/apps/web/src/state/migrations.test.ts index 1dc7b9181f1..120a8e6a4da 100644 --- a/apps/web/src/state/migrations.test.ts +++ b/apps/web/src/state/migrations.test.ts @@ -14,7 +14,7 @@ const defaultState = { user: {}, _persist: { rehydrated: true, - version: 7, + version: 8, }, application: { chainId: null, diff --git a/apps/web/src/state/migrations.ts b/apps/web/src/state/migrations.ts index e3a103ef5c2..61d9fb7a980 100644 --- a/apps/web/src/state/migrations.ts +++ b/apps/web/src/state/migrations.ts @@ -1,15 +1,16 @@ import { createMigrate, MigrationManifest, PersistedState, PersistMigrate } from 'redux-persist' import { MigrationConfig } from 'redux-persist/es/createMigrate' -import { migration0 } from './migrations/0' -import { migration1 } from './migrations/1' -import { migration2 } from './migrations/2' -import { migration3 } from './migrations/3' -import { migration4 } from './migrations/4' -import { migration5 } from './migrations/5' -import { migration6 } from './migrations/6' -import { migration7 } from './migrations/7' -import { legacyLocalStorageMigration } from './migrations/legacy' +import { migration0 } from 'state/migrations/0' +import { migration1 } from 'state/migrations/1' +import { migration2 } from 'state/migrations/2' +import { migration3 } from 'state/migrations/3' +import { migration4 } from 'state/migrations/4' +import { migration5 } from 'state/migrations/5' +import { migration6 } from 'state/migrations/6' +import { migration7 } from 'state/migrations/7' +import { migration8 } from 'state/migrations/8' +import { legacyLocalStorageMigration } from 'state/migrations/legacy' /** * These run once per state re-hydration when a version mismatch is detected. @@ -29,6 +30,7 @@ export const migrations: MigrationManifest = { 5: migration5, 6: migration6, 7: migration7, + 8: migration8, } // We use a custom migration function for the initial state, because redux-persist diff --git a/apps/web/src/state/migrations/3.ts b/apps/web/src/state/migrations/3.ts index c7f5837ef2b..2ed322c0a77 100644 --- a/apps/web/src/state/migrations/3.ts +++ b/apps/web/src/state/migrations/3.ts @@ -1,7 +1,7 @@ import { ChainId, Token } from '@uniswap/sdk-core' import { PersistState } from 'redux-persist' -import { serializeToken } from 'state/user/hooks' import { UserState } from 'state/user/reducer' +import { serializeToken } from 'state/user/utils' export type PersistAppStateV3 = { _persist: PersistState diff --git a/apps/web/src/state/reducer.ts b/apps/web/src/state/reducer.ts index 9eff3e35d94..5fafd353e4e 100644 --- a/apps/web/src/state/reducer.ts +++ b/apps/web/src/state/reducer.ts @@ -44,7 +44,7 @@ export type AppState = ReturnType const persistConfig: PersistConfig = { key: 'interface', - version: 7, // see migrations.ts for more details about this version + version: 8, // see migrations.ts for more details about this version storage: localForage.createInstance({ name: 'redux', }), diff --git a/apps/web/src/state/reducerTypeTest.ts b/apps/web/src/state/reducerTypeTest.ts index 59a2e8c1cb8..0edb5a6939b 100644 --- a/apps/web/src/state/reducerTypeTest.ts +++ b/apps/web/src/state/reducerTypeTest.ts @@ -10,7 +10,7 @@ import { ApplicationModal, ApplicationState, PopupList, PopupType } from './appl import { Field as BurnField } from './burn/actions' import { BurnState } from './burn/reducer' import { BurnV3State } from './burn/v3/reducer' -import { ListsState } from './lists/reducer' +import { ListsState } from './lists/types' import { LogsState } from './logs/slice' import { Log } from './logs/utils' import { Field } from './mint/actions' diff --git a/apps/web/src/state/routing/gas.ts b/apps/web/src/state/routing/gas.ts index 8475f26e646..527b6ed8b29 100644 --- a/apps/web/src/state/routing/gas.ts +++ b/apps/web/src/state/routing/gas.ts @@ -1,7 +1,7 @@ import { MaxUint256, PERMIT2_ADDRESS } from '@uniswap/permit2-sdk' import { ChainId, Currency } from '@uniswap/sdk-core' import { SupportedInterfaceChain } from 'constants/chains' -import { DEPRECATED_RPC_PROVIDERS } from 'constants/providers' +import { RPC_PROVIDERS } from 'constants/providers' import { WRAPPED_NATIVE_CURRENCY } from 'constants/tokens' import ERC20_ABI from 'uniswap/src/abis/erc20.json' import { Erc20, Weth } from 'uniswap/src/abis/types' @@ -32,7 +32,7 @@ export async function getApproveInfo( return { needsApprove: false } } - const provider = DEPRECATED_RPC_PROVIDERS[currency.chainId as SupportedInterfaceChain] + const provider = RPC_PROVIDERS[currency.chainId as SupportedInterfaceChain] const tokenContract = getContract(currency.address, ERC20_ABI, provider) as Erc20 let approveGasUseEstimate @@ -64,7 +64,7 @@ export async function getWrapInfo( ): Promise { if (!needsWrap) return { needsWrap: false } - const provider = DEPRECATED_RPC_PROVIDERS[chainId] + const provider = RPC_PROVIDERS[chainId] const wethAddress = WRAPPED_NATIVE_CURRENCY[chainId]?.address // If any of these arguments aren't provided, then we cannot generate wrap cost info diff --git a/apps/web/src/state/routing/quickRouteSlice.ts b/apps/web/src/state/routing/quickRouteSlice.ts index 1b20bdac83e..23a317610dd 100644 --- a/apps/web/src/state/routing/quickRouteSlice.ts +++ b/apps/web/src/state/routing/quickRouteSlice.ts @@ -47,7 +47,6 @@ export const quickRouteApi = createApi({ typeof errorData === 'object' && (errorData?.errorCode === 'NO_ROUTE' || errorData?.detail === 'No quotes available') ) { - trace.setStatus('not_found') sendAnalyticsEvent('No quote received from quickroute API', { requestBody, response, diff --git a/apps/web/src/state/routing/slice.ts b/apps/web/src/state/routing/slice.ts index 750ccc13e38..a542c6c5f77 100644 --- a/apps/web/src/state/routing/slice.ts +++ b/apps/web/src/state/routing/slice.ts @@ -101,7 +101,7 @@ export const routingApi = createApi({ } try { - return trace.child({ name: 'Quote on server', op: 'quote.server' }, async (serverTrace) => { + return trace.child({ name: 'Quote on server', op: 'quote.server' }, async () => { const response = await fetch({ method: 'POST', url: `${UNISWAP_GATEWAY_DNS_URL}/quote`, @@ -120,8 +120,6 @@ export const routingApi = createApi({ typeof errorData === 'object' && (errorData?.errorCode === 'NO_ROUTE' || errorData?.detail === 'No quotes available') ) { - serverTrace.setStatus('not_found') - trace.setStatus('not_found') sendAnalyticsEvent('No quote received from routing API', { requestBody, response, @@ -149,7 +147,7 @@ export const routingApi = createApi({ } try { - return trace.child({ name: 'Quote on client', op: 'quote.client' }, async (clientTrace) => { + return trace.child({ name: 'Quote on client', op: 'quote.client' }, async () => { const { getRouter, getClientSideQuote } = await import('lib/hooks/routing/clientSideSmartOrderRouter') const router = getRouter(args.tokenInChainId) const quoteResult = await getClientSideQuote(args, router, CLIENT_PARAMS) @@ -159,8 +157,6 @@ export const routingApi = createApi({ data: { ...trade, latencyMs: trace.now() }, } } else { - clientTrace.setStatus('not_found') - trace.setStatus('not_found') return { data: { ...quoteResult, latencyMs: trace.now() } } } }) diff --git a/apps/web/src/state/routing/types.ts b/apps/web/src/state/routing/types.ts index ce4db02c97c..f6fdaf7e024 100644 --- a/apps/web/src/state/routing/types.ts +++ b/apps/web/src/state/routing/types.ts @@ -6,6 +6,7 @@ import { Route as V2Route } from '@uniswap/v2-sdk' import { Route as V3Route } from '@uniswap/v3-sdk' import { ZERO_PERCENT } from 'constants/misc' import { BigNumber } from 'ethers/lib/ethers' +import { SignatureType } from 'state/signatures/types' export enum TradeState { LOADING = 'loading', @@ -281,6 +282,12 @@ export enum OffchainOrderType { LIMIT_ORDER = 'Limit', } +export const OFFCHAIN_ORDER_TYPE_TO_SIGNATURE_TYPE: Record = { + [OffchainOrderType.DUTCH_AUCTION]: SignatureType.SIGN_UNISWAPX_ORDER, + [OffchainOrderType.DUTCH_V2_AUCTION]: SignatureType.SIGN_UNISWAPX_V2_ORDER, + [OffchainOrderType.LIMIT_ORDER]: SignatureType.SIGN_LIMIT, +} + export class DutchOrderTrade extends IDutchOrderTrade { public readonly fillType = TradeFillType.UniswapX public readonly offchainOrderType = OffchainOrderType.DUTCH_AUCTION diff --git a/apps/web/src/state/send/SendContext.tsx b/apps/web/src/state/send/SendContext.tsx index bb5655dbe6a..b7d58d7c696 100644 --- a/apps/web/src/state/send/SendContext.tsx +++ b/apps/web/src/state/send/SendContext.tsx @@ -10,7 +10,7 @@ import { useState, } from 'react' import { RecipientData, SendInfo, useDerivedSendInfo } from 'state/send/hooks' -import { useSwapAndLimitContext } from 'state/swap/SwapContext' +import { useSwapAndLimitContext } from 'state/swap/hooks' export type SendState = | { diff --git a/apps/web/src/state/signatures/hooks.ts b/apps/web/src/state/signatures/hooks.ts index b4481be64fa..54165884164 100644 --- a/apps/web/src/state/signatures/hooks.ts +++ b/apps/web/src/state/signatures/hooks.ts @@ -4,7 +4,7 @@ import { UniswapXOrderStatus } from 'lib/hooks/orders/types' import { useCallback, useMemo } from 'react' import { useDispatch } from 'react-redux' import { useAppSelector } from 'state/hooks' -import { OffchainOrderType } from 'state/routing/types' +import { OFFCHAIN_ORDER_TYPE_TO_SIGNATURE_TYPE, OffchainOrderType } from 'state/routing/types' import { addSignature } from './reducer' import { SignatureDetails, SignatureType, UniswapXOrderDetails } from './types' @@ -27,7 +27,13 @@ export function useOrder(orderHash: string): UniswapXOrderDetails | undefined { const signatures = useAllSignatures() return useMemo(() => { const order = signatures[orderHash] - if (!order || ![SignatureType.SIGN_UNISWAPX_ORDER, SignatureType.SIGN_LIMIT].includes(order.type)) return undefined + if ( + !order || + ![SignatureType.SIGN_UNISWAPX_ORDER, SignatureType.SIGN_UNISWAPX_V2_ORDER, SignatureType.SIGN_LIMIT].includes( + order.type as SignatureType + ) + ) + return undefined return order }, [signatures, orderHash]) } @@ -43,14 +49,11 @@ export function useAddOrder() { expiry: number, swapInfo: UniswapXOrderDetails['swapInfo'], encodedOrder: string, - offchainOrderType?: OffchainOrderType + offchainOrderType: OffchainOrderType ) => { dispatch( addSignature({ - type: - offchainOrderType === OffchainOrderType.DUTCH_AUCTION - ? SignatureType.SIGN_UNISWAPX_ORDER - : SignatureType.SIGN_LIMIT, + type: OFFCHAIN_ORDER_TYPE_TO_SIGNATURE_TYPE[offchainOrderType], offerer, id: orderHash, chainId, @@ -76,5 +79,8 @@ export function isOnChainOrder(orderStatus: UniswapXOrderStatus) { } function isPendingOrder(signature: SignatureDetails): signature is UniswapXOrderDetails { - return signature.type === SignatureType.SIGN_UNISWAPX_ORDER && signature.status === UniswapXOrderStatus.OPEN + return ( + (signature.type === SignatureType.SIGN_UNISWAPX_ORDER || signature.type === SignatureType.SIGN_UNISWAPX_V2_ORDER) && + signature.status === UniswapXOrderStatus.OPEN + ) } diff --git a/apps/web/src/state/signatures/types.ts b/apps/web/src/state/signatures/types.ts index 2edfe90cb96..bf16bcd9b06 100644 --- a/apps/web/src/state/signatures/types.ts +++ b/apps/web/src/state/signatures/types.ts @@ -4,11 +4,12 @@ import { ExactInputSwapTransactionInfo, ExactOutputSwapTransactionInfo } from '. export enum SignatureType { SIGN_UNISWAPX_ORDER = 'signUniswapXOrder', + SIGN_UNISWAPX_V2_ORDER = 'signUniswapXV2Order', SIGN_LIMIT = 'signLimit', } interface BaseSignatureFields { - type: SignatureType + type?: SignatureType id: string addedTime: number chainId: number @@ -16,9 +17,15 @@ interface BaseSignatureFields { offerer: string } +/** + * `UniswapXOrderDetails` is used for both submitting orders and fetching order details. + * - `type` is required for order submission; marked as optional due to the difficulty in distinguishing between X v1 & v2 orders when fetching details from on-chain data + * - `encodedOrder` is required for order submission; marked is optional as it's not returned by the GQL TransactionDetails schema when fetching order details + * - `txHash` is marked as optional because it's only present for orders that have been filled onchain. OrderHash !== TxHash + */ export interface UniswapXOrderDetails extends BaseSignatureFields { - type: SignatureType orderHash: string + type?: SignatureType status: UniswapXOrderStatus swapInfo: (ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo) & { isUniswapXOrder: true } txHash?: string diff --git a/apps/web/src/state/signatures/updater.tsx b/apps/web/src/state/signatures/updater.tsx index bd9b73a3415..041b8cd779f 100644 --- a/apps/web/src/state/signatures/updater.tsx +++ b/apps/web/src/state/signatures/updater.tsx @@ -24,8 +24,9 @@ export default function Updater() { () => Object.values(signatures).filter( (signature) => - [SignatureType.SIGN_UNISWAPX_ORDER, SignatureType.SIGN_LIMIT].includes(signature.type) && - signature.status === UniswapXOrderStatus.OPEN + [SignatureType.SIGN_UNISWAPX_ORDER, SignatureType.SIGN_UNISWAPX_V2_ORDER, SignatureType.SIGN_LIMIT].includes( + signature.type as SignatureType + ) && signature.status === UniswapXOrderStatus.OPEN ) as UniswapXOrderDetails[], [signatures] ) diff --git a/apps/web/src/state/swap/SwapContext.test.tsx b/apps/web/src/state/swap/SwapContext.test.tsx index 0a1e7eafbe4..4ff712a8dd3 100644 --- a/apps/web/src/state/swap/SwapContext.test.tsx +++ b/apps/web/src/state/swap/SwapContext.test.tsx @@ -3,14 +3,9 @@ import { Field, SwapTab } from 'components/swap/constants' import { nativeOnChain } from 'constants/tokens' import { render } from 'test-utils/render' -import { SwapInfo } from './hooks' -import { - SwapAndLimitContext, - SwapAndLimitContextProvider, - SwapContextProvider, - useSwapAndLimitContext, - useSwapContext, -} from './SwapContext' +import { useSwapAndLimitContext, useSwapContext } from 'state/swap/hooks' +import { SwapAndLimitContext, SwapInfo } from 'state/swap/types' +import { SwapAndLimitContextProvider, SwapContextProvider } from './SwapContext' describe('Swap Context', () => { test('should use context', () => { diff --git a/apps/web/src/state/swap/SwapContext.tsx b/apps/web/src/state/swap/SwapContext.tsx index 419e534f1c8..1dcfebe52ab 100644 --- a/apps/web/src/state/swap/SwapContext.tsx +++ b/apps/web/src/state/swap/SwapContext.tsx @@ -1,100 +1,11 @@ -import { ChainId, Currency, Percent } from '@uniswap/sdk-core' +import { ChainId, Currency } from '@uniswap/sdk-core' import { useWeb3React } from '@web3-react/core' -import { Field, SwapTab } from 'components/swap/constants' -import { parsedQueryString } from 'hooks/useParsedQueryString' +import { SwapTab } from 'components/swap/constants' import usePrevious from 'hooks/usePrevious' -import { - createContext, - Dispatch, - PropsWithChildren, - SetStateAction, - useContext, - useEffect, - useMemo, - useState, -} from 'react' -import { TradeState } from 'state/routing/types' +import { PropsWithChildren, useEffect, useMemo, useState } from 'react' -import { queryParametersToSwapState, SwapInfo, useDerivedSwapInfo } from './hooks' - -export interface SerializedCurrencyState { - inputCurrencyId?: string - outputCurrencyId?: string -} - -// shared state between Swap and Limit -export interface CurrencyState { - inputCurrency?: Currency - outputCurrency?: Currency -} - -export interface SwapState { - readonly independentField: Field - readonly typedValue: string -} - -const initialSwapState: SwapState = queryParametersToSwapState(parsedQueryString()) - -type SwapAndLimitContextType = { - currencyState: CurrencyState - prefilledState: { - inputCurrency?: Currency - outputCurrency?: Currency - } - 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 - chainId?: ChainId -} - -type SwapContextType = { - swapState: SwapState - derivedSwapInfo: SwapInfo - setSwapState: Dispatch> -} - -export const SwapAndLimitContext = createContext({ - currencyState: { - inputCurrency: undefined, - outputCurrency: undefined, - }, - setCurrencyState: () => undefined, - prefilledState: { - inputCurrency: undefined, - outputCurrency: undefined, - }, - chainId: ChainId.MAINNET, - currentTab: SwapTab.Swap, - setCurrentTab: () => undefined, -}) - -export const EMPTY_DERIVED_SWAP_INFO: SwapInfo = Object.freeze({ - currencies: {}, - currencyBalances: {}, - inputTax: new Percent(0), - outputTax: new Percent(0), - autoSlippage: new Percent(0), - allowedSlippage: new Percent(0), - trade: { - state: TradeState.LOADING, - }, -}) - -export const SwapContext = createContext({ - swapState: initialSwapState, - derivedSwapInfo: EMPTY_DERIVED_SWAP_INFO, - setSwapState: () => undefined, -}) - -export function useSwapContext() { - return useContext(SwapContext) -} - -export function useSwapAndLimitContext() { - return useContext(SwapAndLimitContext) -} +import { useDerivedSwapInfo } from './hooks' +import { CurrencyState, SwapAndLimitContext, SwapContext, SwapState, initialSwapState } from './types' export function SwapAndLimitContextProvider({ children, diff --git a/apps/web/src/state/swap/hooks.test.ts b/apps/web/src/state/swap/hooks.test.ts index bfb65683901..0e205d37b86 100644 --- a/apps/web/src/state/swap/hooks.test.ts +++ b/apps/web/src/state/swap/hooks.test.ts @@ -1,7 +1,8 @@ import { Field } from 'components/swap/constants' import { parse } from 'qs' -import { queryParametersToCurrencyState, queryParametersToSwapState } from './hooks' +import { queryParametersToSwapState } from 'state/swap/types' +import { queryParametersToCurrencyState } from './hooks' describe('hooks', () => { describe('#queryParametersToCurrencyState', () => { diff --git a/apps/web/src/state/swap/hooks.tsx b/apps/web/src/state/swap/hooks.tsx index 5b30116c40f..a815d201261 100644 --- a/apps/web/src/state/swap/hooks.tsx +++ b/apps/web/src/state/swap/hooks.tsx @@ -1,5 +1,5 @@ import { Trans } from '@lingui/macro' -import { Currency, CurrencyAmount, Percent, TradeType } from '@uniswap/sdk-core' +import { Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core' import { useWeb3React } from '@web3-react/core' import { Field } from 'components/swap/constants' import { useConnectionReady } from 'connection/eagerlyConnect' @@ -9,8 +9,7 @@ import { useSwapTaxes } from 'hooks/useSwapTaxes' import { useUSDPrice } from 'hooks/useUSDPrice' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { ParsedQs } from 'qs' -import { ReactNode, useCallback, useMemo } from 'react' -import { InterfaceTrade, TradeState } from 'state/routing/types' +import { ReactNode, useCallback, useContext, useMemo } from 'react' import { isClassicTrade, isSubmittableTrade, isUniswapXTrade } from 'state/routing/utils' import { useUserSlippageToleranceWithDefault } from 'state/user/hooks' import { isAddress } from 'utilities/src/addresses' @@ -20,10 +19,20 @@ import { useCurrencyBalances } from '../connection/hooks' import { CurrencyState, SerializedCurrencyState, + SwapAndLimitContext, + SwapContext, + SwapInfo, SwapState, - useSwapAndLimitContext, - useSwapContext, -} from './SwapContext' + parseIndependentFieldURLParameter, +} from './types' + +export function useSwapContext() { + return useContext(SwapContext) +} + +export function useSwapAndLimitContext() { + return useContext(SwapAndLimitContext) +} export function useSwapActionHandlers(): { onCurrencySelection: (field: Field, currency: Currency) => void @@ -106,25 +115,6 @@ export function useSwapActionHandlers(): { } } -export type SwapInfo = { - currencies: { [field in Field]?: Currency } - currencyBalances: { [field in Field]?: CurrencyAmount } - inputTax: Percent - outputTax: Percent - outputFeeFiatValue?: number - parsedAmount?: CurrencyAmount - inputError?: ReactNode - trade: { - trade?: InterfaceTrade - state: TradeState - uniswapXGasUseEstimateUSD?: number - error?: any - swapQuoteLatency?: number - } - allowedSlippage: Percent - autoSlippage: Percent -} - // from the current swap inputs, compute the best trade and return it. export function useDerivedSwapInfo(state: SwapState): SwapInfo { const { account } = useWeb3React() @@ -259,14 +249,6 @@ function parseCurrencyFromURLParameter(urlParam: ParsedQs[string]): string { return '' } -function parseTokenAmountURLParameter(urlParam: any): string { - return typeof urlParam === 'string' && !isNaN(parseFloat(urlParam)) ? urlParam : '' -} - -function parseIndependentFieldURLParameter(urlParam: any): Field { - return typeof urlParam === 'string' && urlParam.toLowerCase() === 'output' ? Field.OUTPUT : Field.INPUT -} - export function queryParametersToCurrencyState(parsedQs: ParsedQs): SerializedCurrencyState { let inputCurrency = parseCurrencyFromURLParameter(parsedQs.inputCurrency ?? parsedQs.inputcurrency) let outputCurrency = parseCurrencyFromURLParameter(parsedQs.outputCurrency ?? parsedQs.outputcurrency) @@ -285,13 +267,3 @@ export function queryParametersToCurrencyState(parsedQs: ParsedQs): SerializedCu outputCurrencyId: outputCurrency === '' ? undefined : outputCurrency ?? undefined, } } - -export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState { - const typedValue = parseTokenAmountURLParameter(parsedQs.exactAmount) - const independentField = parseIndependentFieldURLParameter(parsedQs.exactField) - - return { - typedValue, - independentField, - } -} diff --git a/apps/web/src/state/swap/types.ts b/apps/web/src/state/swap/types.ts new file mode 100644 index 00000000000..ab8fe7fd4a2 --- /dev/null +++ b/apps/web/src/state/swap/types.ts @@ -0,0 +1,114 @@ +import { ChainId, Currency, CurrencyAmount, Percent } from '@uniswap/sdk-core' +import { Field, SwapTab } from 'components/swap/constants' +import { parsedQueryString } from 'hooks/useParsedQueryString' +import { ParsedQs } from 'qs' +import { Dispatch, ReactNode, SetStateAction, createContext } from 'react' +import { InterfaceTrade, TradeState } from 'state/routing/types' + +export type SwapInfo = { + currencies: { [field in Field]?: Currency } + currencyBalances: { [field in Field]?: CurrencyAmount } + inputTax: Percent + outputTax: Percent + outputFeeFiatValue?: number + parsedAmount?: CurrencyAmount + inputError?: ReactNode + trade: { + trade?: InterfaceTrade + state: TradeState + uniswapXGasUseEstimateUSD?: number + error?: any + swapQuoteLatency?: number + } + allowedSlippage: Percent + autoSlippage: Percent +} + +type SwapContextType = { + swapState: SwapState + derivedSwapInfo: SwapInfo + setSwapState: Dispatch> +} + +function parseTokenAmountURLParameter(urlParam: any): string { + return typeof urlParam === 'string' && !isNaN(parseFloat(urlParam)) ? urlParam : '' +} + +export function parseIndependentFieldURLParameter(urlParam: any): Field { + return typeof urlParam === 'string' && urlParam.toLowerCase() === 'output' ? Field.OUTPUT : Field.INPUT +} + +export function queryParametersToSwapState(parsedQs: ParsedQs): SwapState { + const typedValue = parseTokenAmountURLParameter(parsedQs.exactAmount) + const independentField = parseIndependentFieldURLParameter(parsedQs.exactField) + + return { + typedValue, + independentField, + } +} + +export const EMPTY_DERIVED_SWAP_INFO: SwapInfo = Object.freeze({ + currencies: {}, + currencyBalances: {}, + inputTax: new Percent(0), + outputTax: new Percent(0), + autoSlippage: new Percent(0), + allowedSlippage: new Percent(0), + trade: { + state: TradeState.LOADING, + }, +}) + +export const initialSwapState: SwapState = queryParametersToSwapState(parsedQueryString()) + +export const SwapContext = createContext({ + swapState: initialSwapState, + derivedSwapInfo: EMPTY_DERIVED_SWAP_INFO, + setSwapState: () => undefined, +}) + +type SwapAndLimitContextType = { + currencyState: CurrencyState + prefilledState: { + inputCurrency?: Currency + outputCurrency?: Currency + } + 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 + chainId?: ChainId +} + +export const SwapAndLimitContext = createContext({ + currencyState: { + inputCurrency: undefined, + outputCurrency: undefined, + }, + setCurrencyState: () => undefined, + prefilledState: { + inputCurrency: undefined, + outputCurrency: undefined, + }, + chainId: ChainId.MAINNET, + currentTab: SwapTab.Swap, + setCurrentTab: () => undefined, +}) + +export interface SerializedCurrencyState { + inputCurrencyId?: string + outputCurrencyId?: string +} + +// shared state between Swap and Limit +export interface CurrencyState { + inputCurrency?: Currency + outputCurrency?: Currency +} + +export interface SwapState { + readonly independentField: Field + readonly typedValue: string +} diff --git a/apps/web/src/state/user/hooks.test.tsx b/apps/web/src/state/user/hooks.test.tsx index c86a37d0999..8f93114a8e6 100644 --- a/apps/web/src/state/user/hooks.test.tsx +++ b/apps/web/src/state/user/hooks.test.tsx @@ -5,7 +5,8 @@ import store from 'state' import { RouterPreference } from 'state/routing/types' import { renderHook } from 'test-utils/render' -import { deserializeToken, serializeToken, useRouterPreference, useUserSlippageTolerance } from './hooks' +import { deserializeToken, serializeToken } from 'state/user/utils' +import { useRouterPreference, useUserSlippageTolerance } from './hooks' import { updateUserSlippageTolerance } from './reducer' import { SlippageTolerance } from './types' diff --git a/apps/web/src/state/user/hooks.tsx b/apps/web/src/state/user/hooks.tsx index c1023934084..97cb774bbe3 100644 --- a/apps/web/src/state/user/hooks.tsx +++ b/apps/web/src/state/user/hooks.tsx @@ -8,10 +8,10 @@ import JSBI from 'jsbi' import { useCallback, useMemo } from 'react' import { useAppDispatch, useAppSelector } from 'state/hooks' import { RouterPreference } from 'state/routing/types' -import { UserAddedToken } from 'types/tokens' +import { useDefaultActiveTokens } from 'hooks/Tokens' +import { deserializeToken, serializeToken } from 'state/user/utils' import { BASES_TO_TRACK_LIQUIDITY_FOR, PINNED_PAIRS } from '../../constants/routing' -import { useDefaultActiveTokens } from '../../hooks/Tokens' import { addSerializedPair, addSerializedToken, @@ -21,27 +21,7 @@ import { updateUserRouterPreference, updateUserSlippageTolerance, } from './reducer' -import { SerializedPair, SerializedToken, SlippageTolerance } from './types' - -export function serializeToken(token: Token): SerializedToken { - return { - chainId: token.chainId, - address: token.address, - decimals: token.decimals, - symbol: token.symbol, - name: token.name, - } -} - -export function deserializeToken(serializedToken: SerializedToken, Class: typeof Token = Token): Token { - return new Class( - serializedToken.chainId, - serializedToken.address, - serializedToken.decimals, - serializedToken.symbol, - serializedToken.name - ) -} +import { SerializedPair, SlippageTolerance } from './types' export function useUserLocale(): SupportedLocale | null { return useAppSelector((state) => state.user.userLocale) @@ -171,22 +151,6 @@ export function useAddUserToken(): (token: Token) => void { ) } -function useUserAddedTokensOnChain(chainId: number | undefined | null): Token[] { - const serializedTokensMap = useAppSelector(({ user: { tokens } }) => tokens) - - return useMemo(() => { - if (!chainId) return [] - const tokenMap: Token[] = serializedTokensMap?.[chainId] - ? Object.values(serializedTokensMap[chainId]).map((value) => deserializeToken(value, UserAddedToken)) - : [] - return tokenMap - }, [serializedTokensMap, chainId]) -} - -export function useUserAddedTokens(): Token[] { - return useUserAddedTokensOnChain(useWeb3React().chainId) -} - function serializePair(pair: Pair): SerializedPair { return { token0: serializeToken(pair.token0), diff --git a/apps/web/src/state/user/userAddedTokens.ts b/apps/web/src/state/user/userAddedTokens.ts new file mode 100644 index 00000000000..a6c59be648c --- /dev/null +++ b/apps/web/src/state/user/userAddedTokens.ts @@ -0,0 +1,22 @@ +import { Token } from '@uniswap/sdk-core' +import { useWeb3React } from '@web3-react/core' +import { useMemo } from 'react' +import { useAppSelector } from 'state/hooks' +import { deserializeToken } from 'state/user/utils' +import { UserAddedToken } from 'types/tokens' + +function useUserAddedTokensOnChain(chainId: number | undefined | null): Token[] { + const serializedTokensMap = useAppSelector(({ user: { tokens } }) => tokens) + + return useMemo(() => { + if (!chainId) return [] + const tokenMap: Token[] = serializedTokensMap?.[chainId] + ? Object.values(serializedTokensMap[chainId]).map((value) => deserializeToken(value, UserAddedToken)) + : [] + return tokenMap + }, [serializedTokensMap, chainId]) +} + +export function useUserAddedTokens(): Token[] { + return useUserAddedTokensOnChain(useWeb3React().chainId) +} diff --git a/apps/web/src/state/user/utils.ts b/apps/web/src/state/user/utils.ts new file mode 100644 index 00000000000..de3e45ec52f --- /dev/null +++ b/apps/web/src/state/user/utils.ts @@ -0,0 +1,22 @@ +import { Token } from '@uniswap/sdk-core' +import { SerializedToken } from 'state/user/types' + +export function serializeToken(token: Token): SerializedToken { + return { + chainId: token.chainId, + address: token.address, + decimals: token.decimals, + symbol: token.symbol, + name: token.name, + } +} + +export function deserializeToken(serializedToken: SerializedToken, Class: typeof Token = Token): Token { + return new Class( + serializedToken.chainId, + serializedToken.address, + serializedToken.decimals, + serializedToken.symbol, + serializedToken.name + ) +} diff --git a/apps/web/src/test-utils/constants.ts b/apps/web/src/test-utils/constants.ts index 51147ade729..15ac59e07c8 100644 --- a/apps/web/src/test-utils/constants.ts +++ b/apps/web/src/test-utils/constants.ts @@ -3,11 +3,11 @@ import { ChainId, CurrencyAmount, Percent, Token, TradeType } from '@uniswap/sdk // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { V3Route } from '@uniswap/smart-order-router' import { FeeAmount, Pool } from '@uniswap/v3-sdk' -import { DAI, nativeOnChain, USDC_MAINNET } from 'constants/tokens' +import { DAI, USDC_MAINNET, nativeOnChain } from 'constants/tokens' import { BigNumber } from 'ethers/lib/ethers' import JSBI from 'jsbi' import { expiryToDeadlineSeconds } from 'state/limit/hooks' -import { Expiry } from 'state/limit/LimitContext' +import { Expiry } from 'state/limit/types' import { ClassicTrade, DutchOrderTrade, LimitOrderTrade, PreviewTrade, QuoteMethod } from 'state/routing/types' export const TEST_TOKEN_1 = new Token(1, '0x0000000000000000000000000000000000000001', 18, 'ABC', 'Abc') diff --git a/apps/web/src/test-utils/images.ts b/apps/web/src/test-utils/images.ts deleted file mode 100644 index a966524ae8c..00000000000 --- a/apps/web/src/test-utils/images.ts +++ /dev/null @@ -1,89 +0,0 @@ -export const arrayBufferBlackPng = new Uint8Array([ - 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, - 0, 0, 0, 13, 73, 68, 65, 84, 8, 91, 99, 96, 96, 96, 248, 15, 0, 1, 4, 1, 0, 193, 45, 142, 80, 0, 0, 0, 0, 73, 69, 78, - 68, 174, 66, 96, 130, -]) - -export const arrayBufferWhitePng = new Uint8Array([ - 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, - 0, 0, 0, 11, 73, 68, 65, 84, 8, 91, 99, 248, 15, 4, 0, 9, 251, 3, 253, 159, 31, 44, 0, 0, 0, 0, 0, 73, 69, 78, 68, - 174, 66, 96, 130, -]) - -export const arrayBufferBluePng = new Uint8Array([ - 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, - 0, 0, 0, 13, 73, 68, 65, 84, 8, 91, 99, 96, 96, 248, 255, 31, 0, 3, 2, 1, 255, 120, 191, 70, 181, 0, 0, 0, 0, 73, 69, - 78, 68, 174, 66, 96, 130, -]) - -export const arrayBufferPinkPng = new Uint8Array([ - 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 3, 0, 0, 0, 3, 8, 6, 0, 0, 0, 86, 40, 181, 191, - 0, 0, 0, 18, 73, 68, 65, 84, 8, 91, 99, 124, 102, 210, 243, 159, 1, 10, 24, 113, 114, 0, 185, 241, 7, 243, 212, 212, - 31, 166, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130, -]) - -export const arrayBufferBlackJpg = new Uint8Array([ - 255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 255, 219, 0, 67, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 219, 0, 67, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 255, 192, 0, 17, 8, 0, 1, 0, 1, 3, 1, 17, 0, 2, 17, 1, 3, 17, 1, 255, 196, 0, 31, 0, 0, 1, - 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 255, 196, 0, 181, 16, 0, 2, 1, 3, 3, - 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125, 1, 2, 3, 0, 4, 17, 5, 18, 33, 49, 65, 6, 19, 81, 97, 7, 34, 113, 20, 50, 129, 145, - 161, 8, 35, 66, 177, 193, 21, 82, 209, 240, 36, 51, 98, 114, 130, 9, 10, 22, 23, 24, 25, 26, 37, 38, 39, 40, 41, 42, - 52, 53, 54, 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, - 104, 105, 106, 115, 116, 117, 118, 119, 120, 121, 122, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, - 150, 151, 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, - 194, 195, 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 225, 226, 227, 228, 229, - 230, 231, 232, 233, 234, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 255, 196, 0, 31, 1, 0, 3, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 255, 196, 0, 181, 17, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, - 4, 0, 1, 2, 119, 0, 1, 2, 3, 17, 4, 5, 33, 49, 6, 18, 65, 81, 7, 97, 113, 19, 34, 50, 129, 8, 20, 66, 145, 161, 177, - 193, 9, 35, 51, 82, 240, 21, 98, 114, 209, 10, 22, 36, 52, 225, 37, 241, 23, 24, 25, 26, 38, 39, 40, 41, 42, 53, 54, - 55, 56, 57, 58, 67, 68, 69, 70, 71, 72, 73, 74, 83, 84, 85, 86, 87, 88, 89, 90, 99, 100, 101, 102, 103, 104, 105, 106, - 115, 116, 117, 118, 119, 120, 121, 122, 130, 131, 132, 133, 134, 135, 136, 137, 138, 146, 147, 148, 149, 150, 151, - 152, 153, 154, 162, 163, 164, 165, 166, 167, 168, 169, 170, 178, 179, 180, 181, 182, 183, 184, 185, 186, 194, 195, - 196, 197, 198, 199, 200, 201, 202, 210, 211, 212, 213, 214, 215, 216, 217, 218, 226, 227, 228, 229, 230, 231, 232, - 233, 234, 242, 243, 244, 245, 246, 247, 248, 249, 250, 255, 218, 0, 12, 3, 1, 0, 2, 17, 3, 17, 0, 63, 0, 255, 0, 63, - 250, 0, 255, 217, -]) - -export const arrayBufferBlackGif = new Uint8Array([ - 71, 73, 70, 56, 57, 97, 1, 0, 1, 0, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, - 9, 9, 9, 10, 10, 10, 11, 11, 11, 13, 13, 13, 15, 15, 15, 17, 17, 17, 19, 19, 19, 21, 21, 21, 23, 23, 23, 26, 26, 26, - 29, 29, 29, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39, 39, 40, 40, - 40, 41, 41, 41, 42, 42, 42, 43, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 46, 47, 47, 47, 48, 48, 48, 49, 49, 49, 50, - 50, 50, 51, 51, 51, 52, 52, 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57, 57, 57, 58, 58, 58, 59, 59, 59, - 60, 60, 60, 61, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 65, 65, 65, 66, 66, 66, 67, 67, 67, 68, 68, 68, 69, 69, - 69, 70, 70, 70, 71, 71, 71, 72, 72, 72, 73, 73, 73, 74, 74, 74, 75, 75, 75, 76, 76, 76, 77, 77, 77, 78, 78, 78, 79, - 79, 79, 80, 80, 80, 81, 81, 81, 82, 82, 82, 83, 83, 83, 84, 84, 84, 85, 85, 85, 86, 86, 86, 87, 87, 87, 88, 88, 88, - 89, 89, 89, 90, 90, 90, 91, 91, 91, 92, 92, 92, 93, 93, 93, 94, 94, 94, 95, 95, 95, 96, 96, 96, 97, 97, 97, 98, 98, - 98, 99, 99, 99, 100, 100, 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, 104, 104, 104, 105, 105, 105, 106, 106, - 106, 107, 107, 107, 108, 108, 108, 109, 109, 109, 110, 110, 110, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, - 114, 114, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 119, 119, 119, 120, 120, 120, 121, 121, 121, - 122, 122, 122, 123, 123, 123, 124, 124, 124, 125, 125, 125, 126, 126, 126, 127, 127, 127, 128, 128, 128, 129, 129, - 129, 130, 130, 130, 131, 131, 131, 132, 132, 132, 133, 133, 133, 134, 134, 134, 135, 135, 135, 136, 136, 136, 137, - 137, 137, 138, 138, 138, 139, 139, 139, 140, 140, 140, 141, 141, 141, 142, 142, 142, 143, 143, 143, 144, 144, 144, - 145, 145, 145, 146, 146, 146, 147, 147, 147, 148, 148, 148, 149, 149, 149, 150, 150, 150, 151, 151, 151, 152, 152, - 152, 153, 153, 153, 154, 154, 154, 155, 155, 155, 156, 156, 156, 157, 157, 157, 158, 158, 158, 159, 159, 159, 160, - 160, 160, 161, 161, 161, 162, 162, 162, 163, 163, 163, 164, 164, 164, 165, 165, 165, 166, 166, 166, 167, 167, 167, - 168, 168, 168, 169, 169, 169, 170, 170, 170, 171, 171, 171, 172, 172, 172, 173, 173, 173, 174, 174, 174, 175, 175, - 175, 176, 176, 176, 177, 177, 177, 178, 178, 178, 179, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182, 182, 183, - 183, 183, 184, 184, 184, 185, 185, 185, 186, 186, 186, 187, 187, 187, 188, 188, 188, 189, 189, 189, 190, 190, 190, - 191, 191, 191, 192, 192, 192, 193, 193, 193, 194, 194, 194, 195, 195, 195, 196, 196, 196, 197, 197, 197, 198, 198, - 198, 199, 199, 199, 200, 200, 200, 201, 201, 201, 202, 202, 202, 203, 203, 203, 204, 204, 204, 205, 205, 205, 206, - 206, 206, 207, 207, 207, 208, 208, 208, 209, 209, 209, 210, 210, 210, 211, 211, 211, 212, 212, 212, 213, 213, 213, - 214, 214, 214, 215, 215, 215, 216, 216, 216, 217, 217, 217, 218, 218, 218, 219, 219, 219, 220, 220, 220, 221, 221, - 221, 222, 222, 222, 223, 223, 223, 224, 224, 224, 225, 225, 225, 226, 226, 226, 227, 227, 227, 228, 228, 228, 229, - 229, 229, 230, 230, 230, 231, 231, 231, 232, 232, 232, 233, 233, 233, 234, 234, 234, 235, 235, 235, 236, 236, 236, - 237, 237, 237, 238, 238, 238, 239, 239, 239, 240, 240, 240, 241, 241, 241, 242, 242, 242, 243, 243, 243, 244, 244, - 244, 245, 245, 245, 246, 246, 246, 247, 247, 247, 248, 248, 248, 249, 249, 249, 250, 250, 250, 251, 251, 251, 252, - 252, 252, 253, 253, 253, 254, 254, 254, 255, 255, 255, 33, 249, 4, 0, 0, 0, 0, 0, 33, 254, 31, 71, 101, 110, 101, 114, - 97, 116, 101, 100, 32, 98, 121, 32, 111, 110, 108, 105, 110, 101, 71, 73, 70, 116, 111, 111, 108, 115, 46, 99, 111, - 109, 0, 44, 0, 0, 0, 0, 1, 0, 1, 0, 0, 8, 4, 0, 11, 4, 4, 0, 59, -]) - -export const arrayBufferBlackPngInvalid = new Uint8Array([ - 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 3, 0, 0, 0, 3, 8, 6, 0, 0, 0, 0, 0, 0, 5, 73, - 68, 65, 84, 0, 0, 0, 0, 49, 47, 4, 49, 0, 0, 0, 0, 73, 69, 78, 68, -]) diff --git a/apps/web/src/test-utils/render.tsx b/apps/web/src/test-utils/render.tsx index 1c519799a9a..50a07f88390 100644 --- a/apps/web/src/test-utils/render.tsx +++ b/apps/web/src/test-utils/render.tsx @@ -4,10 +4,10 @@ import { I18nProvider } from '@lingui/react' import { queries } from '@testing-library/dom' import { render, renderHook, RenderHookOptions, RenderOptions } from '@testing-library/react' import { DEFAULT_LOCALE } from 'constants/locales' -import { BlockNumberProvider } from 'lib/hooks/useBlockNumber' +import { BlockNumberContext } from 'lib/hooks/useBlockNumber' import catalog from 'locales/en-US' import { en } from 'make-plural/plurals' -import { ReactElement, ReactNode } from 'react' +import { PropsWithChildren, ReactElement, ReactNode } from 'react' import { HelmetProvider } from 'react-helmet-async/lib/index' import { QueryClient, QueryClientProvider } from 'react-query' import { Provider } from 'react-redux' @@ -28,6 +28,11 @@ const MockedI18nProvider = ({ children }: any) => {chi const queryClient = new QueryClient() +const BLOCK_NUMBER_CONTEXT = { fastForward: () => {}, block: 1234, mainnetBlock: 1234 } +function MockedBlockNumberProvider({ children }: PropsWithChildren) { + return {children} +} + const WithProviders = ({ children }: { children?: ReactNode }) => { return ( @@ -40,13 +45,13 @@ const WithProviders = ({ children }: { children?: ReactNode }) => { * To test behavior that depends on Web3Provider, use jest.unmock('@web3-react/core') */} - + {children} - + diff --git a/apps/web/src/theme/colors.ts b/apps/web/src/theme/colors.ts index 4f5398b7280..ccffaadb0d6 100644 --- a/apps/web/src/theme/colors.ts +++ b/apps/web/src/theme/colors.ts @@ -98,6 +98,7 @@ export const colors = { networkPolygonSoft: 'rgba(164, 87, 255, 0.16)', networkEthereumSoft: 'rgba(98, 126, 234, 0.16)', networkBase: '#0052FF', + networkBlast: '#fcfc03', //NEW COLORS FOR SPORE - need to define light/dark here cause they are root colors now (different system) neutral1_dark: '#FFFFFF', neutral2_dark: '#9B9B9B', @@ -154,6 +155,7 @@ const commonTheme = { chain_42161_background: colors.blue900, chain_84531: colors.networkBase, chain_56_background: colors.networkBsc, + chain_81457: colors.networkBlast, promotional: colors.magenta300, brandedGradient: 'linear-gradient(139.57deg, #FF79C9 4.35%, #FFB8E2 96.44%);', diff --git a/apps/web/src/theme/utils.ts b/apps/web/src/theme/utils.ts index a7dee15d932..f95842b60f2 100644 --- a/apps/web/src/theme/utils.ts +++ b/apps/web/src/theme/utils.ts @@ -1,4 +1,4 @@ -import { darken, lighten, mix } from 'polished' +import { mix } from 'polished' import { colors } from 'theme/colors' import { hex } from 'wcag-contrast' @@ -47,20 +47,6 @@ export function passesContrast(color: string, backgroundColor: string): boolean return contrast >= MIN_COLOR_CONTRAST_THRESHOLD } -/** Returns a color that passes the minimum contrast threshold against the background color. */ -export function getAccessibleColor(color: string, backgroundColor: string, darkMode: boolean): string { - if (passesContrast(color, backgroundColor)) return color - - let accessibleColor = color - for (let amount = 0.1; amount <= 1; amount += 0.1) { - if (passesContrast(accessibleColor, backgroundColor)) return accessibleColor - accessibleColor = darkMode ? lighten(amount, color) : darken(amount, color) - } - - console.warn(`Unable to find accessible color for ${color} on ${backgroundColor}`) - return color -} - /** * Compares a given color against white to determine if it passes the minimum contrast threshold. * @param color The hex value of the extracted color diff --git a/apps/web/src/tracing/SwapEventTimestampTracker.test.ts b/apps/web/src/tracing/SwapEventTimestampTracker.test.ts index 23f4cd8fdd4..0bf1370e521 100644 --- a/apps/web/src/tracing/SwapEventTimestampTracker.test.ts +++ b/apps/web/src/tracing/SwapEventTimestampTracker.test.ts @@ -1,25 +1,8 @@ import { SwapEventTimestampTracker, SwapEventType } from './SwapEventTimestampTracker' -jest.mock('./utils', () => ({ - calculateElapsedTimeWithPerformanceMarkMs: (mark: string) => { - switch (mark) { - case SwapEventType.FIRST_SWAP_ACTION: - return 100 - case SwapEventType.FIRST_QUOTE_FETCH_STARTED: - return 200 - case SwapEventType.FIRST_SWAP_SIGNATURE_REQUESTED: - return 300 - case SwapEventType.FIRST_SWAP_SIGNATURE_COMPLETED: - return 400 - case SwapEventType.FIRST_SWAP_SUCCESS: - return 500 - default: - return 0 - } - }, -})) - describe('SwapEventTimestampTracker', () => { + let swapSuccessTime: number | undefined + let swapActionTime: number | undefined it('should create a new instance', () => { const instance = SwapEventTimestampTracker.getInstance() expect(instance).toBeInstanceOf(SwapEventTimestampTracker) @@ -33,14 +16,18 @@ describe('SwapEventTimestampTracker', () => { it('should set and get elapsed time', () => { const instance = SwapEventTimestampTracker.getInstance() - expect(instance.setElapsedTime(SwapEventType.FIRST_SWAP_SUCCESS)).toEqual(500) - expect(instance.getElapsedTime(SwapEventType.FIRST_SWAP_SUCCESS)).toEqual(500) + swapSuccessTime = instance.setElapsedTime(SwapEventType.FIRST_SWAP_SUCCESS) + expect(instance.getElapsedTime(SwapEventType.FIRST_SWAP_SUCCESS)).toEqual(swapSuccessTime) }) it('should get elapsed time between two events', () => { const instance = SwapEventTimestampTracker.getInstance() - expect(instance.setElapsedTime(SwapEventType.FIRST_SWAP_ACTION)).toEqual(100) - expect(instance.getElapsedTime(SwapEventType.FIRST_SWAP_SUCCESS, SwapEventType.FIRST_SWAP_ACTION)).toEqual(400) + swapActionTime = instance.setElapsedTime(SwapEventType.FIRST_SWAP_ACTION) + expect(swapSuccessTime).toBeDefined() + expect(swapActionTime).toBeDefined() + expect(instance.getElapsedTime(SwapEventType.FIRST_SWAP_SUCCESS, SwapEventType.FIRST_SWAP_ACTION)).toEqual( + (swapSuccessTime as number) - (swapActionTime as number) + ) }) it('should return undefined if event type not set', () => { diff --git a/apps/web/src/tracing/request.test.ts b/apps/web/src/tracing/request.test.ts index dc0f3c69535..ce839c90183 100644 --- a/apps/web/src/tracing/request.test.ts +++ b/apps/web/src/tracing/request.test.ts @@ -49,14 +49,14 @@ describe('request', () => { it('captures wrapped `error`', async () => { fetch.mockResolvedValue(new Response(JSON.stringify({ error: { foo: 'bar' } }))) await api.fetch(GATEWAY_URL) - expect(span!.status).toBe('unknown_error') + expect(span!.status).toBe('internal_error') expect(span!.data).toHaveProperty('error', { foo: 'bar' }) }) it('captures wrapped `errors`', async () => { fetch.mockResolvedValue(new Response(JSON.stringify({ errors: [{ foo: 'bar' }] }))) await api.fetch(GATEWAY_URL) - expect(span!.status).toBe('unknown_error') + expect(span!.status).toBe('internal_error') expect(span!.data).toHaveProperty('error', [{ foo: 'bar' }]) }) }) @@ -65,14 +65,14 @@ describe('request', () => { it('captures json', async () => { fetch.mockResolvedValue(new Response(JSON.stringify({ foo: 'bar' }), { status: 500 })) await api.fetch(GATEWAY_URL) - expect(span!.status).toBe('unknown_error') + expect(span!.status).toBe('internal_error') expect(span!.data).toHaveProperty('error', { foo: 'bar' }) }) it('captures text', async () => { fetch.mockResolvedValue(new Response('foobar', { status: 500 })) await api.fetch(GATEWAY_URL) - expect(span!.status).toBe('unknown_error') + expect(span!.status).toBe('internal_error') expect(span!.data).toHaveProperty('error', 'foobar') }) }) diff --git a/apps/web/src/tracing/request.ts b/apps/web/src/tracing/request.ts index 415d5bb538c..04bbe6838a1 100644 --- a/apps/web/src/tracing/request.ts +++ b/apps/web/src/tracing/request.ts @@ -1,8 +1,8 @@ import { INFURA_PREFIX_TO_CHAIN_ID } from 'constants/networks' import { CHAIN_ID_TO_BACKEND_NAME } from 'graphql/data/util' +import { isTracing, trace } from 'tracing/trace' import { TraceContext } from 'tracing/types' import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { trace as startTrace } from './trace' export function patchFetch(api: Pick) { const apiFetch = api.fetch @@ -11,7 +11,6 @@ export function patchFetch(api: Pick) { function tracedFetch(input: RequestInfo, init?: RequestInit): Promise function tracedFetch(input: RequestInfo | URL, init?: RequestInit): Promise function tracedFetch(input: RequestInfo | URL, init?: RequestInit): Promise { - const zonedTrace = Zone.current.get('trace') let url try { // Hot-module reload passes a relative path to a local file, which is a technically malformed URL. @@ -19,12 +18,12 @@ export function patchFetch(api: Pick) { } catch { return apiFetch(input, init) } - const traceContext = getTraceContext(url, init, !!zonedTrace) + + const traceContext = getTraceContext(url, init, isTracing()) if (traceContext) { - const trace = zonedTrace || startTrace return trace(traceContext, async (trace) => { const response = await apiFetch(input, init) - trace.setStatus(response.status) + trace.setHttpStatus(response.status) if (is2xx(response.status)) { try { // Check for 200 responses which wrap an error @@ -91,7 +90,7 @@ export function getTraceContext(url: URL, init?: RequestInit, force = false): Tr data: { path: url.pathname }, } } - } else if (url.host.endsWith('.infura.io')) { + } else if (url.host.endsWith('.infura.io') || url.host.endsWith('.quiknode.pro')) { let method: string | undefined let chain: Chain | undefined try { diff --git a/apps/web/src/tracing/trace.test.ts b/apps/web/src/tracing/trace.test.ts index a7f679a7f22..eb6a6896590 100644 --- a/apps/web/src/tracing/trace.test.ts +++ b/apps/web/src/tracing/trace.test.ts @@ -99,6 +99,18 @@ describe('trace', () => { expect(parent.name).toBe(CONTEXT.name) expect(parent.op).toBe(CONTEXT.op) }) + + it("starts a span under the parent Zone's span", async () => { + await trace(CONTEXT, () => { + trace({ name: 'Child', op: 'child' as OpCode } as TraceContext, () => Promise.resolve()) + return Promise.resolve() + }) + expect(span!.name).toBe('Child') + expect(span!.op).toBe('child') + const parent = spanMap.get(span!.parentSpanId!)! + expect(parent.name).toBe(CONTEXT.name) + expect(parent.op).toBe(CONTEXT.op) + }) }) describe('setData', () => { @@ -120,10 +132,12 @@ describe('trace', () => { }) expect(span!.status).toBe('cancelled') }) + }) + describe('setHttpStatus', () => { it('sets a span http status with a number', async () => { - await trace(CONTEXT, ({ setStatus }) => { - setStatus(429) + await trace(CONTEXT, ({ setHttpStatus }) => { + setHttpStatus(429) return Promise.resolve() }) expect(span!.status).toBe('resource_exhausted') @@ -139,7 +153,7 @@ describe('trace', () => { return Promise.reject(error) }) ).rejects.toBeDefined() - expect(span!.status).toEqual('unknown_error') + expect(span!.status).toEqual('internal_error') expect(span!.data).toEqual({ error }) }) @@ -147,11 +161,11 @@ describe('trace', () => { const error = new Error('Test error') await expect( trace(CONTEXT, ({ setError }) => { - setError(error, 'cancelled') + setError(error, 'aborted') return Promise.reject(error) }) ).rejects.toBeDefined() - expect(span!.status).toEqual('cancelled') + expect(span!.status).toEqual('aborted') expect(span!.data).toEqual({ error }) }) }) diff --git a/apps/web/src/tracing/trace.ts b/apps/web/src/tracing/trace.ts index 6b6882f7f23..c41b503f21f 100644 --- a/apps/web/src/tracing/trace.ts +++ b/apps/web/src/tracing/trace.ts @@ -25,16 +25,21 @@ interface TraceCallbackContext { * Multiple keys may be set separately, eg `setData('key', data); setData('key.nested', otherData)`. */ setData(key: string, value: unknown): void + /** + * Sets the status of a trace from an HTTP status. + * If unset, the status will be set to 'ok' (or 'internal_error' if the callback throws). + */ + setHttpStatus(status: number): void /** * Sets the status of a trace. If unset, the status will be set to 'ok' (or 'internal_error' if the callback throws). - * @param status - If a number is passed, the corresponding http status will be used. + * Note that `ok`, `cancelled`, and `unknown_error` are considered non-failing; all others must use `setError`. */ - setStatus(status: number | SpanStatusType): void + setStatus(status: 'ok' | 'cancelled' | 'unknown_error'): void /** * Sets the error data of a trace. * If unset and the callback throws, the thrown error will automatically be set for the trace. */ - setError(error: unknown, status?: number | SpanStatusType): void + setError(error: unknown, status?: Exclude): void /** The elapsed time (in ms) since this trace was started. This mirrors `performance.now()`. */ now(): number @@ -43,24 +48,27 @@ type TraceCallback = (options: TraceCallbackContext) => Promise async function sentryAdaptor(context: TraceContext, callback: TraceCallback, span: Span | undefined): Promise { const start = performance.now() + let isStatusSet = false const callbackContext: TraceCallbackContext = { child(context, callback) { const { name, op, tags, data } = context - return sentryAdaptor(context, callback, span?.startChild({ description: name, op, tags, data })) + const sentryContext: TransactionContext = { name, description: name, op, tags, data } + return sentryAdaptor(context, callback, span?.startChild(sentryContext)) }, setData(key, value) { span?.setData(key, value) }, + setHttpStatus(status) { + span?.setHttpStatus(status) + }, setStatus(status) { - if (typeof status === 'number') { - span?.setHttpStatus(status) - } else { - span?.setStatus(status) - } + span?.setStatus(status) + isStatusSet = true }, - setError(error, status = 'unknown_error') { - callbackContext.setStatus(status) + setError(error, status) { span?.setData('error', error) + if (!isStatusSet && !status) status = 'internal_error' + if (status) span?.setStatus(status) }, now() { return performance.now() - start @@ -91,11 +99,21 @@ async function sentryAdaptor(context: TraceContext, callback: TraceCallback(context: TraceContext, callback: TraceCallback): Promise { - const { name, op, tags, data } = context - const sentryContext: TransactionContext = { name, description: name, op, tags, data } - // We use inactive spans so that we can measure two distinct flows at once, without mingling them. - const span = Sentry.startInactiveSpan(sentryContext) - return sentryAdaptor(context, callback, span) + const parentTrace = Zone.current.get('trace') + if (parentTrace) { + return parentTrace?.(context, callback) + } else { + const { name, op, tags, data } = context + const sentryContext: TransactionContext = { name, description: name, op, tags, data } + // We use inactive spans so that we can measure two distinct flows at once, without mingling them. + const span = Sentry.startInactiveSpan(sentryContext) + return sentryAdaptor(context, callback, span) + } } diff --git a/apps/web/src/tracing/types.ts b/apps/web/src/tracing/types.ts index da29d4ece0c..2e3322e4cb7 100644 --- a/apps/web/src/tracing/types.ts +++ b/apps/web/src/tracing/types.ts @@ -26,7 +26,6 @@ export type OpCode = | 'wallet.approve' | 'wallet.connect' | 'wallet.connect.eager' - | 'wallet.estimate_gas' | 'wallet.send_transaction' | 'wallet.sign' | 'wallet.switch_chain' diff --git a/apps/web/src/utils/getColor.test.ts b/apps/web/src/utils/getColor.test.ts deleted file mode 100644 index 0bc906180aa..00000000000 --- a/apps/web/src/utils/getColor.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { DEFAULT_COLOR } from 'constants/tokenColors' -import { - arrayBufferBlackGif, - arrayBufferBlackJpg, - arrayBufferBlackPng, - arrayBufferBlackPngInvalid, - arrayBufferBluePng, - arrayBufferPinkPng, - arrayBufferWhitePng, -} from 'test-utils/images' - -import { getColor } from './getColor' - -function getMockImageFetch(data: Uint8Array, dataType = 'image/png') { - return () => - Promise.resolve({ - ok: true, - status: 200, - headers: { - get: jest.fn().mockReturnValue(dataType), - }, - arrayBuffer: jest.fn().mockResolvedValue(data), - } as unknown as Response) -} - -test('should return the average color of a black PNG image', async () => { - jest.spyOn(global, 'fetch').mockImplementation(getMockImageFetch(arrayBufferBlackPng)) - - const color = await getColor('fakeUrl') - expect(color).toEqual([0, 0, 0]) -}) - -test('should return the average color of a blue PNG image', async () => { - jest.spyOn(global, 'fetch').mockImplementation(getMockImageFetch(arrayBufferBluePng)) - - const color = await getColor('fakeUrl') - expect(color).toEqual([0, 0, 255]) -}) - -test('should return the average color of a white PNG image', async () => { - jest.spyOn(global, 'fetch').mockImplementation(getMockImageFetch(arrayBufferWhitePng)) - - const color = await getColor('fakeUrl') - expect(color).toEqual([255, 255, 255]) -}) - -test('should return the average color of a white PNG image with whiteness dimmed', async () => { - jest.spyOn(global, 'fetch').mockImplementation(getMockImageFetch(arrayBufferWhitePng)) - - const color = await getColor('fakeUrl', true) - expect(color).toEqual(DEFAULT_COLOR) -}) - -test('should return the average color of a black JPG image', async () => { - jest.spyOn(global, 'fetch').mockImplementation(getMockImageFetch(arrayBufferBlackJpg, 'image/jpeg')) - - const color = await getColor('fakeUrl') - expect(color).toEqual([0, 0, 0]) -}) - -test('should return default color for a GIF image', async () => { - jest.spyOn(global, 'fetch').mockImplementation(getMockImageFetch(arrayBufferBlackGif)) - - const color = await getColor('fakeUrl') - expect(color).toEqual(DEFAULT_COLOR) -}) - -test('should return default color for a invalid image', async () => { - jest.spyOn(global, 'fetch').mockImplementation(getMockImageFetch(arrayBufferBlackPngInvalid)) - - const color = await getColor('fakeUrl') - expect(color).toEqual(DEFAULT_COLOR) -}) - -test('should return pink for a pink PNG image', async () => { - jest.spyOn(global, 'fetch').mockImplementation(getMockImageFetch(arrayBufferPinkPng)) - - const color = await getColor('fakeUrl') - expect(color).toEqual([230, 52, 140]) -}) diff --git a/apps/web/src/utils/getColor.ts b/apps/web/src/utils/getColor.ts deleted file mode 100644 index 5cecde8bfe5..00000000000 --- a/apps/web/src/utils/getColor.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { Buffer } from 'buffer' -import JPEG from 'jpeg-js' -import PNG from 'png-ts' - -import { DEFAULT_COLOR, predefinedTokenColors } from '../constants/tokenColors' - -export async function getColor(image: string | undefined, checkDistance = false) { - if (!image) { - return DEFAULT_COLOR - } - if (image in predefinedTokenColors) { - return predefinedTokenColors[image] - } - try { - const data = await fetch(image) - const buffer = await data.arrayBuffer() - const arrayBuffer = Buffer.from(buffer) - - const type = data.headers.get('content-type') ?? '' - return getAverageColor(arrayBuffer, type, checkDistance) - } catch (e) { - return DEFAULT_COLOR - } -} - -function getAverageColor(arrayBuffer: Uint8Array, type: string, checkDistance: boolean) { - let pixels - switch (type) { - case 'image/png': { - const image = PNG.load(arrayBuffer) - pixels = image.decode() - break - } - case 'image/jpeg' || 'image/jpg': { - const jpeg = JPEG.decode(arrayBuffer, { useTArray: true }) - pixels = jpeg.data - break - } - default: { - return DEFAULT_COLOR - } - } - - const pixelCount = pixels.length / 4 - - let transparentPixels = 0 - - let r = 0 - let g = 0 - let b = 0 - - for (let i = 0; i < pixelCount; i++) { - if (pixels[i * 4 + 3] === 0) { - transparentPixels++ - continue - } - r += pixels[i * 4] - g += pixels[i * 4 + 1] - b += pixels[i * 4 + 2] - } - - r = Math.floor(r / (pixelCount - transparentPixels)) - g = Math.floor(g / (pixelCount - transparentPixels)) - b = Math.floor(b / (pixelCount - transparentPixels)) - - if (checkDistance) { - const distance = Math.sqrt(Math.pow(r - 255, 2) + Math.pow(g - 255, 2) + Math.pow(b - 255, 2)) - - if (distance < 50) { - return DEFAULT_COLOR - } - } - - return [r, g, b] -} diff --git a/apps/web/src/utils/getExplorerLink.test.ts b/apps/web/src/utils/getExplorerLink.test.ts index 0ffc6b585ef..12566d513e8 100644 --- a/apps/web/src/utils/getExplorerLink.test.ts +++ b/apps/web/src/utils/getExplorerLink.test.ts @@ -38,4 +38,7 @@ describe('#getExplorerLink', () => { it('base', () => { expect(getExplorerLink(ChainId.BASE, 'abc', ExplorerDataType.ADDRESS)).toEqual('https://basescan.org/address/abc') }) + it('blast', () => { + expect(getExplorerLink(ChainId.BLAST, 'abc', ExplorerDataType.ADDRESS)).toEqual('https://blastscan.io/address/abc') + }) }) diff --git a/apps/web/src/utils/getExplorerLink.ts b/apps/web/src/utils/getExplorerLink.ts index 4708a77bf9e..c3fb0bb6815 100644 --- a/apps/web/src/utils/getExplorerLink.ts +++ b/apps/web/src/utils/getExplorerLink.ts @@ -15,6 +15,7 @@ const BLOCK_EXPLORER_PREFIXES: { [chainId: number]: string } = { [ChainId.BNB]: 'https://bscscan.com', [ChainId.AVALANCHE]: 'https://snowtrace.io', [ChainId.BASE]: 'https://basescan.org', + [ChainId.BLAST]: 'https://blastscan.io', } export enum ExplorerDataType { diff --git a/apps/web/src/utils/isEmpty.ts b/apps/web/src/utils/isEmpty.ts index 764fbd4d7b5..fd98f79c6fc 100644 --- a/apps/web/src/utils/isEmpty.ts +++ b/apps/web/src/utils/isEmpty.ts @@ -1,3 +1,6 @@ -export function isEmptyObject(obj: object) { +export function isEmptyObject(obj?: object) { + if (!obj) { + return true + } return Object.keys(obj).length === 0 } diff --git a/apps/web/src/utils/openDownloadApp.ts b/apps/web/src/utils/openDownloadApp.ts index 8ad8be2dafd..e5f6e809696 100644 --- a/apps/web/src/utils/openDownloadApp.ts +++ b/apps/web/src/utils/openDownloadApp.ts @@ -1,6 +1,6 @@ import { AppDownloadPlatform, InterfaceElementName, InterfaceEventName } from '@uniswap/analytics-events' import { sendAnalyticsEvent } from 'analytics' -import { isAndroid, isIOS } from 'utils/platform' +import { isWebAndroid, isWebIOS } from 'uniswap/src/utils/platform' // OneLink will direct to App/Play Store or microsite depending on user agent const APP_DOWNLOAD_LINKS: Partial<{ [key in InterfaceElementName]: string }> = { @@ -28,9 +28,9 @@ type OpenDownloadAppOptions = { * I've added a helper `getDownloadAppLinkProps` that unifies this behavior into one thing. */ export function openDownloadApp({ element }: OpenDownloadAppOptions) { - if (isIOS) { + if (isWebIOS) { openDownloadStore({ element, appPlatform: AppDownloadPlatform.IOS, linkTarget: 'uniswap_wallet_appstore' }) - } else if (isAndroid) { + } else if (isWebAndroid) { openDownloadStore({ element, appPlatform: AppDownloadPlatform.ANDROID, linkTarget: 'uniswap_wallet_playstore' }) } else { sendAnalyticsEvent(InterfaceEventName.UNISWAP_WALLET_MICROSITE_OPENED, { element }) diff --git a/apps/web/src/utils/platform.ts b/apps/web/src/utils/platform.ts deleted file mode 100644 index 2b12c5e15d6..00000000000 --- a/apps/web/src/utils/platform.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { UAParser } from 'ua-parser-js' - -// TODO(WEB-3908): Switch to definition in packages/uniswap once fixed -const parser = new UAParser(navigator.userAgent) -const { name: platform } = parser.getOS() -export const isIOS = platform === 'iOS' -export const isAndroid = platform === 'Android' diff --git a/package.json b/package.json index 6fb6d58a7e2..9eb685abe15 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,9 @@ "cypress": "12.12.0", "@babel/preset-env": "7.23.3", "immer": "9.0.21", - "@uniswap/v2-sdk": "4.1.0", + "@uniswap/v2-sdk": "4.3.0", "@apollo/client": "3.9.6", - "@uniswap/sdk-core": "4.1.2", + "@uniswap/sdk-core": "4.2.0", "@react-navigation/routers": "6.1.9", "@react-navigation/core": "6.2.2", "@sideway/formula": "3.0.1", @@ -49,7 +49,6 @@ "multicodec": "3.0.1", "multihashes": "4.0.2", "@ethersproject/hash": "5.7.0", - "jpeg-js": "0.4.4", "json5": "2.2.2", "is-core-module": "2.13.0", "qs": "6.11.0", @@ -117,6 +116,7 @@ "upgrade:tamagui:canary": "yarn up '*tamagui*'@canary '@tamagui/*'@canary", "wallet": "yarn workspace wallet", "web": "yarn workspace @uniswap/interface", + "uniswap": "yarn workspace uniswap", "utilities": "yarn workspace utilities" }, "workspaces": [ diff --git a/packages/ui/package.json b/packages/ui/package.json index 303dd7309a2..f43f65cba04 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -17,11 +17,13 @@ "i18next": "23.10.0", "react": "18.2.0", "react-native": "0.71.13", + "react-native-image-colors": "1.5.2", "react-native-reanimated": "3.3.0", "react-native-safe-area-context": "4.5.0", "react-native-svg": "13.9.0", "tamagui": "1.92.0", - "utilities": "workspace:^" + "utilities": "workspace:^", + "wcag-contrast": "3.0.0" }, "devDependencies": { "@tamagui/animations-moti": "1.92.0", diff --git a/packages/ui/src/assets/graphics/extension-promo-banner-dark.png b/packages/ui/src/assets/graphics/extension-promo-banner-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..a2ac148377d8d17d77aca40f014d70c039ddddbe GIT binary patch literal 52594 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 WwA9-zi@~OM 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/graphics/extension-promo-banner-light.png b/packages/ui/src/assets/graphics/extension-promo-banner-light.png new file mode 100644 index 0000000000000000000000000000000000000000..9b7e3c157d7c25965199d09913399af4b7c40797 GIT binary patch literal 46581 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/graphics/extension-promo-modal-dark.png b/packages/ui/src/assets/graphics/extension-promo-modal-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..c685e698129ec6722a22443d82707e414c98ec12 GIT binary patch literal 342538 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

>YHY^WE1voVJcCJ3_ch*aYy4p^? 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 + + diff --git a/packages/ui/src/assets/icons/document-list.svg b/packages/ui/src/assets/icons/document-list.svg new file mode 100644 index 00000000000..600af640419 --- /dev/null +++ b/packages/ui/src/assets/icons/document-list.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/icons/gallery.svg b/packages/ui/src/assets/icons/gallery.svg new file mode 100644 index 00000000000..305a203464f --- /dev/null +++ b/packages/ui/src/assets/icons/gallery.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/icons/globe.svg b/packages/ui/src/assets/icons/globe.svg index fe35d20c1d6..69b292045c3 100644 --- a/packages/ui/src/assets/icons/globe.svg +++ b/packages/ui/src/assets/icons/globe.svg @@ -1,5 +1,3 @@ - - - - + + diff --git a/packages/ui/src/assets/icons/mobile.svg b/packages/ui/src/assets/icons/mobile.svg new file mode 100644 index 00000000000..94b83dedd0d --- /dev/null +++ b/packages/ui/src/assets/icons/mobile.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/icons/person.svg b/packages/ui/src/assets/icons/person.svg new file mode 100644 index 00000000000..5b6d203c6f3 --- /dev/null +++ b/packages/ui/src/assets/icons/person.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/icons/settings.svg b/packages/ui/src/assets/icons/settings.svg index f9c3409f774..5cc3be198ba 100644 --- a/packages/ui/src/assets/icons/settings.svg +++ b/packages/ui/src/assets/icons/settings.svg @@ -1,3 +1,3 @@ - + diff --git a/packages/ui/src/assets/icons/time-past.svg b/packages/ui/src/assets/icons/time-past.svg new file mode 100644 index 00000000000..6dc5b80c46c --- /dev/null +++ b/packages/ui/src/assets/icons/time-past.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/index.ts b/packages/ui/src/assets/index.ts index aeeda4249a3..bce06a0e993 100644 --- a/packages/ui/src/assets/index.ts +++ b/packages/ui/src/assets/index.ts @@ -4,7 +4,6 @@ export const ARBITRUM_LOGO = require('./logos/png/arbitrum-logo.png') export const BASE_LOGO = require('./logos/png/base-logo.png') export const BNB_LOGO = require('./logos/png/bnb-logo.png') export const POLYGON_LOGO = require('./logos/png/polygon-logo.png') -export const BLAST_LOGO = require('./logos/png/blast-logo.png') export const GOERLI_LOGO = require('./logos/png/goerli-logo.png') export const MUMBAI_LOGO = require('./logos/png/mumbai-logo.png') export const UNISWAP_LOGO = require('./logos/png/uniswap-logo.png') @@ -19,6 +18,11 @@ export const UNITAGS_BANNER_VERTICAL_DARK = require('./graphics/unitags-banner-v export const UNITAGS_INTRO_BANNER_LIGHT = require('./graphics/unitags-intro-banner-light.png') export const UNITAGS_INTRO_BANNER_DARK = require('./graphics/unitags-intro-banner-dark.png') +export const EXTENSION_PROMO_BANNER_LIGHT = require('./graphics/extension-promo-banner-light.png') +export const EXTENSION_PROMO_BANNER_DARK = require('./graphics/extension-promo-banner-dark.png') +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 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/assets/logos/png/blast-logo.png b/packages/ui/src/assets/logos/png/blast-logo.png deleted file mode 100644 index 40f86848b066cfeebbd9ed0636e727d16271148b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4823 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 ( - + diff --git a/packages/ui/src/components/icons/Code.tsx b/packages/ui/src/components/icons/Code.tsx new file mode 100644 index 00000000000..38644603dbc --- /dev/null +++ b/packages/ui/src/components/icons/Code.tsx @@ -0,0 +1,17 @@ +import { Path, Svg } from 'react-native-svg' + +// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths +import { createIcon } from '../factories/createIcon' + +export const [Code, AnimatedCode] = createIcon({ + name: 'Code', + getIcon: (props) => ( + + + + ), + defaultFill: '#7D7D7D', +}) diff --git a/packages/ui/src/components/icons/DocumentList.tsx b/packages/ui/src/components/icons/DocumentList.tsx new file mode 100644 index 00000000000..b92d9957be3 --- /dev/null +++ b/packages/ui/src/components/icons/DocumentList.tsx @@ -0,0 +1,17 @@ +import { Path, Svg } from 'react-native-svg' + +// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths +import { createIcon } from '../factories/createIcon' + +export const [DocumentList, AnimatedDocumentList] = createIcon({ + name: 'DocumentList', + getIcon: (props) => ( + + + + ), + defaultFill: '#7D7D7D', +}) diff --git a/packages/ui/src/components/icons/FileListLock.tsx b/packages/ui/src/components/icons/FileListLock.tsx index fbeaabdbec7..1b8bfd35284 100644 --- a/packages/ui/src/components/icons/FileListLock.tsx +++ b/packages/ui/src/components/icons/FileListLock.tsx @@ -6,9 +6,9 @@ import { createIcon } from '../factories/createIcon' export const [FileListLock, AnimatedFileListLock] = createIcon({ name: 'FileListLock', getIcon: (props) => ( - + diff --git a/packages/ui/src/components/icons/Gallery.tsx b/packages/ui/src/components/icons/Gallery.tsx new file mode 100644 index 00000000000..d96f5d1fa07 --- /dev/null +++ b/packages/ui/src/components/icons/Gallery.tsx @@ -0,0 +1,17 @@ +import { Path, Svg } from 'react-native-svg' + +// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths +import { createIcon } from '../factories/createIcon' + +export const [Gallery, AnimatedGallery] = createIcon({ + name: 'Gallery', + getIcon: (props) => ( + + + + ), + defaultFill: '#9B9B9B', +}) diff --git a/packages/ui/src/components/icons/Globe.tsx b/packages/ui/src/components/icons/Globe.tsx index 6edbe9d671a..d30bce90b42 100644 --- a/packages/ui/src/components/icons/Globe.tsx +++ b/packages/ui/src/components/icons/Globe.tsx @@ -6,27 +6,12 @@ import { createIcon } from '../factories/createIcon' export const [Globe, AnimatedGlobe] = createIcon({ name: 'Globe', getIcon: (props) => ( - + - - ), diff --git a/packages/ui/src/components/icons/Mobile.tsx b/packages/ui/src/components/icons/Mobile.tsx index 09d466614f2..33443a74f4d 100644 --- a/packages/ui/src/components/icons/Mobile.tsx +++ b/packages/ui/src/components/icons/Mobile.tsx @@ -3,12 +3,12 @@ import { Path, Svg } from 'react-native-svg' // eslint-disable-next-line no-relative-import-paths/no-relative-import-paths import { createIcon } from '../factories/createIcon' -export const [Mobile, AnimatedMobileSend] = createIcon({ +export const [Mobile, AnimatedMobile] = createIcon({ name: 'Mobile', getIcon: (props) => ( - + diff --git a/packages/ui/src/components/icons/Person.tsx b/packages/ui/src/components/icons/Person.tsx new file mode 100644 index 00000000000..4d2ed83633a --- /dev/null +++ b/packages/ui/src/components/icons/Person.tsx @@ -0,0 +1,17 @@ +import { Path, Svg } from 'react-native-svg' + +// eslint-disable-next-line no-relative-import-paths/no-relative-import-paths +import { createIcon } from '../factories/createIcon' + +export const [Person, AnimatedPerson] = createIcon({ + name: 'Person', + getIcon: (props) => ( + + + + ), + defaultFill: '#9B9B9B', +}) diff --git a/packages/ui/src/components/icons/Settings.tsx b/packages/ui/src/components/icons/Settings.tsx index 140361596cb..77ffc487c6f 100644 --- a/packages/ui/src/components/icons/Settings.tsx +++ b/packages/ui/src/components/icons/Settings.tsx @@ -8,7 +8,7 @@ export const [Settings, AnimatedSettings] = createIcon({ getIcon: (props) => ( diff --git a/packages/ui/src/components/icons/TimePast.tsx b/packages/ui/src/components/icons/TimePast.tsx index f2d709d0889..35f840d1815 100644 --- a/packages/ui/src/components/icons/TimePast.tsx +++ b/packages/ui/src/components/icons/TimePast.tsx @@ -6,9 +6,9 @@ import { createIcon } from '../factories/createIcon' export const [TimePast, AnimatedTimePast] = createIcon({ name: 'TimePast', getIcon: (props) => ( - + diff --git a/packages/ui/src/components/icons/index.ts b/packages/ui/src/components/icons/index.ts index 685c4d20ccb..7190d14f8bc 100644 --- a/packages/ui/src/components/icons/index.ts +++ b/packages/ui/src/components/icons/index.ts @@ -34,6 +34,7 @@ export * from './Clipboard' export * from './ClipboardPaste' export * from './Clock' export * from './Cloud' +export * from './Code' export * from './Coffee' export * from './Coin' export * from './CoinConvert' @@ -46,6 +47,7 @@ export * from './CopySheets' export * from './DaiIcon' export * from './Dash' export * from './DiamondExclamation' +export * from './DocumentList' export * from './Dollar' export * from './DoubleChevron' export * from './Edit' @@ -69,6 +71,7 @@ export * from './FileListCheck' export * from './FileListLock' export * from './Fingerprint' export * from './Flashbots' +export * from './Gallery' export * from './Gas' export * from './Global' export * from './Globe' @@ -113,6 +116,7 @@ export * from './PenLine' export * from './Pencil' export * from './PencilBox' export * from './PencilDetailed' +export * from './Person' export * from './Photo' export * from './Pin' export * from './Plus' diff --git a/packages/ui/src/env.d.ts b/packages/ui/src/env.d.ts index 774de44b5b6..8f9e3d2541f 100644 --- a/packages/ui/src/env.d.ts +++ b/packages/ui/src/env.d.ts @@ -1,3 +1,5 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// import { config } from './tamagui.config' type Conf = typeof config diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index ecf003c3ac5..70f46cb0d56 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -80,4 +80,5 @@ export * from './loading/Skeleton' export * from './loading/TransactionLoader' export { config as tamaguiConfig } from './tamagui.config' export * from './theme/shadows' +export * from './utils/colors' export * from './utils/haptics/HapticFeedback' diff --git a/packages/ui/src/theme/color/colors.ts b/packages/ui/src/theme/color/colors.ts index ee8caa632e9..35576dd1fc3 100644 --- a/packages/ui/src/theme/color/colors.ts +++ b/packages/ui/src/theme/color/colors.ts @@ -159,7 +159,6 @@ export const colors = { networkArbitrum: '#28A0F0', networkBsc: '#F0B90B', networkBase: '#0052FF', - networkBlast: '#FCFC03', fiatOnRampBanner: '#FB36D0', } @@ -274,7 +273,6 @@ export const colorsLight = { chain_42161: colors.networkArbitrum, chain_80001: colors.networkPolygon, chain_8453: colors.networkBase, - chain_81457: colors.networkBlast, chain_56: colors.networkBsc, } @@ -337,6 +335,5 @@ export const colorsDark = { chain_42161: colors.networkArbitrum, chain_80001: colors.networkPolygon, chain_8453: colors.networkBase, - chain_81457: colors.networkBlast, chain_56: colors.networkBsc, } diff --git a/packages/ui/src/theme/fonts.ts b/packages/ui/src/theme/fonts.ts index 16ec25309b8..6944dff0acc 100644 --- a/packages/ui/src/theme/fonts.ts +++ b/packages/ui/src/theme/fonts.ts @@ -38,96 +38,99 @@ const platformFontFamily = ( return fontFamily.sansSerif[family] } +const BOOK_WEIGHT = '400' +const MEDIUM_WEIGHT = '500' + export const fonts = { heading1: { family: platformFontFamily('book'), fontSize: adjustedSize(52), lineHeight: 60, - fontWeight: '400', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.2, }, heading2: { family: platformFontFamily('book'), fontSize: adjustedSize(36), lineHeight: 44, - fontWeight: '400', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.2, }, heading3: { family: platformFontFamily('book'), fontSize: adjustedSize(24), lineHeight: 32, - fontWeight: '400', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.2, }, subheading1: { family: platformFontFamily('book'), fontSize: adjustedSize(18), lineHeight: 24, - fontWeight: '400', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.4, }, subheading2: { family: platformFontFamily('book'), fontSize: adjustedSize(16), lineHeight: 24, - fontWeight: '400', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.4, }, body1: { family: platformFontFamily('book'), fontSize: adjustedSize(18), lineHeight: 24, - fontWeight: '400', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.4, }, body2: { family: platformFontFamily('book'), fontSize: adjustedSize(16), lineHeight: 24, - fontWeight: '400', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.4, }, body3: { family: platformFontFamily('book'), fontSize: adjustedSize(14), lineHeight: 20, - fontWeight: '400', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.4, }, body4: { - family: platformFontFamily('medium'), + family: platformFontFamily('book'), fontSize: adjustedSize(12), lineHeight: 16, - fontWeight: '500', + fontWeight: BOOK_WEIGHT, maxFontSizeMultiplier: 1.4, }, buttonLabel1: { family: platformFontFamily('medium'), fontSize: adjustedSize(20), lineHeight: 24, - fontWeight: '500', + fontWeight: MEDIUM_WEIGHT, maxFontSizeMultiplier: 1.2, }, buttonLabel2: { family: platformFontFamily('medium'), fontSize: adjustedSize(18), lineHeight: 24, - fontWeight: '500', + fontWeight: MEDIUM_WEIGHT, maxFontSizeMultiplier: 1.2, }, buttonLabel3: { family: platformFontFamily('medium'), fontSize: adjustedSize(16), lineHeight: 24, - fontWeight: '500', + fontWeight: MEDIUM_WEIGHT, maxFontSizeMultiplier: 1.2, }, buttonLabel4: { family: platformFontFamily('medium'), fontSize: adjustedSize(14), lineHeight: 16, - fontWeight: '500', + fontWeight: MEDIUM_WEIGHT, maxFontSizeMultiplier: 1.2, }, monospace: { @@ -143,7 +146,7 @@ const baselMedium = isWeb : 'Basel-Medium' const baselBook = isWeb - ? 'Basel, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif' + ? 'Basel-Book, -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif' : 'Basel-Book' export const headingFont = createFont({ diff --git a/packages/ui/src/utils/colors.ts b/packages/ui/src/utils/colors.ts new file mode 100644 index 00000000000..e48a620f5d2 --- /dev/null +++ b/packages/ui/src/utils/colors.ts @@ -0,0 +1,356 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import ImageColors from 'react-native-image-colors' +import { useIsDarkMode } from 'ui/src/hooks/useIsDarkMode' +import { useSporeColors } from 'ui/src/hooks/useSporeColors' +import { ColorKeys, colorsLight } from 'ui/src/theme' +import { isSVGUri } from 'utilities/src/format/urls' +import { useAsyncData } from 'utilities/src/react/hooks' +import { hex } from 'wcag-contrast' + +/** The contrast threshold for token colors is currently lower than the WCAG AA standard of 3.0 because a slightly lower threshold leads to better results right now due to imitations of the color extraction library. */ +const MIN_TOKEN_COLOR_CONTRAST_THRESHOLD = 1.95 + +export type ExtractedColors = { + primary?: string + secondary?: string + base?: string + detail?: string +} + +const specialCaseTokenColors: { [key: string]: string } = { + // old WBTC + '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', + // DAI + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png': + '#FAB01B', + // UNI + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png': + '#E6358C', + // BUSD + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4Fabb145d64652a948d72533023f6E7A623C7C53/logo.png': + '#EFBA09', + // AI-X + 'https://s2.coinmarketcap.com/static/img/coins/64x64/26984.png': '#29A1F1', + // 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', + // 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', + // USDC + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png': + '#0066D9', + // HEX + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png': + '#F93F8C', + // MONG + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C/logo.png': + '#A96DFF', + // ARB + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1/logo.png': + '#29A1F1', + // PSYOP + 'https://s2.coinmarketcap.com/static/img/coins/64x64/25422.png': '#E88F00', + // MATIC + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png': + '#A96DFF', + // TURBO + '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', + // SIMPSON + 'https://assets.coingecko.com/coins/images/30243/large/1111.png?1683692033': '#E88F00', + // MAKER + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2/logo.png': + '#50B197', + // 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', + // APE + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4d224452801ACEd8B2F0aebE155379bb5D594381/logo.png': + '#054AA9', + // GUSD + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd/logo.png': + '#00A4BD', + // OGN + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png': + '#054AA9', + // RPL + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xD33526068D116cE69F19A9ee46F0bd304F21A51f/logo.png': + '#FF7B4F', +} + +const blackAndWhiteSpecialCase: Set = new Set([ + // QNT + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4a220E6096B25EADb88358cb44068A3248254675/logo.png', + // Xen + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x06450dEe7FD2Fb8E39061434BAbCFC05599a6Fb8/logo.png', + // FWB + 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x35bD01FC9d6D5D81CA9E055Db88Dc49aa2c699A8/logo.png', +]) + +export async function getExtractedColors( + imageUrl: Maybe, + fallback: ColorKeys = 'accent1', + cache = true +): Promise { + if (!imageUrl) { + return + } + + const imageColors = await ImageColors.getColors(imageUrl, { + key: imageUrl, + ...(fallback && { fallback }), + ...(cache && { cache }), + }) + + switch (imageColors.platform) { + case 'android': + return { + primary: imageColors.dominant, + base: imageColors.average, + detail: imageColors.vibrant, + } + case 'ios': + return { + primary: imageColors.primary, + secondary: imageColors.secondary, + base: imageColors.background, + detail: imageColors.detail, + } + case 'web': + return { + primary: imageColors.dominant, + detail: imageColors.vibrant, + } + } +} + +export function useExtractedColors( + imageUrl: Maybe, + fallback: ColorKeys = 'accent1', + cache = true +): { colors?: ExtractedColors; colorsLoading: boolean } { + const getImageColors = useCallback( + async () => getExtractedColors(imageUrl, fallback, cache), + [imageUrl, fallback, cache] + ) + + const { data: colors, isLoading: colorsLoading } = useAsyncData(getImageColors) + + return { colors, colorsLoading } +} + +function getSpecialCaseTokenColor(imageUrl: Maybe, isDarkMode: boolean): Nullable { + if (imageUrl && blackAndWhiteSpecialCase.has(imageUrl)) { + return isDarkMode ? '#FFFFFF' : '#000000' + } + + if (!imageUrl || !specialCaseTokenColors[imageUrl]) { + return null + } + + return specialCaseTokenColors[imageUrl] ?? null +} +/** + * Picks a contrast-passing color from a given token image URL and background color. + * The color extracting library will return a few options, and this function will + * try to pick the best of those given options. + * + * Usage: + * + * ```ts + * const { tokenColor, tokenColorLoading } = useExtractedTokenColor( + * tokenImageUrl, + * theme.colors.surface1, + * theme.colors.neutral3 + * ) + * ``` + * + * @param imageUrl The URL of the image to extract a color from + * @param tokenName The ticker of the asset (used to derive a color when no logo is available) + * @param backgroundColor The hex value of the background color to check contrast against + * @param defaultColor The color that will be returned while the extraction is still loading + * @returns The extracted color as a hex code string + */ +export function useExtractedTokenColor( + imageUrl: Maybe, + tokenName: Maybe, + backgroundColor: string, + defaultColor: string +): { tokenColor: Nullable; tokenColorLoading: boolean } { + const sporeColors = useSporeColors() + const { colors, colorsLoading } = useExtractedColors(imageUrl) + const [tokenColor, setTokenColor] = useState(defaultColor) + const [tokenColorLoading, setTokenColorLoading] = useState(true) + const isDarkMode = useIsDarkMode() + const logolessColorScheme = useLogolessColorScheme(tokenName ?? '') + + useEffect(() => { + if (!colorsLoading && !!colors) { + setTokenColor(pickContrastPassingTokenColor(colors, backgroundColor)) + setTokenColorLoading(false) + } + }, [backgroundColor, colors, colorsLoading]) + + const specialCaseTokenColor = useMemo(() => { + return getSpecialCaseTokenColor(imageUrl, isDarkMode) + }, [imageUrl, isDarkMode]) + + if (specialCaseTokenColor) { + return { tokenColor: specialCaseTokenColor, tokenColorLoading: false } + } + + if (isSVGUri(imageUrl)) { + // Fall back to a more neutral color for SVG's since they fail extraction but we can render them elsewhere + return { tokenColor: sporeColors.neutral1?.val, tokenColorLoading: false } + } + + if (!imageUrl) { + const { foreground } = isDarkMode ? logolessColorScheme.dark : logolessColorScheme.light + return { tokenColor: foreground, tokenColorLoading: false } + } + + return { tokenColor, tokenColorLoading } +} + +enum LOGOLESS_COLORS { + PINK = 'PINK', + ORANGE = 'ORANGE', + YELLOW = 'YELLOW', + GREEN = 'GREEN', + TURQUOISE = 'TURQUOISE', + CYAN = 'CYAN', + BLUE = 'BLUE', + PURPLE = 'PURPLE', +} + +type ColorScheme = { + light: { foreground: string; background: string } + dark: { foreground: string; background: string } +} + +type LogolessColorSchemes = { + [key in LOGOLESS_COLORS]: ColorScheme +} + +const logolessColorSchemes: LogolessColorSchemes = { + // TODO (MOB-2417): update the colors in the global colors file to these and pull from there + [LOGOLESS_COLORS.PINK]: { + light: { foreground: '#FC74FE', background: '#FEF4FF' }, + dark: { foreground: '#FC74FE', background: '#361A37' }, + }, + [LOGOLESS_COLORS.ORANGE]: { + light: { foreground: '#FF7715', background: '#FFF2F1' }, + dark: { foreground: '#FF7715', background: '#2E0805' }, + }, + [LOGOLESS_COLORS.YELLOW]: { + light: { foreground: '#FFBF17', background: '#FFFCF2' }, + dark: { foreground: '#FFF612', background: '#1F1E02' }, + }, + [LOGOLESS_COLORS.GREEN]: { + light: { foreground: '#2FBA61', background: '#EEFBF1' }, + dark: { foreground: '#2FBA61', background: '#0F2C1A' }, + }, + [LOGOLESS_COLORS.TURQUOISE]: { + light: { foreground: '#00C3A0', background: '#F7FEEB' }, + dark: { foreground: '#5CFE9D', background: '#1A2A21' }, + }, + [LOGOLESS_COLORS.CYAN]: { + light: { foreground: '#2ABDFF', background: '#EBF8FF' }, + dark: { foreground: '#2ABDFF', background: '#15242B' }, + }, + [LOGOLESS_COLORS.BLUE]: { + light: { foreground: '#3271FF', background: '#EFF4FF' }, + dark: { foreground: '#3271FF', background: '#10143D' }, + }, + [LOGOLESS_COLORS.PURPLE]: { + light: { foreground: '#9E62FF', background: '#FAF5FF' }, + dark: { foreground: '#9E62FF', background: '#1A0040' }, + }, +} + +function getLogolessColorIndex(tokenName: string, numOptions: number): number { + const charCodes = Array.from(tokenName).map((char) => char.charCodeAt(0)) + const sum = charCodes.reduce((acc, curr) => acc + curr, 0) + return sum % numOptions +} + +/** + * Picks a color scheme for a token that doesn't have a logo. + * The color scheme is derived from the characters of the token name and will only change if the name changes + * @param tokenName The name of the token + * @returns a light and dark version of a color scheme with a foreground and background color + */ +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] + ] + }, [tokenName]) +} + +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 + } + + const contrast = hex(color, backgroundColor) + return contrast >= contrastThreshold +} + +/** + * Picks a contrast-passing color from a given few that are returned from the color extraction library. + * + * @param extractedColors An object of `background`, `primary`, `detail`, and `secondary` colors that + * the color extraction library returns for a given image URL + * @param backgroundHex The hex value of the background color to check the contrast of the resulting + * color against + * @returns a hex code that will pass a contrast check against the background + */ +function pickContrastPassingTokenColor( + extractedColors: ExtractedColors, + backgroundHex: string +): string { + const colorsInOrder = [ + extractedColors.base, + extractedColors.detail, + extractedColors.secondary, + extractedColors.primary, + ] as const + + // TODO(MOB-643): Define more robust color extraction logic. Some ideas: + // - compute all extracted colors and find the highest contrast one (that isn't #000000 or #FFFFFF) + // - bump color until it passes contrast: e.g. `import { lighten, desaturate } from 'polished'` + // - locally cache the result with the image logo URL as a key + // - move this logic to the backend + + for (const c of colorsInOrder) { + if (!!c && passesContrast(c, backgroundHex, MIN_TOKEN_COLOR_CONTRAST_THRESHOLD)) { + return c + } + } + + return colorsLight.accent1 +} diff --git a/packages/uniswap/package.json b/packages/uniswap/package.json index 7aa8b540703..066ce019eaa 100644 --- a/packages/uniswap/package.json +++ b/packages/uniswap/package.json @@ -2,7 +2,7 @@ "name": "uniswap", "version": "0.0.0", "scripts": { - "prepare": "yarn contracts && yarn i18n:generate", + "prepare": "yarn contracts && yarn i18n:generate && yarn graphql:generate", "contracts:compile:abi": "typechain --target ethers-v5 --out-dir src/abis/types \"./src/abis/**/*.json\"", "contracts:compile:v3": "typechain --target ethers-v5 --out-dir src/abis/types/v3 \"../../node_modules/@uniswap/**/artifacts/contracts/**/*[!dbg].json\"", "contracts": "yarn contracts:compile:abi && yarn contracts:compile:v3", @@ -33,6 +33,7 @@ "@graphql-codegen/typescript-react-apollo": "^3.3.7", "@graphql-codegen/typescript-resolvers": "^3.2.1", "@uniswap/eslint-config": "workspace:^", + "@uniswap/sdk-core": "4.2.0", "depcheck": "1.4.7", "eslint": "8.44.0", "get-graphql-schema": "^2.1.2", diff --git a/packages/uniswap/src/constants/urls.ts b/packages/uniswap/src/constants/urls.ts index 3974d0ad30d..f1733f11775 100644 --- a/packages/uniswap/src/constants/urls.ts +++ b/packages/uniswap/src/constants/urls.ts @@ -11,14 +11,15 @@ export const uniswapUrls = { helpUrl, helpRequestUrl: `${helpUrl}/hc/en-us/requests/new`, helpArticleUrls: { + extensionWaitlist: `${helpUrl}/hc/en-us/articles/24458735271181-Get-started-with-the-Uniswap-Extension`, feeOnTransferHelp: `${helpUrl}/hc/en-us/articles/18673568523789-What-is-a-token-fee-`, moonpayHelp: `${helpUrl}/hc/en-us/articles/11306574799117-How-to-use-Moon-Pay-on-the-Uniswap-web-app-`, networkFeeInfo: `${helpUrl}/hc/en-us/articles/8370337377805-What-is-a-network-fee-`, - swapFeeInfo: `${helpUrl}/hc/en-us/articles/20131678274957`, recoveryPhraseHelp: `${helpUrl}/hc/en-us/articles/11380692567949-How-to-import-my-recovery-phrase-`, - swapSlippage: `${helpUrl}/hc/en-us/articles/8643879653261-What-is-Price-Slippage-`, - swapProtection: `${helpUrl}/hc/en-us/articles/18814993155853`, supportedNetworks: `${helpUrl}/hc/en-us/articles/14569415293325`, + swapFeeInfo: `${helpUrl}/hc/en-us/articles/20131678274957`, + swapProtection: `${helpUrl}/hc/en-us/articles/18814993155853`, + swapSlippage: `${helpUrl}/hc/en-us/articles/8643879653261-What-is-Price-Slippage-`, tokenWarning: `${helpUrl}/hc/en-us/articles/8723118437133-What-are-token-warnings-`, unitagClaimPeriod: `${helpUrl}/hc/en-us/articles/24009960408589`, walletHelp: `${helpUrl}/hc/en-us/categories/11301970439565-Uniswap-Wallet`, diff --git a/packages/uniswap/src/data/cache.ts b/packages/uniswap/src/data/cache.ts index b20c51c5cbb..4033e091629 100644 --- a/packages/uniswap/src/data/cache.ts +++ b/packages/uniswap/src/data/cache.ts @@ -63,16 +63,21 @@ export function setupWalletCache(): InMemoryCache { * see https://music.youtube.com/watch?v=twd4Pb4o_fU&feature=share */ - // simply use chain / address pair as id instead for tokens - token: { - read(_, { args, toReference }): Reference | undefined { - return toReference({ - __typename: 'Token', - chain: args?.chain, - address: args?.address?.toLowerCase(), - }) - }, - }, + // Cache redirects don't work in test environment, so we don't use them in tests + ...(process.env.NODE_ENV !== 'test' + ? { + // simply use chain / address pair as id instead for tokens + token: { + read(_, { args, toReference }): Reference | undefined { + return toReference({ + __typename: 'Token', + chain: args?.chain, + address: args?.address?.toLowerCase(), + }) + }, + }, + } + : {}), // Ignore `valueModifiers` when caching `portfolios`. // IMPORTANT: This assumes that `valueModifiers` are always the same when querying `portfolios` across the entire app. 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 77794d301fb..ed2cc48de99 100644 --- a/packages/uniswap/src/data/graphql/uniswap-data-api/queries.graphql +++ b/packages/uniswap/src/data/graphql/uniswap-data-api/queries.graphql @@ -301,7 +301,7 @@ query PortfolioBalances( ) { portfolios( ownerAddresses: [$ownerAddress] - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] valueModifiers: $valueModifiers ) { id @@ -355,7 +355,7 @@ query MultiplePortfolioBalances( ) { portfolios( ownerAddresses: $ownerAddresses - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] valueModifiers: $valueModifiers ) { id @@ -407,7 +407,7 @@ query MultiplePortfolioBalances( query SelectWalletScreen($ownerAddresses: [String!]!) { portfolios( ownerAddresses: $ownerAddresses - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] ) { id ownerAddress @@ -420,7 +420,7 @@ query SelectWalletScreen($ownerAddresses: [String!]!) { query TransactionHistoryUpdater($addresses: [String!]!) { portfolios( ownerAddresses: $addresses - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] ) { id ownerAddress @@ -543,13 +543,13 @@ query TokenProjects($contracts: [ContractInput!]!) { query TransactionList($address: String!) { portfolios( ownerAddresses: [$address] - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] ) { id assetActivities( pageSize: 100 page: 1 - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] ) { id timestamp @@ -638,14 +638,14 @@ query TransactionList($address: String!) { query FeedTransactionList($addresses: [String!]!) { portfolios( ownerAddresses: $addresses - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] ) { id ownerAddress assetActivities( pageSize: 30 page: 1 - chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB, BLAST] + chains: [ETHEREUM, POLYGON, ARBITRUM, OPTIMISM, BASE, BNB] ) { id timestamp 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 ab360520f84..19837f49c1c 100644 --- a/packages/uniswap/src/data/graphql/uniswap-data-api/schema.graphql +++ b/packages/uniswap/src/data/graphql/uniswap-data-api/schema.graphql @@ -2,20 +2,14 @@ directive @defer on FIELD """ -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 an API key. """ -directive @aws_publish( - """ - List of subscriptions which will be published to when this mutation is called. - """ - subscriptions: [String] -) on FIELD_DEFINITION +directive @aws_api_key on OBJECT | FIELD_DEFINITION """ -Tells the service this field/object has access authorized by an API key. +Tells the service this field/object has access authorized by sigv4 signing. """ -directive @aws_api_key on OBJECT | FIELD_DEFINITION +directive @aws_iam on OBJECT | FIELD_DEFINITION """ Tells the service this field/object has access authorized by a Cognito User Pools token. @@ -25,28 +19,26 @@ directive @aws_cognito_user_pools( cognito_groups: [String] ) on OBJECT | FIELD_DEFINITION -"""Tells the service which mutation triggers this subscription.""" -directive @aws_subscribe( +""" +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_publish( """ - List of mutations which will trigger this subscription when they are called. + List of subscriptions which will be published to when this mutation is called. """ - mutations: [String] + subscriptions: [String] ) on FIELD_DEFINITION -""" -Tells the service this field/object has access authorized by an OIDC token. -""" -directive @aws_oidc on OBJECT | 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 sigv4 signing. +Tells the service this field/object has access authorized by an OIDC token. """ -directive @aws_iam on OBJECT | FIELD_DEFINITION +directive @aws_oidc on OBJECT | FIELD_DEFINITION """Directs the schema to enforce authorization on a field""" directive @aws_auth( @@ -54,6 +46,14 @@ directive @aws_auth( cognito_groups: [String] ) on FIELD_DEFINITION +"""Tells the service which mutation triggers this subscription.""" +directive @aws_subscribe( + """ + List of mutations which will trigger this subscription when they are called. + """ + mutations: [String] +) on FIELD_DEFINITION + """ Types, unions, and inputs (alphabetized): These are colocated to highlight the relationship between some types and their inputs. @@ -972,6 +972,7 @@ enum SwapOrderStatus { enum SwapOrderType { DUTCH LIMIT + DUTCH_V2 } type TimestampedAmount implements IAmount { diff --git a/packages/uniswap/src/data/graphql/uniswap-data-api/web/search.graphql b/packages/uniswap/src/data/graphql/uniswap-data-api/web/search.graphql index 7ccd52ff3fb..49bfea3bdb5 100644 --- a/packages/uniswap/src/data/graphql/uniswap-data-api/web/search.graphql +++ b/packages/uniswap/src/data/graphql/uniswap-data-api/web/search.graphql @@ -32,8 +32,8 @@ query TrendingTokens($chain: Chain!) { } } -query SearchTokensWeb($searchQuery: String!) { - searchTokens(searchQuery: $searchQuery) { +query SearchTokensWeb($searchQuery: String!, $chains: [Chain!]) { + searchTokens(searchQuery: $searchQuery, chains: $chains) { id decimals name diff --git a/packages/uniswap/src/features/chains/utils.test.ts b/packages/uniswap/src/features/chains/utils.test.ts new file mode 100644 index 00000000000..0f81e19b505 --- /dev/null +++ b/packages/uniswap/src/features/chains/utils.test.ts @@ -0,0 +1,13 @@ +import { ChainId } from '@uniswap/sdk-core' +import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { toGraphQLChain } from 'uniswap/src/features/chains/utils' + +describe(toGraphQLChain, () => { + it('handles supported chain', () => { + expect(toGraphQLChain(ChainId.MAINNET)).toEqual(Chain.Ethereum) + }) + + it('handle unsupported chain', () => { + expect(toGraphQLChain(7)).toEqual(undefined) + }) +}) diff --git a/packages/uniswap/src/features/chains/utils.ts b/packages/uniswap/src/features/chains/utils.ts new file mode 100644 index 00000000000..99423e1e0f1 --- /dev/null +++ b/packages/uniswap/src/features/chains/utils.ts @@ -0,0 +1,36 @@ +import { ChainId } from '@uniswap/sdk-core' +import { Chain } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' + +export function toGraphQLChain(chainId: ChainId | number): Chain | undefined { + switch (chainId) { + case ChainId.MAINNET: + return Chain.Ethereum + case ChainId.ARBITRUM_ONE: + return Chain.Arbitrum + case ChainId.ARBITRUM_GOERLI: + return Chain.Arbitrum + case ChainId.GOERLI: + return Chain.EthereumGoerli + case ChainId.SEPOLIA: + return Chain.EthereumSepolia + case ChainId.OPTIMISM: + return Chain.Optimism + case ChainId.OPTIMISM_GOERLI: + return Chain.Optimism + case ChainId.POLYGON: + return Chain.Polygon + case ChainId.POLYGON_MUMBAI: + return Chain.Polygon + case ChainId.BASE: + return Chain.Base + case ChainId.BNB: + return Chain.Bnb + case ChainId.AVALANCHE: + return Chain.Avalanche + case ChainId.CELO: + return Chain.Celo + case ChainId.CELO_ALFAJORES: + return Chain.Celo + } + return undefined +} diff --git a/packages/wallet/src/features/dataApi/types.ts b/packages/uniswap/src/features/dataApi/types.ts similarity index 91% rename from packages/wallet/src/features/dataApi/types.ts rename to packages/uniswap/src/features/dataApi/types.ts index ce744ea3bfd..0289da05595 100644 --- a/packages/wallet/src/features/dataApi/types.ts +++ b/packages/uniswap/src/features/dataApi/types.ts @@ -1,6 +1,6 @@ import { Currency } from '@uniswap/sdk-core' import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { CurrencyId } from 'wallet/src/utils/currencyId' +import { CurrencyId } from 'uniswap/src/types/currency' export type CurrencyInfo = { currency: Currency diff --git a/packages/uniswap/src/features/experiments/flags.ts b/packages/uniswap/src/features/experiments/flags.ts index 32ed7c74150..e84a701600d 100644 --- a/packages/uniswap/src/features/experiments/flags.ts +++ b/packages/uniswap/src/features/experiments/flags.ts @@ -11,8 +11,10 @@ export enum FeatureFlags { Unitags, // Wallet + ExtensionOnboarding, FeedTab, ForAggregator, + CexTransfers, GatewayDNSUpdateMobile, LanguageSelection, MevBlocker, @@ -28,7 +30,6 @@ export enum FeatureFlags { // Web Eip6936Enabled, ExitAnimation, - FallbackProvider, GqlTokenLists, LimitsEnabled, LimitsFees, @@ -38,6 +39,7 @@ export enum FeatureFlags { SendEnabled, TraceJsonRpc, UniswapXSyntheticQuote, + UniswapXv2, V2Everywhere, // TODO(WEB-3625): Remove these once we have a generalized system for outage banners. OutageBannerArbitrum, @@ -52,7 +54,6 @@ export const WEB_FEATURE_FLAG_NAMES = new Map([ // Web Specific [FeatureFlags.Eip6936Enabled, 'eip6963_enabled'], [FeatureFlags.ExitAnimation, 'exit_animation'], - [FeatureFlags.FallbackProvider, 'fallback_provider'], [FeatureFlags.GqlTokenLists, 'gql_token_lists'], [FeatureFlags.LimitsEnabled, 'limits_enabled'], [FeatureFlags.LimitsFees, 'limits_fees'], @@ -62,6 +63,7 @@ export const WEB_FEATURE_FLAG_NAMES = new Map([ [FeatureFlags.SendEnabled, 'swap_send'], [FeatureFlags.TraceJsonRpc, 'traceJsonRpc'], [FeatureFlags.UniswapXSyntheticQuote, 'uniswapx_synthetic_quote'], + [FeatureFlags.UniswapXv2, 'uniswapx_v2'], [FeatureFlags.V2Everywhere, 'v2_everywhere'], // TODO(WEB-3625): Remove these once we have a generalized system for outage banners. [FeatureFlags.OutageBannerArbitrum, 'outage_banner_feb_2024_arbitrum'], @@ -75,8 +77,10 @@ export const WALLET_FEATURE_FLAG_NAMES = new Map([ [FeatureFlags.UniconsV2, 'unicons-v2'], [FeatureFlags.Unitags, 'unitags'], // Wallet Specific + [FeatureFlags.ExtensionOnboarding, 'extension-onboarding'], [FeatureFlags.FeedTab, 'feed-tab'], [FeatureFlags.ForAggregator, 'for-aggregator'], + [FeatureFlags.CexTransfers, 'cex-transfers'], [FeatureFlags.GatewayDNSUpdateMobile, 'cloudflare-gateway'], [FeatureFlags.LanguageSelection, 'language-selection'], [FeatureFlags.MevBlocker, 'mev-blocker'], diff --git a/packages/uniswap/src/features/unitags/api.ts b/packages/uniswap/src/features/unitags/api.ts index 3ce7c1a0218..b6de88c1a96 100644 --- a/packages/uniswap/src/features/unitags/api.ts +++ b/packages/uniswap/src/features/unitags/api.ts @@ -5,7 +5,11 @@ import { uniswapUrls } from 'uniswap/src/constants/urls' import { createNewInMemoryCache } from 'uniswap/src/data/cache' import { REQUEST_SOURCE, getVersionHeader } from 'uniswap/src/data/constants' import { useRestQuery } from 'uniswap/src/data/rest' -import { UnitagAddressResponse, UnitagUsernameResponse } from 'uniswap/src/features/unitags/types' +import { + UnitagAddressResponse, + UnitagUsernameResponse, + UnitagWaitlistPositionResponse, +} from 'uniswap/src/features/unitags/types' import { ONE_MINUTE_MS } from 'utilities/src/time/time' const restLink = new RestLink({ @@ -33,7 +37,7 @@ export const unitagsApolloClient = new ApolloClient({ export function addQueryParamsToEndpoint( endpoint: string, - params: Record + params: Record ): string { const url = new URL(endpoint, uniswapUrls.appBaseUrl) // dummy base URL, we only need the path with query params Object.entries(params).forEach(([key, value]) => { @@ -76,3 +80,21 @@ export function useUnitagByAddressQuery( unitagsApolloClient ) } + +export function useWaitlistPositionQuery( + accounts: Address[], + skip: boolean +): ReturnType> { + const addresses = accounts.join(',') + return useRestQuery>( + addQueryParamsToEndpoint('/waitlist/position', { addresses }), + { addresses }, // dummy body so that cache key is unique per query params + ['isAccepted', 'waitlistPosition'], // return all fields + { + skip, + ttlMs: ONE_MINUTE_MS * 2, + }, + 'GET', + unitagsApolloClient + ) +} diff --git a/packages/uniswap/src/features/unitags/types.ts b/packages/uniswap/src/features/unitags/types.ts index 8efba1fef4d..e611bcfc000 100644 --- a/packages/uniswap/src/features/unitags/types.ts +++ b/packages/uniswap/src/features/unitags/types.ts @@ -27,9 +27,16 @@ export type UnitagUsernameResponse = { export type UnitagAddressResponse = { username?: string + address?: Address metadata?: ProfileMetadata } +export type UnitagAddressesResponse = { + usernames: { + [address: Address]: UnitagAddressResponse + } +} + export type UnitagResponse = { success: boolean errorCode?: UnitagErrorCodes @@ -89,6 +96,18 @@ export type UnitagChangeUsernameRequestBody = { deviceId: string } +export type UnitagWaitlistPositionResponse = + | { + isAccepted: false + waitlistPosition?: never + address?: never + } + | { + isAccepted: true + waitlistPosition: number + address: Address + } + // Copied enum from unitags backend code -- needs to be up-to-date export enum UnitagErrorCodes { UnitagNotAvailable = 'unitags-1', diff --git a/packages/uniswap/src/i18n/locales/source/en-US.json b/packages/uniswap/src/i18n/locales/source/en-US.json index 7e90b2d6d00..e1904c77772 100644 --- a/packages/uniswap/src/i18n/locales/source/en-US.json +++ b/packages/uniswap/src/i18n/locales/source/en-US.json @@ -188,6 +188,7 @@ "retry": "Retry", "review": "Review", "save": "Save", + "scrollDown": "Scroll down", "sell": "Sell", "send": "Send", "setup": "Set up", @@ -277,6 +278,9 @@ "base": { "title": "Confirm transaction" }, + "changeChain": { + "title": "Switch network to {{network}}" + }, "connect": { "helptext": "Allow this site to view your wallet address, balance, and request approvals for transactions.", "title": "Connect to site" @@ -299,9 +303,13 @@ } }, "signature": { - "education": { - "description": "A signature is required to prove that you own the wallet without exposing your private keys", - "title": "What’s a signature request?" + "containsUnrenderableCharacters": "This message contains unrenderable characters. Make sure you trust this site.", + "error": { + "712-spec-compliance": "SignTypedDataRequestContent received data for signing which does not comply with the EIP-712 spec." + }, + "toggleDataView": { + "raw": "View raw data", + "readable": "View original data" } }, "warning": { @@ -504,6 +512,11 @@ "title": "Activity" }, "banner": { + "extension": { + "confirm": "Join Beta", + "message": "Be the first to try out the Uniswap Extension on your web browser", + "title": "Uniswap Extension is here" + }, "offline": "You are in offline mode" }, "extension": { @@ -525,6 +538,14 @@ "send": "Send", "swap": "Swap" }, + "modal": { + "getExtension": { + "step1": "1. Visit uniswap.org/ext on your computer", + "step2": "2. Add the Uniswap Extension on your Chrome browser", + "step3": "3. Enter your username to get access", + "title": "Join the Uniswap Extension Beta" + } + }, "nfts": { "title": "NFTs" }, @@ -750,6 +771,10 @@ "button": "Import from your phone", "title": "Have the Uniswap mobile app?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Download the mobile app and claim a username", + "title": "Get on the Beta waitlist" + }, "password": { "subtitle": "You’ll need this to unlock your wallet and access your recovery phrase", "title": { @@ -799,6 +824,19 @@ }, "title": "Welcome to \nUniswap Wallet" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Check eligibility", + "letsGo": "Let’s go" + }, + "checkEligibilityInstructions": "Enter your uni.eth username below to check if you’re eligible for the Beta.", + "eligible": { + "tagline": "Welcome to the Beta — you’re one of the first to try out the Uniswap wallet.", + "title": "You’re off the waitlist!" + }, + "ineligibleExplanation": "You’re still on the waitlist. We’ll notify you in the Uniswap mobile app when you become eligible!", + "unitagPlaceholder": "username" + }, "landing": { "button": { "add": "Add an existing wallet", @@ -853,8 +891,8 @@ "subtitle": "Check your Uniswap mobile app for the 6-character code", "title": "Enter one-time code" }, - "subtitle": "Scan the QR code with the Uniswap app to import your wallet", - "title": "Sync from your phone" + "subtitle": "Scan the QR code with the Uniswap mobile app to begin importing your wallet(s)", + "title": "Import wallet from the app" }, "security": { "alert": { @@ -957,7 +995,7 @@ "title": "Scan a QR code", "wallet": { "networks": { - "description": "Uniswap Wallet supports tokens on Ethereum, Polygon, Arbitrum, Optimism, Base, Blast, and BNB Chain. Right now, we only support NFTs on Ethereum.", + "description": "Uniswap Wallet supports tokens on Ethereum, Polygon, Arbitrum, Optimism, Base, and BNB Chain. Right now, we only support NFTs on Ethereum.", "title": "Supported Networks" }, "title": "You can send tokens on all of our supported networks to this address." @@ -994,6 +1032,16 @@ "message": "Scan the QR code on the Uniswap Extension again to continue syncing your wallet.", "title": "Your connection timed out" } + }, + "modal": { + "notOnWaitlist": { + "message": "In order to become eligible for the Uniswap Extension Beta, join the waitlist by claiming a uni.eth username", + "title": "You’re not on the waitlist" + }, + "onWaitlist": { + "message": "We’ll notify you in the app when you become eligible to join the Uniswap Extension Beta.", + "title": "You’re still on the waitlist" + } } }, "send": { @@ -1034,9 +1082,20 @@ } }, "recipientSelect": { + "search": { + "empty": { + "message": "When you send tokens to a wallet address, they’ll show up here", + "title": "No wallets saved" + } + }, "title": "To" }, "review": { + "input": { + "tokenAmount": { + "title": "{{currencyAmount}} of {{currencySymbol}}" + } + }, "modal": { "title": "You’re sending" }, @@ -1194,6 +1253,7 @@ "setting": { "appearance": { "option": { + "auto": "Auto", "dark": { "subtitle": "Always use dark mode", "title": "Dark mode" @@ -1263,6 +1323,9 @@ "title": "{{cloudProviderName}} backup" } }, + "beta": { + "tooltip": "Coming soon!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1311,6 +1374,9 @@ "currency": { "title": "Local currency" }, + "giveFeedback": { + "title": "Give feedback" + }, "helpCenter": { "title": "Help center" }, @@ -1319,6 +1385,7 @@ "navigate": "Go to settings" }, "description": "Uniswap defaults to your device‘s language settings. To change your preferred language, go to “Uniswap” in your device settings and tap on “Language”", + "exampleTitle": "English", "title": "Language" }, "password": { @@ -1339,6 +1406,9 @@ "smallBalances": { "title": "Hide small balances" }, + "theme": { + "title": "Theme" + }, "unknownTokens": { "title": "Hide unknown tokens" }, @@ -1410,9 +1480,6 @@ "wrap": "Hold to wrap" }, "request": { - "details": { - "header": "You’re swapping" - }, "title": { "full": "Swap {{inputCurrencySymbol}} → {{outputCurrencySymbol}}", "short": "Swap Tokens" @@ -2003,7 +2070,8 @@ }, "pending": { "button": { - "connect": "Connect" + "connect": "Connect", + "scrollDown": "Scroll down to connect" }, "switchAccount": "Switch Account", "switchNetwork": "Switch Network", @@ -2020,6 +2088,7 @@ }, "request": { "button": { + "scrollDown": "Scroll down to sign", "sign": "Sign" }, "details": { diff --git a/packages/uniswap/src/i18n/locales/translations/es-ES.json b/packages/uniswap/src/i18n/locales/translations/es-ES.json index e8d6df2da1d..6a6f337b8a7 100644 --- a/packages/uniswap/src/i18n/locales/translations/es-ES.json +++ b/packages/uniswap/src/i18n/locales/translations/es-ES.json @@ -174,6 +174,7 @@ "dismiss": "Cerrar", "done": "Listo", "enable": "Habilitar", + "goBack": "Regresa", "hide": "Ocultar", "later": "Quizá más tarde", "learn": "Aprender más", @@ -205,6 +206,7 @@ "title": "¡Ups! Algo salió mal." } }, + "endAdornment": "y", "error": { "general": "Algo salió mal." }, @@ -266,21 +268,48 @@ "dapp": { "request": { "approve": { - "label": "Billetera" + "action": "Aprobar", + "fallbackTitle": "Aprobar tokens de gasto", + "helptext": "Permita que este sitio acceda y gaste este token desde su billetera.", + "label": "Wallet", + "title": "Aprobar gasto {{tokenSymbol}}" + }, + "base": { + "title": "Confirmar transacción" + }, + "changeChain": { + "title": "Cambiar red a {{network}}" + }, + "connect": { + "helptext": "Permitir a este sitio ver su dirección de wallet, saldo y solicitar aprobaciones para las transacciones.", + "title": "Conectarse al sitio" }, "error": { "none": "No hay aprobaciones pendientes" }, + "fallback": { + "calldata": { + "label": "datos" + }, + "function": { + "label": "función" + }, + "recipient": { + "label": "para" + }, + "sending": { + "label": "enviando" + } + }, "signature": { - "education": { - "description": "Se requiere una firma para demostrar que eres propietario de la wallet sin exponer tus llaves privadas", - "title": "¿Qué es una solicitud de firma?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent recibió datos para firmar que no cumplen con la especificación EIP-712." } }, "warning": { "notActive": { "message": "Asegúrate de que sea el correcto", - "title": "Esta no es tu billetera activa" + "title": "Esta no es tu wallet activa" } } } @@ -313,7 +342,7 @@ "recent": "Búsquedas recientes", "suggestedWallets": "Wallets sugeridas", "tokens": "Tokens", - "wallets": "Carteras" + "wallets": "Wallets" } }, "tokens": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Su billetera no está conectada a este sitio.", - "popupWithButton": "Su billetera no está conectada a este sitio. Busque el botón \"Conectar Wallet\" o \"Iniciar sesión\"." + "popup": "Su wallet no está conectada a este sitio. Busque un botón \"Conectar Wallet\" o \"Iniciar sesión\".", + "popupWithButton": "Su billetera no está conectada a este sitio.", + "titleConnected": "Conectado", + "titleNotConnected": "No conectado" + }, + "dappRequest": { + "signatureRequest": { + "header": "Solicitud de firma" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Conectándote a {{serviceProvider}}", - "quote": "Comprando {{amount}} por valor de {{currencySymbol}}" + "quote": "Comprando {{amount}} por valor de {{currencySymbol}}", + "terms": "Al continuar, reconoces que estarás sujeto a los Términos de servicio y la Política de privacidad de {{serviceProvider}}, según corresponda." }, "error": { "default": "Algo salió mal.", @@ -427,10 +464,9 @@ "usd": "Sólo disponible para comprar en USD" }, "quote": { - "amount": "Recibir {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} después de tarifas", + "advice": "Continuará al portal del proveedor para ver las tarifas asociadas con su transacción.", + "others": "otros", "type": { - "best": "Mejor en general", "other": "Otras opciones", "recent": "Recientemente usado" } @@ -470,6 +506,11 @@ "title": "Actividad" }, "banner": { + "extension": { + "confirm": "Unirse a la beta", + "message": "Sea el primero en probar la extensión Uniswap en su navegador web", + "title": "La extensión Uniswap está aquí" + }, "offline": "Estás en modo offline" }, "extension": { @@ -491,6 +532,14 @@ "send": "Enviar", "swap": "Intercambiar" }, + "modal": { + "getExtension": { + "step1": "1. Visita uniswap.org/ext en tu computadora", + "step2": "2. Agregue la extensión Uniswap en su navegador Chrome", + "step3": "3. Ingrese su nombre de usuario para obtener acceso.", + "title": "Únase a la versión beta de la extensión Uniswap" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Dirección copiada", + "calldata": "Datos de llamada copiados", "contractAddress": "Dirección del contrato copiada", "failed": "Error al copiar al portapapeles", "image": "Imagen copiada", @@ -715,6 +765,10 @@ "button": "Importa desde tu teléfono", "title": "¿Tienes la aplicación móvil Uniswap?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Descargue la aplicación móvil y reclame un nombre de usuario", + "title": "Inscríbete en la lista de espera Beta" + }, "password": { "subtitle": "Necesitará esto para desbloquear su billetera y acceder a su frase de recuperación.", "title": { @@ -764,6 +818,19 @@ }, "title": "Bienvenido a \nUniswap Wallet" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Verificar elegibilidad", + "letsGo": "Vamos" + }, + "checkEligibilityInstructions": "Ingresa tu nombre de usuario uni.eth a continuación para verificar si eres elegible para la versión Beta.", + "eligible": { + "tagline": "Bienvenido a la Beta: eres uno de los primeros en probar la billetera Uniswap.", + "title": "¡Estás fuera de la lista de espera!" + }, + "ineligibleExplanation": "Todavía estás en la lista de espera. ¡Te notificaremos en la aplicación móvil de Uniswap cuando seas elegible!", + "unitagPlaceholder": "nombre de usuario" + }, "landing": { "button": { "add": "Agregar una wallet existente", @@ -814,12 +881,12 @@ "error": "Lo sentimos, no podemos cargar el código QR en este momento. Pruebe con otro método de incorporación.", "otp": { "error": "El código que envió es incorrecto o hubo un error al enviarlo. Inténtalo de nuevo.", - "failed": "Intentos fallidos: {{count}}", + "failed": "Intentos fallidos: {{number}}", "subtitle": "Consulte su aplicación móvil Uniswap para ver el código de 6 caracteres", "title": "Ingrese el código de un solo uso" }, - "subtitle": "Escanea el código QR con la aplicación Uniswap para importar tu billetera", - "title": "Sincroniza desde tu teléfono" + "subtitle": "Escanee el código QR con la aplicación móvil Uniswap para comenzar a importar su(s) billetera(s)", + "title": "Importar wallet desde la aplicación" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Escanee el código QR en Uniswap Extension nuevamente para continuar sincronizando su billetera.", "title": "Tu conexión se agotó" } + }, + "modal": { + "notOnWaitlist": { + "message": "Para ser elegible para la extensión Beta de Uniswap, únase a la lista de espera solicitando un nombre de usuario uni.eth", + "title": "No estás en la lista de espera" + }, + "onWaitlist": { + "message": "Le notificaremos en la aplicación cuando sea elegible para unirse a Uniswap Extension Beta.", + "title": "Todavía estás en la lista de espera." + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Revisar transferencia", "send": "Enviar" }, + "gas": { + "error": { + "title": "N/D" + }, + "networkCost": { + "title": "Costo de la red" + } + }, + "input": { + "token": { + "balance": { + "title": "Saldo: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} transferencia anterior", + "previous_one": "1 transferencia anterior", "previous_other": "{{count}} transferencias anteriores", "section": { "favorite": "Wallets favoritas", "recent": "Reciente", "search": "Resultados de la búsqueda", + "viewOnly": "Wallets de sólo lectura", "yours": "Tus wallets" + }, + "warning": { + "viewOnly": { + "message": "Sólo envíe fondos a esta wallet si tiene la frase de recuperación o si conoce al propietario de la wallet.", + "title": "Tienes esto como una billetera de solo visualización." + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Cuando envías tokens a una dirección de billetera, aparecerán aquí", + "title": "No hay billeteras guardadas" + } + }, + "title": "Para" + }, "review": { + "modal": { + "title": "estas enviando" + }, "summary": { + "button": { + "title": "Confirmar envío" + }, "sending": "Enviando", "to": "Para" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Tu saldo de {{currencySymbol}} ha disminuido desde que ingresaste la cantidad que quieres enviar", - "title": "No hay suficientes {{currencySymbol}}." + "title": "No hay suficientes {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "Aceptar", + "cancel": "Cancelar", + "confirm": "Confirmar" + } + } }, "newAddress": { "message": "No has realizado transacciones con esta dirección antes. Confirma que la dirección sea correcta antes de continuar.", @@ -1103,19 +1226,23 @@ "wallet": { "action": { "hide": "Ocultar wallets", - "showAll_one": "Mostrar una billetera", + "showAll_one": "Mostrar una wallet", "showAll_other": "Ver todas las {{count}} wallets" }, "button": { "viewAll": "Ver todo", "viewLess": "Ver menos" }, + "label": { + "viewOnly": "Sólo lectura" + }, "title": "Configuración de wallet" } }, "setting": { "appearance": { "option": { + "auto": "Auto", "dark": { "subtitle": "Usa siempre el modo oscuro", "title": "Modo oscuro" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} copia de seguridad" } }, + "beta": { + "tooltip": "¡Muy pronto!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Si no activas {{biometricsMethod}}, cualquiera que obtenga acceso a tu dispositivo podrá abrir Uniswap Wallet y realizar transacciones.", - "ios": "Si no activas la biometría, cualquiera que obtenga acceso a tu dispositivo podrá abrir Uniswap Wallet y realizar transacciones." + "android": "Si no activas la biometría, cualquiera que obtenga acceso a tu dispositivo podrá abrir Uniswap Wallet y realizar transacciones.", + "ios": "Si no activas {{biometricsMethod}}, cualquiera que obtenga acceso a tu dispositivo podrá abrir Uniswap Wallet y realizar transacciones." }, "title": "¿Estás seguro?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Moneda local" }, + "giveFeedback": { + "title": "Dar opinion" + }, "helpCenter": { "title": "Centro de ayuda" }, @@ -1241,6 +1374,7 @@ "navigate": "Ir a la configuración" }, "description": "Uniswap utiliza de forma predeterminada la configuración de idioma de su dispositivo. Para cambiar su idioma preferido, vaya a \"Uniswap\" en la configuración de su dispositivo y toque \"Idioma\"", + "exampleTitle": "Inglés", "title": "Idioma" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Ocultar saldos pequeños" }, + "theme": { + "title": "Tema" + }, "unknownTokens": { "title": "Ocultar tokens desconocidos" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Máx", + "review": "Revisar intercambio", "swap": "Intercambiar", "unwrap": "Desenvolver", "view": "Ver transacción", @@ -1928,7 +2066,7 @@ }, "switchAccount": "Cambiar Cuenta", "switchNetwork": "Cambiar Red", - "title": "{{dappName}} quiere conectarse a tu billetera" + "title": "{{dappName}} quiere conectarse a su wallet" }, "permissions": { "networks": "Redes", diff --git a/packages/uniswap/src/i18n/locales/translations/fr-FR.json b/packages/uniswap/src/i18n/locales/translations/fr-FR.json index c3ac12b2b94..c3123e89784 100644 --- a/packages/uniswap/src/i18n/locales/translations/fr-FR.json +++ b/packages/uniswap/src/i18n/locales/translations/fr-FR.json @@ -174,6 +174,7 @@ "dismiss": "Ignorer", "done": "Terminé", "enable": "Activer", + "goBack": "Retourner", "hide": "Masquer", "later": "Peut-être plus tard", "learn": "Apprendre encore plus", @@ -205,6 +206,7 @@ "title": "Oups ! Quelque chose s'est mal passé." } }, + "endAdornment": "et", "error": { "general": "Quelque chose s'est mal passé." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Portefeuille" + "action": "Approuver", + "fallbackTitle": "Approuver les jetons de dépenses", + "helptext": "Autorisez ce site à accéder et à dépenser ce jeton depuis votre portefeuille.", + "label": "Portefeuille", + "title": "Approuver les dépenses {{tokenSymbol}}" + }, + "base": { + "title": "Confirmer la transaction" + }, + "changeChain": { + "title": "Basculer le réseau sur {{network}}" + }, + "connect": { + "helptext": "Autorisez ce site à afficher l'adresse de votre portefeuille, votre solde et à demander des approbations pour les transactions.", + "title": "Se connecter au site" }, "error": { "none": "Aucune approbation en attente" }, + "fallback": { + "calldata": { + "label": "données" + }, + "function": { + "label": "fonction" + }, + "recipient": { + "label": "à" + }, + "sending": { + "label": "envoi en cours" + } + }, "signature": { - "education": { - "description": "Une signature est nécessaire pour prouver que vous possédez le wallet sans exposer vos clés privées", - "title": "Qu'est-ce qu'une demande de signature ?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent a reçu des données à signer qui ne sont pas conformes à la spécification EIP-712." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Votre portefeuille n'est pas connecté à ce site.", - "popupWithButton": "Votre portefeuille n'est pas connecté à ce site. Recherchez un bouton « Connecter le portefeuille » ou « Connexion »." + "popup": "Votre portefeuille n'est pas connecté à ce site. Recherchez un bouton « Connecter le portefeuille » ou « Connexion ».", + "popupWithButton": "Votre portefeuille n'est pas connecté à ce site.", + "titleConnected": "Connecté", + "titleNotConnected": "Pas connecté" + }, + "dappRequest": { + "signatureRequest": { + "header": "Demande de signature" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Vous connecter à {{serviceProvider}}", - "quote": "En cours d'acheter {{amount}} de {{currencySymbol}}" + "quote": "En cours d'acheter {{amount}} de {{currencySymbol}}", + "terms": "En continuant, vous reconnaissez que vous serez soumis aux conditions d'utilisation et à la politique de confidentialité de {{serviceProvider}}, le cas échéant." }, "error": { "default": "Quelque chose s'est mal passé.", @@ -427,10 +464,9 @@ "usd": "Uniquement disponible à l'achat en USD" }, "quote": { - "amount": "Recevez {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} après frais", + "advice": "Vous continuerez vers le portail du fournisseur pour voir les frais associés à votre transaction.", + "others": "autres", "type": { - "best": "Meilleur dans l'ensemble", "other": "Autres options", "recent": "Utilisé récemment" } @@ -470,6 +506,11 @@ "title": "Activité" }, "banner": { + "extension": { + "confirm": "Rejoindre la bêta", + "message": "Soyez le premier à essayer l'extension Uniswap sur votre navigateur Web", + "title": "L'extension Uniswap est ici" + }, "offline": "Vous êtes en mode hors ligne" }, "extension": { @@ -491,6 +532,14 @@ "send": "Envoyer", "swap": "Échanger" }, + "modal": { + "getExtension": { + "step1": "1. Visitez uniswap.org/ext sur votre ordinateur", + "step2": "2. Ajoutez l'extension Uniswap sur votre navigateur Chrome", + "step3": "3. Entrez votre nom d'utilisateur pour y accéder", + "title": "Rejoignez la version bêta de l'extension Uniswap" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Adresse copiée", + "calldata": "Données d'appel copiées", "contractAddress": "Adresse du contrat copiée", "failed": "Échec de la copie dans le presse-papiers", "image": "Image copiée", @@ -715,6 +765,10 @@ "button": "Importer depuis votre téléphone", "title": "Vous disposez de l'application mobile Uniswap ?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Téléchargez l'application mobile et réclamez un nom d'utilisateur", + "title": "Inscrivez-vous sur la liste d'attente de la version bêta" + }, "password": { "subtitle": "Vous en aurez besoin pour déverrouiller votre portefeuille et accéder à votre phrase de récupération", "title": { @@ -764,6 +818,19 @@ }, "title": "Bienvenue sur \nPortefeuille Uniswap" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Vérifier l'éligibilité", + "letsGo": "Allons-y" + }, + "checkEligibilityInstructions": "Entrez votre nom d'utilisateur uni.eth ci-dessous pour vérifier si vous êtes éligible pour la version bêta.", + "eligible": { + "tagline": "Bienvenue dans la version bêta — vous êtes l'un des premiers à essayer le portefeuille Uniswap.", + "title": "Vous n'êtes plus sur la liste d'attente !" + }, + "ineligibleExplanation": "Vous êtes toujours sur la liste d'attente. Nous vous informerons dans l'application mobile Uniswap lorsque vous deviendrez éligible !", + "unitagPlaceholder": "nom d'utilisateur" + }, "landing": { "button": { "add": "Ajouter un wallet existant", @@ -814,12 +881,12 @@ "error": "Désolé, nous ne pouvons pas charger le code QR pour le moment. Veuillez essayer une autre méthode d'intégration.", "otp": { "error": "Le code que vous avez soumis est incorrect ou une erreur s'est produite lors de la soumission. Veuillez réessayer.", - "failed": "Tentatives échouées : {{count}}", + "failed": "Tentatives échouées : {{number}}", "subtitle": "Vérifiez votre application mobile Uniswap pour le code à 6 caractères", "title": "Entrez le code à usage unique" }, - "subtitle": "Scannez le code QR avec l'application Uniswap pour importer votre portefeuille", - "title": "Synchronisez depuis votre téléphone" + "subtitle": "Scannez le code QR avec l'application mobile Uniswap pour commencer à importer votre (vos) portefeuille(s)", + "title": "Importer un portefeuille depuis l'application" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Scannez à nouveau le code QR sur l'extension Uniswap pour continuer la synchronisation de votre portefeuille.", "title": "Votre connexion a expiré" } + }, + "modal": { + "notOnWaitlist": { + "message": "Afin de devenir éligible à la version bêta de l'extension Uniswap, rejoignez la liste d'attente en réclamant un nom d'utilisateur uni.eth.", + "title": "Vous n'êtes pas sur la liste d'attente" + }, + "onWaitlist": { + "message": "Nous vous informerons dans l'application lorsque vous deviendrez éligible pour rejoindre la version bêta de l'extension Uniswap.", + "title": "Vous êtes toujours sur la liste d'attente" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Vérifier le transfert", "send": "Envoyer" }, + "gas": { + "error": { + "title": "N / A" + }, + "networkCost": { + "title": "Coût du réseau" + } + }, + "input": { + "token": { + "balance": { + "title": "Solde : {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} transfert précédent", + "previous_one": "1 transfert précédent", "previous_other": "{{count}} transferts précédents", "section": { "favorite": "Wallets favoris", "recent": "Récent", "search": "Résultats de recherche", + "viewOnly": "Portefeuilles en lecture seule", "yours": "Vos wallets" + }, + "warning": { + "viewOnly": { + "message": "N'envoyez des fonds vers ce portefeuille que si vous disposez de la phrase de récupération ou si vous connaissez le propriétaire du portefeuille.", + "title": "Vous l'avez comme portefeuille en lecture seule" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Lorsque vous envoyez des jetons à une adresse de portefeuille, ils apparaîtront ici", + "title": "Aucun portefeuille enregistré" + } + }, + "title": "À" + }, "review": { + "modal": { + "title": "Vous envoyez" + }, "summary": { + "button": { + "title": "Confirmer l'envoi" + }, "sending": "Envoi en cours", "to": "À" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Votre solde de {{currencySymbol}} a diminué depuis que vous avez saisi le montant que vous souhaitez envoyer", - "title": "Pas assez {{currencySymbol}}." + "title": "Pas assez {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "D'ACCORD", + "cancel": "Annuler", + "confirm": "Confirmer" + } + } }, "newAddress": { "message": "Vous n'avez jamais effectué de transaction avec cette adresse. Veuillez confirmer que l'adresse est correcte avant de continuer.", @@ -1110,12 +1233,16 @@ "viewAll": "Tout voir", "viewLess": "Voir moins" }, + "label": { + "viewOnly": "Lecture seule" + }, "title": "Paramètres du wallet" } }, "setting": { "appearance": { "option": { + "auto": "Auto", "dark": { "subtitle": "Utilisez toujours le thème sombre", "title": "Thème sombre" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} sauvegarde" } }, + "beta": { + "tooltip": "À venir!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Si vous n'activez pas {{biometricsMethod}}, n'importe qui ayant accès à votre appareil peut ouvrir Uniswap Wallet et effectuer des transactions.", - "ios": "Si vous n'activez pas la biométrie, n'importe qui ayant accès à votre appareil peut ouvrir Uniswap Wallet et effectuer des transactions." + "android": "Si vous n'activez pas la biométrie, n'importe qui ayant accès à votre appareil peut ouvrir Uniswap Wallet et effectuer des transactions.", + "ios": "Si vous n'activez pas {{biometricsMethod}}, n'importe qui ayant accès à votre appareil peut ouvrir Uniswap Wallet et effectuer des transactions." }, "title": "Êtes-vous sûr ?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Devise locale" }, + "giveFeedback": { + "title": "Donnez votre avis" + }, "helpCenter": { "title": "Centre d'aide" }, @@ -1241,6 +1374,7 @@ "navigate": "Aller aux paramètres" }, "description": "Uniswap utilise par défaut les paramètres de langue de votre appareil. Pour changer votre langue préférée, allez sur « Uniswap » dans les paramètres de votre appareil et appuyez sur « Langue »", + "exampleTitle": "Anglais", "title": "Langue" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Masquer les petits soldes" }, + "theme": { + "title": "Thème" + }, "unknownTokens": { "title": "Masquer les tokens inconnus" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Max.", + "review": "Vérification", "swap": "Échanger", "unwrap": "Déballez", "view": "Voyez la transaction", diff --git a/packages/uniswap/src/i18n/locales/translations/hi-IN.json b/packages/uniswap/src/i18n/locales/translations/hi-IN.json index 76a38c018fa..123cbe983f3 100644 --- a/packages/uniswap/src/i18n/locales/translations/hi-IN.json +++ b/packages/uniswap/src/i18n/locales/translations/hi-IN.json @@ -174,6 +174,7 @@ "dismiss": "नकार देना", "done": "हो गया", "enable": "सक्षम", + "goBack": "वापस जाओ", "hide": "छिपाना", "later": "शायद बाद में", "learn": "और अधिक जानें", @@ -205,6 +206,7 @@ "title": "उफ़! कुछ गलत हो गया।" } }, + "endAdornment": "और", "error": { "general": "कुछ गलत हो गया।" }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "बटुआ" + "action": "मंज़ूरी देना", + "fallbackTitle": "खर्च टोकन को मंजूरी दें", + "helptext": "इस साइट को अपने वॉलेट से इस टोकन तक पहुंचने और खर्च करने की अनुमति दें।", + "label": "बटुआ", + "title": "खर्च को मंजूरी दें {{tokenSymbol}}" + }, + "base": { + "title": "लेन-देन की पुष्टि करें" + }, + "changeChain": { + "title": "नेटवर्क को {{network}}पर स्विच करें" + }, + "connect": { + "helptext": "इस साइट को अपना वॉलेट पता, शेष राशि देखने और लेनदेन के लिए अनुमोदन का अनुरोध करने की अनुमति दें।", + "title": "साइट से कनेक्ट करें" }, "error": { "none": "कोई अनुमोदन लंबित नहीं है" }, + "fallback": { + "calldata": { + "label": "डेटा" + }, + "function": { + "label": "समारोह" + }, + "recipient": { + "label": "को" + }, + "sending": { + "label": "भेजना" + } + }, "signature": { - "education": { - "description": "यह साबित करने के लिए कि आप अपनी निजी चाबियाँ उजागर किए बिना वॉलेट के मालिक हैं, एक हस्ताक्षर की आवश्यकता है", - "title": "हस्ताक्षर अनुरोध क्या है?" + "error": { + "712-spec-compliance": "signTypedDataRequestContent को हस्ताक्षर करने के लिए डेटा प्राप्त हुआ जो EIP-712 विनिर्देश का अनुपालन नहीं करता है।" } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "आपका बटुआ इस साइट से कनेक्ट नहीं है.", - "popupWithButton": "आपका बटुआ इस साइट से कनेक्ट नहीं है. \"कनेक्ट वॉलेट\" या \"लॉग इन\" बटन देखें।" + "popup": "आपका बटुआ इस साइट से कनेक्ट नहीं है. \"कनेक्ट वॉलेट\" या \"लॉग इन\" बटन देखें।", + "popupWithButton": "आपका बटुआ इस साइट से कनेक्ट नहीं है.", + "titleConnected": "जुड़े हुए", + "titleNotConnected": "जुड़े नहीं हैं" + }, + "dappRequest": { + "signatureRequest": { + "header": "हस्ताक्षर अनुरोध" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "आपको {{serviceProvider}}से जोड़ रहा हूँ", - "quote": "{{currencySymbol}}के मूल्य का {{amount}} खरीदना" + "quote": "{{currencySymbol}}के मूल्य का {{amount}} खरीदना", + "terms": "जारी रखकर, आप स्वीकार करते हैं कि आप {{serviceProvider}}के साथ सेवा की शर्तों और गोपनीयता नीति के अधीन होंगे, जैसा लागू हो।" }, "error": { "default": "कुछ गलत हो गया।", @@ -427,10 +464,9 @@ "usd": "केवल USD में खरीदने के लिए उपलब्ध है" }, "quote": { - "amount": "{{tokenAmount}}प्राप्त करें", - "amountAfterFees": "फीस के बाद {{tokenAmount}}", + "advice": "आप अपने लेनदेन से जुड़ी फीस देखने के लिए प्रदाता के पोर्टल पर बने रहेंगे।", + "others": "अन्य", "type": { - "best": "कुल मिलाकर सर्वश्रेष्ठ", "other": "अन्य विकल्प", "recent": "हाल ही में उपयोग किया गया" } @@ -470,6 +506,11 @@ "title": "गतिविधि" }, "banner": { + "extension": { + "confirm": "बीटा से जुड़ें", + "message": "अपने वेब ब्राउज़र पर Uniswap एक्सटेंशन को आज़माने वाले पहले व्यक्ति बनें", + "title": "यूनिस्वैप एक्सटेंशन यहाँ है" + }, "offline": "आप ऑफ़लाइन मोड में हैं" }, "extension": { @@ -491,6 +532,14 @@ "send": "भेजना", "swap": "बदलना" }, + "modal": { + "getExtension": { + "step1": "1. अपने कंप्यूटर पर uniswap.org/ext पर जाएं", + "step2": "2. अपने Chrome ब्राउज़र पर Uniswap एक्सटेंशन जोड़ें", + "step3": "3. पहुंच पाने के लिए अपना उपयोगकर्ता नाम दर्ज करें", + "title": "Uniswap एक्सटेंशन बीटा में शामिल हों" + } + }, "nfts": { "title": "एनएफटी" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "पता कॉपी किया गया", + "calldata": "कॉलडेटा कॉपी किया गया", "contractAddress": "अनुबंध का पता कॉपी किया गया", "failed": "क्लिपबोर्ड पर कॉपी करने में विफल", "image": "छवि कॉपी की गई", @@ -715,6 +765,10 @@ "button": "अपने फ़ोन से आयात करें", "title": "क्या आपके पास Uniswap मोबाइल ऐप है?" }, + "getOnTheBetaWaitlist": { + "subtitle": "मोबाइल ऐप डाउनलोड करें और उपयोगकर्ता नाम का दावा करें", + "title": "बीटा प्रतीक्षा सूची पर जाएँ" + }, "password": { "subtitle": "आपको अपने वॉलेट को अनलॉक करने और अपने पुनर्प्राप्ति वाक्यांश तक पहुंचने के लिए इसकी आवश्यकता होगी", "title": { @@ -764,6 +818,19 @@ }, "title": "\nUniswap वॉलेट में आपका स्वागत है" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "पात्रता की जांच करें", + "letsGo": "चल दर" + }, + "checkEligibilityInstructions": "यह जांचने के लिए कि क्या आप बीटा के लिए पात्र हैं, अपना uni.eth उपयोगकर्ता नाम नीचे दर्ज करें।", + "eligible": { + "tagline": "बीटा में आपका स्वागत है - आप Uniswap वॉलेट को आज़माने वाले पहले लोगों में से एक हैं।", + "title": "आप प्रतीक्षा सूची से बाहर हैं!" + }, + "ineligibleExplanation": "आप अभी भी प्रतीक्षा सूची में हैं. जब आप पात्र हो जाएंगे तो हम आपको Uniswap मोबाइल ऐप में सूचित करेंगे!", + "unitagPlaceholder": "उपयोगकर्ता नाम" + }, "landing": { "button": { "add": "एक मौजूदा वॉलेट जोड़ें", @@ -814,12 +881,12 @@ "error": "क्षमा करें, हम अभी QR कोड लोड करने में असमर्थ हैं। कृपया कोई अन्य ऑनबोर्डिंग विधि आज़माएँ.", "otp": { "error": "आपके द्वारा सबमिट किया गया कोड गलत है, या सबमिट करने में कोई त्रुटि हुई है। कृपया पुन: प्रयास करें।", - "failed": "असफल प्रयास: {{count}}", + "failed": "असफल प्रयास: {{number}}", "subtitle": "6-अक्षर वाले कोड के लिए अपना Uniswap मोबाइल ऐप जांचें", "title": "एक बार का कोड दर्ज करें" }, - "subtitle": "अपना वॉलेट आयात करने के लिए Uniswap ऐप से QR कोड को स्कैन करें", - "title": "अपने फ़ोन से सिंक करें" + "subtitle": "अपना वॉलेट आयात करना शुरू करने के लिए Uniswap मोबाइल ऐप से QR कोड को स्कैन करें", + "title": "ऐप से वॉलेट आयात करें" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "अपने वॉलेट को सिंक करना जारी रखने के लिए Uniswap एक्सटेंशन पर QR कोड को फिर से स्कैन करें।", "title": "आपके कनेक्शन का समय समाप्त हो गया" } + }, + "modal": { + "notOnWaitlist": { + "message": "Uniswap एक्सटेंशन बीटा के लिए पात्र बनने के लिए, uni.eth उपयोगकर्ता नाम का दावा करके प्रतीक्षा सूची में शामिल हों", + "title": "आप प्रतीक्षा सूची में नहीं हैं" + }, + "onWaitlist": { + "message": "जब आप Uniswap एक्सटेंशन बीटा में शामिल होने के योग्य हो जाएंगे तो हम आपको ऐप में सूचित करेंगे।", + "title": "आप अभी भी प्रतीक्षा सूची में हैं" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "स्थानांतरण की समीक्षा करें", "send": "भेजना" }, + "gas": { + "error": { + "title": "एन/ए" + }, + "networkCost": { + "title": "नेटवर्क लागत" + } + }, + "input": { + "token": { + "balance": { + "title": "शेष राशि: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} पिछला स्थानांतरण", + "previous_one": "1 पिछला स्थानांतरण", "previous_other": "{{count}} पिछले स्थानान्तरण", "section": { "favorite": "पसंदीदा बटुए", "recent": "हाल ही का", "search": "खोज के परिणाम", + "viewOnly": "केवल देखने के लिए बटुए", "yours": "आपके बटुए" + }, + "warning": { + "viewOnly": { + "message": "इस वॉलेट में केवल तभी धनराशि भेजें यदि आपके पास पुनर्प्राप्ति वाक्यांश है या आप वॉलेट के मालिक को जानते हैं।", + "title": "आपके पास यह केवल-दृश्य बटुए के रूप में है" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "जब आप किसी वॉलेट पते पर टोकन भेजते हैं, तो वे यहां दिखाई देंगे", + "title": "कोई बटुआ सहेजा नहीं गया" + } + }, + "title": "को" + }, "review": { + "modal": { + "title": "आप भेज रहे हैं" + }, "summary": { + "button": { + "title": "भेजने की पुष्टि करें" + }, "sending": "भेजना", "to": "को" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "जब से आपने वह राशि दर्ज की है जो आप भेजना चाहते हैं, आपकी {{currencySymbol}} शेष राशि कम हो गई है", - "title": "पर्याप्त नहीं {{currencySymbol}}." + "title": "पर्याप्त नहीं {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "ठीक है", + "cancel": "रद्द करना", + "confirm": "पुष्टि करना" + } + } }, "newAddress": { "message": "आपने पहले इस पते से लेन-देन नहीं किया है. कृपया आगे बढ़ने से पहले पुष्टि करें कि पता सही है।", @@ -1110,12 +1233,16 @@ "viewAll": "सभी को देखें", "viewLess": "कम देखें" }, + "label": { + "viewOnly": "केवल देखें" + }, "title": "वॉलेट सेटिंग्स" } }, "setting": { "appearance": { "option": { + "auto": "ऑटो", "dark": { "subtitle": "हमेशा डार्क मोड का इस्तेमाल करें", "title": "डार्क मोड" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} बैकअप" } }, + "beta": { + "tooltip": "जल्द आ रहा है!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "यदि आप {{biometricsMethod}}चालू नहीं करते हैं, तो आपके डिवाइस तक पहुंच प्राप्त करने वाला कोई भी व्यक्ति Uniswap वॉलेट खोल सकता है और लेनदेन कर सकता है।", - "ios": "यदि आप बायोमेट्रिक्स चालू नहीं करते हैं, तो आपके डिवाइस तक पहुंच प्राप्त करने वाला कोई भी व्यक्ति Uniswap वॉलेट खोल सकता है और लेनदेन कर सकता है।" + "android": "यदि आप बायोमेट्रिक्स चालू नहीं करते हैं, तो आपके डिवाइस तक पहुंच प्राप्त करने वाला कोई भी व्यक्ति Uniswap वॉलेट खोल सकता है और लेनदेन कर सकता है।", + "ios": "यदि आप {{biometricsMethod}}चालू नहीं करते हैं, तो आपके डिवाइस तक पहुंच प्राप्त करने वाला कोई भी व्यक्ति Uniswap वॉलेट खोल सकता है और लेनदेन कर सकता है।" }, "title": "क्या आपको यकीन है?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "स्थानीय मुद्रा" }, + "giveFeedback": { + "title": "प्रतिक्रिया दें" + }, "helpCenter": { "title": "सहायता केंद्र" }, @@ -1241,6 +1374,7 @@ "navigate": "सेटिंग्स में जाओ" }, "description": "Uniswap आपके डिवाइस की भाषा सेटिंग को डिफ़ॉल्ट बनाता है। अपनी पसंदीदा भाषा बदलने के लिए, अपनी डिवाइस सेटिंग में \"यूनीस्वैप\" पर जाएं और \"भाषा\" पर टैप करें।", + "exampleTitle": "अंग्रेज़ी", "title": "भाषा" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "छोटे-छोटे शेष छिपाएँ" }, + "theme": { + "title": "विषय" + }, "unknownTokens": { "title": "अज्ञात टोकन छिपाएँ" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "अधिकतम", + "review": "स्वैप की समीक्षा करें", "swap": "बदलना", "unwrap": "खोलना", "view": "लेन-देन देखें", @@ -1617,7 +1755,7 @@ "currency": { "unknown": "अज्ञात टोकन" }, - "date": "{{date}}पर सबमिट किया गया", + "date": "{{date}}पर प्रस्तुत किया गया", "network": { "all": "सभी नेटवर्क" }, diff --git a/packages/uniswap/src/i18n/locales/translations/id-ID.json b/packages/uniswap/src/i18n/locales/translations/id-ID.json index 8dd6ed23aa4..73e34f2c2fa 100644 --- a/packages/uniswap/src/i18n/locales/translations/id-ID.json +++ b/packages/uniswap/src/i18n/locales/translations/id-ID.json @@ -174,6 +174,7 @@ "dismiss": "Membubarkan", "done": "Selesai", "enable": "Memungkinkan", + "goBack": "Kembali", "hide": "Bersembunyi", "later": "mungkin nanti", "learn": "Belajarlah lagi", @@ -205,6 +206,7 @@ "title": "Ups! Ada yang salah." } }, + "endAdornment": "Dan", "error": { "general": "Ada yang salah." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Dompet" + "action": "Menyetujui", + "fallbackTitle": "Setujui token pembelanjaan", + "helptext": "Izinkan situs ini mengakses dan membelanjakan token ini dari dompet Anda.", + "label": "Dompet", + "title": "Setujui pembelanjaan {{tokenSymbol}}" + }, + "base": { + "title": "Konfirmasikan transaksi" + }, + "changeChain": { + "title": "Alihkan jaringan ke {{network}}" + }, + "connect": { + "helptext": "Izinkan situs ini melihat alamat dompet Anda, saldo, dan meminta persetujuan transaksi.", + "title": "Hubungkan ke situs" }, "error": { "none": "Tidak ada persetujuan yang tertunda" }, + "fallback": { + "calldata": { + "label": "data" + }, + "function": { + "label": "fungsi" + }, + "recipient": { + "label": "ke" + }, + "sending": { + "label": "mengirim" + } + }, "signature": { - "education": { - "description": "Tanda tangan diperlukan untuk membuktikan bahwa Anda pemilik dompet tersebut tanpa memperlihatkan kunci pribadi Anda", - "title": "Apa itu permintaan tanda tangan?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent menerima data untuk penandatanganan yang tidak sesuai dengan spesifikasi EIP-712." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Dompet Anda tidak terhubung ke situs ini.", - "popupWithButton": "Dompet Anda tidak terhubung ke situs ini. Cari tombol “Hubungkan Dompet” atau “Masuk”." + "popup": "Dompet Anda tidak terhubung ke situs ini. Cari tombol “Hubungkan Dompet” atau “Masuk”.", + "popupWithButton": "Dompet Anda tidak terhubung ke situs ini.", + "titleConnected": "Terhubung", + "titleNotConnected": "Tidak terhubung" + }, + "dappRequest": { + "signatureRequest": { + "header": "Permintaan tanda tangan" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Menghubungkan Anda ke {{serviceProvider}}", - "quote": "Membeli {{amount}} bernilai {{currencySymbol}}" + "quote": "Membeli {{amount}} bernilai {{currencySymbol}}", + "terms": "Dengan melanjutkan, Anda mengakui bahwa Anda akan tunduk pada Persyaratan Layanan dan Kebijakan Privasi dengan {{serviceProvider}}, sebagaimana berlaku." }, "error": { "default": "Ada yang salah.", @@ -427,10 +464,9 @@ "usd": "Hanya tersedia untuk dibeli dalam USD" }, "quote": { - "amount": "Terima {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} setelah biaya", + "advice": "Anda akan melanjutkan ke portal penyedia untuk melihat biaya yang terkait dengan transaksi Anda.", + "others": "yang lain", "type": { - "best": "Secara keseluruhan terbaik", "other": "Pilihan lain", "recent": "Baru - baru ini digunakan" } @@ -470,6 +506,11 @@ "title": "Aktivitas" }, "banner": { + "extension": { + "confirm": "Bergabunglah dengan Beta", + "message": "Jadilah orang pertama yang mencoba Ekstensi Uniswap di browser web Anda", + "title": "Ekstensi Uniswap ada di sini" + }, "offline": "Anda berada dalam mode offline" }, "extension": { @@ -491,6 +532,14 @@ "send": "Mengirim", "swap": "Menukar" }, + "modal": { + "getExtension": { + "step1": "1. Kunjungi uniswap.org/ext di komputer Anda", + "step2": "2. Tambahkan Ekstensi Uniswap di browser Chrome Anda", + "step3": "3. Masukkan nama pengguna Anda untuk mendapatkan akses", + "title": "Bergabunglah dengan Ekstensi Uniswap Beta" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Alamat disalin", + "calldata": "Data panggilan disalin", "contractAddress": "Alamat kontrak disalin", "failed": "Gagal menyalin ke papan klip", "image": "Gambar disalin", @@ -715,6 +765,10 @@ "button": "Impor dari ponsel Anda", "title": "Punya aplikasi seluler Uniswap?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Unduh aplikasi seluler dan klaim nama pengguna", + "title": "Masuk daftar tunggu Beta" + }, "password": { "subtitle": "Anda memerlukan ini untuk membuka kunci dompet Anda dan mengakses frase pemulihan Anda", "title": { @@ -764,6 +818,19 @@ }, "title": "Selamat datang di \nDompet Uniswap" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Periksa kelayakan", + "letsGo": "Ayo pergi" + }, + "checkEligibilityInstructions": "Masukkan nama pengguna uni.eth Anda di bawah untuk memeriksa apakah Anda memenuhi syarat untuk Beta.", + "eligible": { + "tagline": "Selamat datang di versi Beta — Anda adalah orang pertama yang mencoba dompet Uniswap.", + "title": "Anda keluar dari daftar tunggu!" + }, + "ineligibleExplanation": "Anda masih dalam daftar tunggu. Kami akan memberi tahu Anda di aplikasi seluler Uniswap saat Anda memenuhi syarat!", + "unitagPlaceholder": "nama belakang" + }, "landing": { "button": { "add": "Tambahkan dompet yang ada", @@ -814,12 +881,12 @@ "error": "Maaf, kami tidak dapat memuat kode QR saat ini. Silakan coba metode orientasi lainnya.", "otp": { "error": "Kode yang Anda kirimkan salah, atau terjadi kesalahan saat mengirimkan. Silakan coba lagi.", - "failed": "Upaya yang gagal: {{count}}", + "failed": "Upaya yang gagal: {{number}}", "subtitle": "Periksa aplikasi seluler Uniswap Anda untuk kode 6 karakter", "title": "Masukkan kode satu kali" }, - "subtitle": "Pindai kode QR dengan aplikasi Uniswap untuk mengimpor dompet Anda", - "title": "Sinkronkan dari ponsel Anda" + "subtitle": "Pindai kode QR dengan aplikasi seluler Uniswap untuk mulai mengimpor dompet Anda", + "title": "Impor dompet dari aplikasi" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Pindai lagi kode QR di Ekstensi Uniswap untuk melanjutkan sinkronisasi dompet Anda.", "title": "Waktu koneksi Anda habis" } + }, + "modal": { + "notOnWaitlist": { + "message": "Agar memenuhi syarat untuk Ekstensi Uniswap Beta, bergabunglah dalam daftar tunggu dengan mengklaim nama pengguna uni.eth", + "title": "Anda tidak ada dalam daftar tunggu" + }, + "onWaitlist": { + "message": "Kami akan memberi tahu Anda di aplikasi saat Anda memenuhi syarat untuk bergabung dengan Uniswap Extension Beta.", + "title": "Anda masih dalam daftar tunggu" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Tinjau transfer", "send": "Mengirim" }, + "gas": { + "error": { + "title": "T/A" + }, + "networkCost": { + "title": "Biaya Jaringan" + } + }, + "input": { + "token": { + "balance": { + "title": "Saldo: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} transfer sebelumnya", + "previous_one": "1 transfer sebelumnya", "previous_other": "{{count}} transfer sebelumnya", "section": { "favorite": "Dompet favorit", "recent": "Terkini", "search": "Hasil Pencarian", + "viewOnly": "Dompet hanya lihat", "yours": "dompet Anda" + }, + "warning": { + "viewOnly": { + "message": "Kirimkan dana ke dompet ini hanya jika Anda memiliki frasa pemulihan atau mengetahui pemilik dompet.", + "title": "Anda memiliki ini sebagai dompet hanya lihat" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Saat Anda mengirim token ke alamat dompet, token tersebut akan muncul di sini", + "title": "Tidak ada dompet yang disimpan" + } + }, + "title": "Ke" + }, "review": { + "modal": { + "title": "Anda mengirim" + }, "summary": { + "button": { + "title": "Konfirmasikan pengiriman" + }, "sending": "Mengirim", "to": "Ke" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Saldo {{currencySymbol}} Anda berkurang sejak Anda memasukkan jumlah yang ingin Anda kirim", - "title": "Tidak cukup {{currencySymbol}}." + "title": "Tidak cukup {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "OKE", + "cancel": "Membatalkan", + "confirm": "Mengonfirmasi" + } + } }, "newAddress": { "message": "Anda belum pernah bertransaksi dengan alamat ini sebelumnya. Harap konfirmasikan bahwa alamatnya benar sebelum melanjutkan.", @@ -1110,12 +1233,16 @@ "viewAll": "Lihat semua", "viewLess": "Lihat lebih sedikit" }, + "label": { + "viewOnly": "Hanya lihat" + }, "title": "Pengaturan dompet" } }, "setting": { "appearance": { "option": { + "auto": "Mobil", "dark": { "subtitle": "Selalu gunakan mode gelap", "title": "Mode gelap" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} cadangan" } }, + "beta": { + "tooltip": "Segera hadir!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Jika Anda tidak mengaktifkan {{biometricsMethod}}, siapa pun yang mendapatkan akses ke perangkat Anda dapat membuka Dompet Uniswap dan melakukan transaksi.", - "ios": "Jika Anda tidak mengaktifkan biometrik, siapa pun yang mendapatkan akses ke perangkat Anda dapat membuka Dompet Uniswap dan melakukan transaksi." + "android": "Jika Anda tidak mengaktifkan biometrik, siapa pun yang mendapatkan akses ke perangkat Anda dapat membuka Dompet Uniswap dan melakukan transaksi.", + "ios": "Jika Anda tidak mengaktifkan {{biometricsMethod}}, siapa pun yang mendapatkan akses ke perangkat Anda dapat membuka Dompet Uniswap dan melakukan transaksi." }, "title": "Apa kamu yakin?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Mata uang lokal" }, + "giveFeedback": { + "title": "Berikan umpan balik" + }, "helpCenter": { "title": "Pusat Bantuan" }, @@ -1241,6 +1374,7 @@ "navigate": "Pergi ke pengaturan" }, "description": "Uniswap default pada pengaturan bahasa perangkat Anda. Untuk mengubah bahasa pilihan Anda, buka “Uniswap” di pengaturan perangkat Anda dan ketuk “Bahasa”", + "exampleTitle": "Bahasa Inggris", "title": "Bahasa" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Sembunyikan saldo kecil" }, + "theme": { + "title": "Tema" + }, "unknownTokens": { "title": "Sembunyikan token yang tidak diketahui" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Maks", + "review": "Tinjau pertukaran", "swap": "Menukar", "unwrap": "Membuka", "view": "Lihat transaksi", diff --git a/packages/uniswap/src/i18n/locales/translations/ja-JP.json b/packages/uniswap/src/i18n/locales/translations/ja-JP.json index 4ca8f3af498..4b5f64f6860 100644 --- a/packages/uniswap/src/i18n/locales/translations/ja-JP.json +++ b/packages/uniswap/src/i18n/locales/translations/ja-JP.json @@ -174,6 +174,7 @@ "dismiss": "閉じる", "done": "完了", "enable": "有効にする", + "goBack": "戻る", "hide": "非表示", "later": "後で", "learn": "もっと詳しく知る", @@ -205,6 +206,7 @@ "title": "エラーが発生しました。" } }, + "endAdornment": "と", "error": { "general": "エラーが発生しました" }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "ウォレット" + "action": "承認する", + "fallbackTitle": "トークンの使用を承認する", + "helptext": "このサイトがウォレットからこのトークンにアクセスして使用できるようにします。", + "label": "ウォレット", + "title": "支出を承認 {{tokenSymbol}}" + }, + "base": { + "title": "取引の確認" + }, + "changeChain": { + "title": "ネットワークを {{network}}に切り替えます" + }, + "connect": { + "helptext": "このサイトでウォレット アドレス、残高を表示し、取引の承認をリクエストできるようにします。", + "title": "サイトに接続する" }, "error": { "none": "保留中の承認はありません" }, + "fallback": { + "calldata": { + "label": "データ" + }, + "function": { + "label": "関数" + }, + "recipient": { + "label": "受信者" + }, + "sending": { + "label": "送金" + } + }, "signature": { - "education": { - "description": "秘密鍵を公開せずにウォレットを所有していることを証明するために署名が必要です", - "title": "署名リクエストとは?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent は、EIP-712 仕様に準拠していない署名用のデータを受信しました。" } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "あなたのウォレットはこのサイトに接続されていません。", - "popupWithButton": "あなたのウォレットはこのサイトに接続されていません。 「ウォレットに接続」または「ログイン」ボタンを探します。" + "popup": "あなたのウォレットはこのサイトに接続されていません。 「ウォレットに接続」または「ログイン」ボタンを探します。", + "popupWithButton": "あなたのウォレットはこのサイトに接続されていません。", + "titleConnected": "接続されました", + "titleNotConnected": "接続されていません" + }, + "dappRequest": { + "signatureRequest": { + "header": "署名リクエスト" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "あなたを {{serviceProvider}}に繋ぐ", - "quote": "{{amount}} 相当の {{currencySymbol}} を購入" + "quote": "{{amount}} 相当の {{currencySymbol}} を購入", + "terms": "続行すると、 {{serviceProvider}}の利用規約およびプライバシー ポリシー (該当する場合) が適用されることを承認したことになります。" }, "error": { "default": "エラーが発生しました", @@ -427,10 +464,9 @@ "usd": "米ドルのみで購入" }, "quote": { - "amount": "{{tokenAmount}}を受信", - "amountAfterFees": "手数料後{{tokenAmount}}", + "advice": "引き続きプロバイダーのポータルに移動して、トランザクションに関連する料金を確認します。", + "others": "その他", "type": { - "best": "全体的に最高", "other": "その他のオプション", "recent": "最近使用された" } @@ -470,6 +506,11 @@ "title": "履歴" }, "banner": { + "extension": { + "confirm": "ベータ版に参加する", + "message": "Web ブラウザで Uniswap 拡張機能を最初に試してみましょう", + "title": "Uniswap 拡張機能はこちら" + }, "offline": "インターネットに接続されていません" }, "extension": { @@ -491,6 +532,14 @@ "send": "送金", "swap": "スワップ" }, + "modal": { + "getExtension": { + "step1": "1. コンピューターで uniswap.org/ext にアクセスします", + "step2": "2. Chrome ブラウザに Uniswap 拡張機能を追加します。", + "step3": "3. ユーザー名を入力してアクセスします", + "title": "Uniswap 拡張機能ベータ版に参加する" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "コピーしました", + "calldata": "通話データがコピーされました", "contractAddress": "契約アドレスをコピーしました", "failed": "クリップボードにコピーできませんでした", "image": "画像をコピー", @@ -708,13 +758,17 @@ "label": "ニックネーム", "subtitle": "ウォレットにニックネームを作成しよう", "title": "このニックネームはあなただけに表示されます", - "walletAddress": "公開アドレスは {{walletAddress}}になります。" + "walletAddress": "あなたの公開アドレスは{{walletAddress}}になります" }, "extension": { "connectMobile": { "button": "携帯電話からインポートする", "title": "Uniswap アプリをお持ちですか?" }, + "getOnTheBetaWaitlist": { + "subtitle": "モバイルアプリをダウンロードしてユーザー名を取得します", + "title": "ベータ版の待機リストに登録する" + }, "password": { "subtitle": "ウォレットのロックを解除し、回復フレーズにアクセスするにはこれが必要です", "title": { @@ -764,6 +818,19 @@ }, "title": "\nUniswap ウォレットへようこそ" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "資格を確認する", + "letsGo": "さあ行こう" + }, + "checkEligibilityInstructions": "以下に uni.eth ユーザー名を入力して、ベータ版の利用資格があるかどうかを確認してください。", + "eligible": { + "tagline": "ベータ版へようこそ — あなたは Uniswap ウォレットを最初に試した一人です。", + "title": "あなたは順番待ちリストから外れました!" + }, + "ineligibleExplanation": "あなたはまだ順番待ちリストに載っています。資格が得られたら、Uniswap モバイル アプリで通知します。", + "unitagPlaceholder": "ユーザー名" + }, "landing": { "button": { "add": "既存のウォレットを追加", @@ -814,12 +881,12 @@ "error": "申し訳ございませんが、現在 QR コードを読み込むことができません。別のオンボーディング方法を試してください。", "otp": { "error": "送信したコードが間違っているか、送信中にエラーが発生しました。もう一度試してください。", - "failed": "失敗した試行: {{count}}", + "failed": "失敗した試行: {{number}}", "subtitle": "Uniswap モバイル アプリで 6 文字のコードを確認してください", "title": "ワンタイムコードを入力してください" }, - "subtitle": "Uniswap アプリで QR コードをスキャンしてウォレットをインポートします", - "title": "携帯電話から同期する" + "subtitle": "Uniswap モバイル アプリで QR コードをスキャンして、ウォレットのインポートを開始します", + "title": "アプリからウォレットをインポートする" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "ウォレットの同期を続けるには、Uniswap Extension の QR コードを再度スキャンしてください。", "title": "接続がタイムアウトしました" } + }, + "modal": { + "notOnWaitlist": { + "message": "Uniswap Extension Beta の資格を得るには、uni.eth ユーザー名を主張して待機リストに参加してください。", + "title": "あなたは順番待ちリストに載っていません" + }, + "onWaitlist": { + "message": "Uniswap Extension ベータ版に参加する資格が得られると、アプリ内で通知されます。", + "title": "あなたはまだ順番待ちリストにいます" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "確認へ進む", "send": "送金" }, + "gas": { + "error": { + "title": "該当なし" + }, + "networkCost": { + "title": "ネットワークコスト" + } + }, + "input": { + "token": { + "balance": { + "title": "残高: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} 過去の送金", + "previous_one": "1回前の転送", "previous_other": "{{count}} 過去の送金", "section": { "favorite": "お気に入りウォレット", "recent": "最近", "search": "検索結果", + "viewOnly": "表示専用ウォレット", "yours": "あなたのウォレット" + }, + "warning": { + "viewOnly": { + "message": "回復フレーズを持っている場合、またはウォレットの所有者を知っている場合にのみ、このウォレットに資金を送信してください。", + "title": "これを表示専用ウォレットとして使用します" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "ウォレットアドレスにトークンを送信すると、ここに表示されます", + "title": "ウォレットが保存されていません" + } + }, + "title": "受信者" + }, "review": { + "modal": { + "title": "あなたが送っているのは" + }, "summary": { + "button": { + "title": "送信の確認" + }, "sending": "送金", "to": "受信者" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "送金額を入力してから {{currencySymbol}} 残高が減少しました", - "title": "{{currencySymbol}} が足りません." + "title": "{{currencySymbol}} が足りません" + }, + "modal": { + "button": { + "cta": { + "blocking": "了解", + "cancel": "キャンセル", + "confirm": "確認" + } + } }, "newAddress": { "message": "このアドレスと取引したことがありません。アドレスが正しいことを確認してください。", @@ -1110,12 +1233,16 @@ "viewAll": "すべて表示", "viewLess": "表示を減らす" }, + "label": { + "viewOnly": "閲覧専用" + }, "title": "ウォレット設定" } }, "setting": { "appearance": { "option": { + "auto": "自動", "dark": { "subtitle": "常にダークモードを使用", "title": "ダークモード" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} バックアップ" } }, + "beta": { + "tooltip": "近日公開!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "{{biometricsMethod}}をオンにしないと、デバイスにアクセスできる人は誰でも取引を行うことができます。", - "ios": "生体認証をオンにしないと、デバイスにアクセスできる人は誰でも取引を行うことができます。" + "android": "生体認証をオンにしないと、デバイスにアクセスできる人は誰でも取引を行うことができます。", + "ios": "{{biometricsMethod}}をオンにしないと、デバイスにアクセスできる人は誰でも取引を行うことができます。" }, "title": "本当によろしいですか?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "現地通貨" }, + "giveFeedback": { + "title": "フィードバックを与えます" + }, "helpCenter": { "title": "ヘルプセンター" }, @@ -1241,6 +1374,7 @@ "navigate": "設定へ" }, "description": "Uniswap のデフォルトはデバイスの言語設定です。優先言語を変更するには、デバイス設定の「Uniswap」に移動し、「言語」をタップします。", + "exampleTitle": "英語", "title": "言語" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "少額の残高を非表示" }, + "theme": { + "title": "テーマ" + }, "unknownTokens": { "title": "不明なトークンを非表示" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "最大額", + "review": "レビュー交換", "swap": "スワップ", "unwrap": "アンラップ", "view": "取引を表示", @@ -1608,7 +1746,7 @@ "copy": "取引 ID をコピー", "copyMoonPay": "MoonPay取引IDをコピー", "view": "{{tokenSymbol}}を見る", - "viewEtherscan": "{{blockExplorerName}}で見る", + "viewEtherscan": "{{blockExplorerName}} で表示", "viewMoonPay": "MoonPayで表示" }, "amount": { @@ -1708,7 +1846,7 @@ }, "swap": { "canceled": "スワップ取消済", - "canceling": "スワップ取消中", + "canceling": "スワップのキャンセル", "failed": "スワップに失敗", "pending": "スワップ中", "success": "スワップ済", @@ -1945,7 +2083,7 @@ }, "details": { "label": { - "function": "関数: ", + "function": "関数: ", "recipient": "に: ", "sending": "送金: " } diff --git a/packages/uniswap/src/i18n/locales/translations/ms-MY.json b/packages/uniswap/src/i18n/locales/translations/ms-MY.json index c06099b3c52..b04535f9adb 100644 --- a/packages/uniswap/src/i18n/locales/translations/ms-MY.json +++ b/packages/uniswap/src/i18n/locales/translations/ms-MY.json @@ -78,7 +78,7 @@ "input": "Taip frasa pemulihan anda", "remove": { "final": { - "description": "Pastikan anda telah menulis frasa pemulihan anda atau menyandarkannya pada {{cloudProviderName}}. Anda tidak akan dapat mengakses dana anda sebaliknya.", + "description": "Pastikan anda telah menulis frasa pemulihan anda atau menyandarkannya pada {{cloudProviderName}}. Anda tidak akan dapat mengakses dana anda jika tidak.", "title": "Anda sedang mengalih keluar frasa pemulihan" }, "import": { @@ -174,6 +174,7 @@ "dismiss": "Tolak", "done": "Selesai", "enable": "Dayakan", + "goBack": "Pergi balik", "hide": "Sembunyi", "later": "Mungkin kemudian", "learn": "Ketahui lebih lanjut", @@ -205,6 +206,7 @@ "title": "Aduh! Sesuatu telah berlaku." } }, + "endAdornment": "dan", "error": { "general": "Sesuatu telah berlaku." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Dompet" + "action": "Terima", + "fallbackTitle": "Luluskan token perbelanjaan", + "helptext": "Benarkan tapak ini mengakses dan membelanjakan token ini daripada dompet anda.", + "label": "Dompet", + "title": "Luluskan perbelanjaan {{tokenSymbol}}" + }, + "base": { + "title": "Sahkan transaksi" + }, + "changeChain": { + "title": "Tukar rangkaian kepada {{network}}" + }, + "connect": { + "helptext": "Benarkan tapak ini melihat alamat dompet anda, baki dan meminta kelulusan untuk transaksi.", + "title": "Sambung ke tapak" }, "error": { "none": "Tiada kelulusan belum selesai" }, + "fallback": { + "calldata": { + "label": "data" + }, + "function": { + "label": "fungsi" + }, + "recipient": { + "label": "kepada" + }, + "sending": { + "label": "menghantar" + } + }, "signature": { - "education": { - "description": "Tandatangan diperlukan untuk membuktikan bahawa anda memiliki dompet tanpa mendedahkan kunci peribadi anda", - "title": "Apakah permintaan tandatangan?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent menerima data untuk menandatangani yang tidak mematuhi spesifikasi EIP-712." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Dompet anda tidak disambungkan ke tapak ini.", - "popupWithButton": "Dompet anda tidak disambungkan ke tapak ini. Cari butang \"Sambung Dompet\" atau \"Log masuk\"." + "popup": "Dompet anda tidak disambungkan ke tapak ini. Cari butang \"Sambung Dompet\" atau \"Log masuk\".", + "popupWithButton": "Dompet anda tidak disambungkan ke tapak ini.", + "titleConnected": "Bersambung", + "titleNotConnected": "Tidak bersambung" + }, + "dappRequest": { + "signatureRequest": { + "header": "Permintaan tandatangan" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Menghubungkan anda ke {{serviceProvider}}", - "quote": "Membeli {{amount}} bernilai {{currencySymbol}}" + "quote": "Membeli {{amount}} bernilai {{currencySymbol}}", + "terms": "Dengan meneruskan, anda mengakui bahawa anda akan tertakluk pada Syarat Perkhidmatan dan Dasar Privasi dengan {{serviceProvider}}, mengikut mana yang berkenaan." }, "error": { "default": "Sesuatu telah berlaku.", @@ -427,10 +464,9 @@ "usd": "Hanya tersedia untuk pembelian dalam USD" }, "quote": { - "amount": "Terima {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} selepas yuran", + "advice": "Anda akan terus ke portal penyedia untuk melihat yuran yang berkaitan dengan transaksi anda.", + "others": "yang lain", "type": { - "best": "Terbaik secara keseluruhan", "other": "Pilihan lain", "recent": "Baru-baru ini digunakan" } @@ -470,6 +506,11 @@ "title": "Aktiviti" }, "banner": { + "extension": { + "confirm": "Sertai Beta", + "message": "Jadilah yang pertama mencuba Sambungan Uniswap pada penyemak imbas web anda", + "title": "Sambungan Uniswap ada di sini" + }, "offline": "Anda berada dalam mod luar talian" }, "extension": { @@ -491,6 +532,14 @@ "send": "Hantar", "swap": "Tukar" }, + "modal": { + "getExtension": { + "step1": "1. Lawati uniswap.org/ext pada komputer anda", + "step2": "2. Tambahkan Sambungan Uniswap pada penyemak imbas Chrome anda", + "step3": "3. Masukkan nama pengguna anda untuk mendapatkan akses", + "title": "Sertai Uniswap Extension Beta" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Alamat disalin", + "calldata": "Data panggilan disalin", "contractAddress": "Alamat kontrak disalin", "failed": "Gagal menyalin ke papan keratan", "image": "Imej disalin", @@ -715,6 +765,10 @@ "button": "Import dari telefon anda", "title": "Mempunyai apl mudah alih Uniswap?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Muat turun apl mudah alih dan tuntut nama pengguna", + "title": "Dapatkan senarai tunggu Beta" + }, "password": { "subtitle": "Anda memerlukan ini untuk membuka kunci dompet anda dan mengakses frasa pemulihan anda", "title": { @@ -764,6 +818,19 @@ }, "title": "Selamat datang ke \nUniswap Wallet" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Semak kelayakan", + "letsGo": "Mari pergi" + }, + "checkEligibilityInstructions": "Masukkan uni.eth nama pengguna anda di bawah untuk menyemak sama ada anda layak untuk Beta.", + "eligible": { + "tagline": "Selamat datang ke Beta — anda salah seorang yang pertama mencuba dompet Uniswap.", + "title": "Anda tiada dalam senarai menunggu!" + }, + "ineligibleExplanation": "Anda masih dalam senarai menunggu. Kami akan memberitahu anda dalam apl mudah alih Uniswap apabila anda layak!", + "unitagPlaceholder": "nama pengguna" + }, "landing": { "button": { "add": "Tambah dompet sedia ada", @@ -814,12 +881,12 @@ "error": "Maaf, kami tidak dapat memuatkan kod QR sekarang. Sila cuba kaedah sedia ada yang lain.", "otp": { "error": "Kod yang anda serahkan adalah salah, atau terdapat ralat semasa menyerahkan. Sila cuba lagi.", - "failed": "Percubaan gagal: {{count}}", + "failed": "Percubaan gagal: {{number}}", "subtitle": "Semak apl mudah alih Uniswap anda untuk kod 6 aksara", "title": "Masukkan kod sekali sahaja" }, - "subtitle": "Imbas kod QR dengan apl Uniswap untuk mengimport dompet anda", - "title": "Segerakkan daripada telefon anda" + "subtitle": "Imbas kod QR dengan apl mudah alih Uniswap untuk mula mengimport dompet anda", + "title": "Import dompet daripada apl" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Imbas kod QR pada Sambungan Uniswap sekali lagi untuk meneruskan penyegerakan dompet anda.", "title": "Sambungan anda tamat masa" } + }, + "modal": { + "notOnWaitlist": { + "message": "Untuk menjadi layak untuk Uniswap Extension Beta, sertai senarai tunggu dengan menuntut nama pengguna uni.eth", + "title": "Anda tiada dalam senarai menunggu" + }, + "onWaitlist": { + "message": "Kami akan memberitahu anda dalam apl apabila anda layak untuk menyertai Uniswap Extension Beta.", + "title": "Anda masih dalam senarai menunggu" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Semak semula pemindahan", "send": "Hantar" }, + "gas": { + "error": { + "title": "T/A" + }, + "networkCost": { + "title": "Kos Rangkaian" + } + }, + "input": { + "token": { + "balance": { + "title": "Baki: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} pemindahan sebelumnya", + "previous_one": "1 pemindahan sebelumnya", "previous_other": "{{count}} pemindahan sebelumnya", "section": { "favorite": "Dompet kegemaran", "recent": "Baru-baru ini", "search": "Keputusan Carian", + "viewOnly": "Dompet lihat sahaja", "yours": "dompet anda" + }, + "warning": { + "viewOnly": { + "message": "Hanya hantar dana ke dompet ini jika anda mempunyai frasa pemulihan atau mengenali pemilik dompet itu.", + "title": "Anda mempunyai ini sebagai dompet lihat sahaja" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Apabila anda menghantar token ke alamat dompet, token tersebut akan dipaparkan di sini", + "title": "Tiada dompet disimpan" + } + }, + "title": "Kepada" + }, "review": { + "modal": { + "title": "Anda menghantar" + }, "summary": { + "button": { + "title": "Sahkan hantar" + }, "sending": "Menghantar", "to": "Kepada" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Baki {{currencySymbol}} anda telah berkurangan sejak anda memasukkan jumlah yang anda ingin hantar", - "title": "Tak cukup {{currencySymbol}}." + "title": "Tidak cukup {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "okey", + "cancel": "Batal", + "confirm": "sahkan" + } + } }, "newAddress": { "message": "Anda tidak pernah berurusan dengan alamat ini sebelum ini. Sila sahkan bahawa alamat adalah betul sebelum meneruskan.", @@ -1110,12 +1233,16 @@ "viewAll": "Lihat semua", "viewLess": "Lihat kurang" }, + "label": { + "viewOnly": "Lihat sahaja" + }, "title": "Tetapan dompet" } }, "setting": { "appearance": { "option": { + "auto": "Auto", "dark": { "subtitle": "Sentiasa gunakan mod gelap", "title": "Mod gelap" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} sandaran" } }, + "beta": { + "tooltip": "Akan datang!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Jika anda tidak menghidupkan {{biometricsMethod}}, sesiapa sahaja yang mendapat akses kepada peranti anda boleh membuka Uniswap Wallet dan membuat transaksi.", - "ios": "Jika anda tidak menghidupkan biometrik, sesiapa sahaja yang mendapat akses kepada peranti anda boleh membuka Uniswap Wallet dan membuat transaksi." + "android": "Jika anda tidak menghidupkan biometrik, sesiapa sahaja yang mendapat akses kepada peranti anda boleh membuka Uniswap Wallet dan membuat transaksi.", + "ios": "Jika anda tidak menghidupkan {{biometricsMethod}}, sesiapa sahaja yang mendapat akses kepada peranti anda boleh membuka Uniswap Wallet dan membuat transaksi." }, "title": "Adakah anda pasti?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Mata wang tempatan" }, + "giveFeedback": { + "title": "Beri maklum balas" + }, "helpCenter": { "title": "Pusat bantuan" }, @@ -1241,6 +1374,7 @@ "navigate": "Pergi ke tetapan" }, "description": "Uniswap lalai kepada tetapan bahasa peranti anda. Untuk menukar bahasa pilihan anda, pergi ke \"Uniswap\" dalam tetapan peranti anda dan ketik pada \"Bahasa\"", + "exampleTitle": "Bahasa Inggeris", "title": "Bahasa" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Sembunyikan baki kecil" }, + "theme": { + "title": "Tema" + }, "unknownTokens": { "title": "Sembunyikan token yang tidak diketahui" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Maks", + "review": "Tukar ulasan", "swap": "Tukar", "unwrap": "Buka bungkus", "view": "Lihat transaksi", @@ -1398,7 +1536,7 @@ "button": "Tidak cukup {{currencySymbol}}", "cta": { "button": "Beli {{currencySymbol}}", - "message": "Anda memerlukan lebih {{currencySymbol}} untuk menampung kos rangkaian untuk transaksi ini." + "message": "Anda memerlukan lebih banyak {{currencySymbol}} untuk menampung kos rangkaian untuk transaksi ini." }, "title": "Anda tidak mempunyai cukup {{currencySymbol}} untuk menampung kos rangkaian" }, @@ -1608,7 +1746,7 @@ "copy": "Salin ID transaksi", "copyMoonPay": "Salin ID transaksi MoonPay", "view": "Lihat {{tokenSymbol}}", - "viewEtherscan": "Lihat pada {{blockExplorerName}}", + "viewEtherscan": "Lihat di {{blockExplorerName}}", "viewMoonPay": "Lihat di MoonPay" }, "amount": { diff --git a/packages/uniswap/src/i18n/locales/translations/nl-NL.json b/packages/uniswap/src/i18n/locales/translations/nl-NL.json index 859bd2d337a..23a18b72f89 100644 --- a/packages/uniswap/src/i18n/locales/translations/nl-NL.json +++ b/packages/uniswap/src/i18n/locales/translations/nl-NL.json @@ -174,6 +174,7 @@ "dismiss": "Afwijzen", "done": "Gereed", "enable": "Inschakelen", + "goBack": "Ga terug", "hide": "Verbergen", "later": "Misschien later", "learn": "Kom meer te weten", @@ -205,6 +206,7 @@ "title": "Oeps! Er is iets fout gegaan." } }, + "endAdornment": "en", "error": { "general": "Er is iets fout gegaan." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Portemonnee" + "action": "Goedkeuren", + "fallbackTitle": "Keur bestedingstokens goed", + "helptext": "Geef deze site toegang tot dit token en geef het uit uw portemonnee uit.", + "label": "Portemonnee", + "title": "Uitgaven goedkeuren {{tokenSymbol}}" + }, + "base": { + "title": "Transactie bevestigen" + }, + "changeChain": { + "title": "Schakel het netwerk naar {{network}}" + }, + "connect": { + "helptext": "Sta toe dat deze site uw portemonneeadres en saldo bekijkt en goedkeuringen voor transacties aanvraagt.", + "title": "Maak verbinding met de locatie" }, "error": { "none": "Er zijn geen goedkeuringen in behandeling" }, + "fallback": { + "calldata": { + "label": "gegevens" + }, + "function": { + "label": "functie" + }, + "recipient": { + "label": "naar" + }, + "sending": { + "label": "bezig met verzenden" + } + }, "signature": { - "education": { - "description": "Er is een handtekening vereist om te bewijzen dat u de eigenaar van de portemonnee bent, zonder uw privésleutels bloot te leggen", - "title": "Wat is een handtekeningverzoek?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent heeft gegevens ontvangen voor ondertekening die niet voldoen aan de EIP-712-specificatie." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Uw portemonnee is niet verbonden met deze site.", - "popupWithButton": "Uw portemonnee is niet verbonden met deze site. Zoek naar de knop 'Wallet verbinden' of 'Inloggen'." + "popup": "Uw portemonnee is niet verbonden met deze site. Zoek naar de knop 'Wallet verbinden' of 'Inloggen'.", + "popupWithButton": "Uw portemonnee is niet verbonden met deze site.", + "titleConnected": "Gekoppeld", + "titleNotConnected": "Niet verbonden" + }, + "dappRequest": { + "signatureRequest": { + "header": "Handtekening verzoek" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Ik verbind je met {{serviceProvider}}", - "quote": "{{amount}} waarde van {{currencySymbol}}kopen" + "quote": "{{amount}} waarde van {{currencySymbol}}kopen", + "terms": "Als u doorgaat, erkent u dat u onderworpen bent aan de Servicevoorwaarden en het Privacybeleid met {{serviceProvider}}, indien van toepassing." }, "error": { "default": "Er is iets fout gegaan.", @@ -427,10 +464,9 @@ "usd": "Alleen verkrijgbaar in USD" }, "quote": { - "amount": "Ontvang {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} na kosten", + "advice": "U gaat door naar het portaal van de aanbieder om de kosten te zien die aan uw transactie zijn verbonden.", + "others": "anderen", "type": { - "best": "Beste overall", "other": "Andere opties", "recent": "Laatst gebruikt" } @@ -470,6 +506,11 @@ "title": "Activiteit" }, "banner": { + "extension": { + "confirm": "Sluit je aan bij Bèta", + "message": "Probeer als eerste de Uniswap-extensie uit in uw webbrowser", + "title": "Uniswap-extensie is hier" + }, "offline": "U bevindt zich in de offlinemodus" }, "extension": { @@ -491,6 +532,14 @@ "send": "Versturen", "swap": "Ruil" }, + "modal": { + "getExtension": { + "step1": "1. Ga naar uniswap.org/ext op je computer", + "step2": "2. Voeg de Uniswap-extensie toe aan uw Chrome-browser", + "step3": "3. Voer uw gebruikersnaam in om toegang te krijgen", + "title": "Doe mee aan de Uniswap-extensiebèta" + } + }, "nfts": { "title": "NFT's" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Adres gekopieerd", + "calldata": "Oproepgegevens gekopieerd", "contractAddress": "Contractadres gekopieerd", "failed": "Kon niet naar klembord kopiëren", "image": "Afbeelding gekopieerd", @@ -715,6 +765,10 @@ "button": "Importeren vanaf uw telefoon", "title": "Heb je de Uniswap mobiele app?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Download de mobiele app en claim een gebruikersnaam", + "title": "Zet u op de bèta-wachtlijst" + }, "password": { "subtitle": "U heeft dit nodig om uw portemonnee te ontgrendelen en toegang te krijgen tot uw herstelzin", "title": { @@ -764,6 +818,19 @@ }, "title": "Welkom bij \nUniswap-portemonnee" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Controleer geschiktheid", + "letsGo": "Laten we gaan" + }, + "checkEligibilityInstructions": "Voer hieronder uw uni.eth gebruikersnaam in om te controleren of u in aanmerking komt voor de bèta.", + "eligible": { + "tagline": "Welkom bij de bèta: u bent een van de eersten die de Uniswap-portemonnee uitprobeert.", + "title": "Je bent van de wachtlijst verwijderd!" + }, + "ineligibleExplanation": "Je staat nog steeds op de wachtlijst. We laten het je weten in de Uniswap mobiele app wanneer je in aanmerking komt!", + "unitagPlaceholder": "gebruikersnaam" + }, "landing": { "button": { "add": "Voeg een bestaande portemonnee toe", @@ -814,12 +881,12 @@ "error": "Sorry, we kunnen de QR-code momenteel niet laden. Probeer een andere onboardingmethode.", "otp": { "error": "De code die u heeft ingediend, is onjuist of er is een fout opgetreden bij het indienen. Probeer het opnieuw.", - "failed": "Mislukte pogingen: {{count}}", + "failed": "Mislukte pogingen: {{number}}", "subtitle": "Controleer uw Uniswap mobiele app voor de code van 6 tekens", "title": "Voer eenmalige code in" }, - "subtitle": "Scan de QR-code met de Uniswap-app om uw portemonnee te importeren", - "title": "Synchroniseer vanaf uw telefoon" + "subtitle": "Scan de QR-code met de Uniswap mobiele app om te beginnen met het importeren van uw portemonnee(s)", + "title": "Importeer portemonnee vanuit de app" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Scan de QR-code op de Uniswap-extensie opnieuw om door te gaan met het synchroniseren van uw portemonnee.", "title": "Er is een time-out opgetreden in uw verbinding" } + }, + "modal": { + "notOnWaitlist": { + "message": "Om in aanmerking te komen voor de Uniswap Extension Beta, moet u zich op de wachtlijst plaatsen door een uni.eth-gebruikersnaam te claimen", + "title": "Je staat niet op de wachtlijst" + }, + "onWaitlist": { + "message": "We laten het u weten in de app wanneer u in aanmerking komt voor deelname aan de Uniswap Extensie Bèta.", + "title": "Je staat nog steeds op de wachtlijst" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Overdracht beoordelen", "send": "Versturen" }, + "gas": { + "error": { + "title": "N.v.t" + }, + "networkCost": { + "title": "Netwerkkosten" + } + }, + "input": { + "token": { + "balance": { + "title": "Saldo: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} vorige overdracht", + "previous_one": "1 eerdere overdracht", "previous_other": "{{count}} eerdere overdrachten", "section": { "favorite": "Favoriete portemonnees", "recent": "Recent", "search": "Zoekresultaten", + "viewOnly": "Alleen-lezen-portemonnees", "yours": "Je portemonnees" + }, + "warning": { + "viewOnly": { + "message": "Stuur alleen geld naar deze portemonnee als u over de herstelzin beschikt of als u de eigenaar van de portemonnee kent.", + "title": "Je hebt dit als een alleen-lezen portemonnee" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Wanneer u tokens naar een portemonnee-adres verzendt, worden ze hier weergegeven", + "title": "Geen portemonnees opgeslagen" + } + }, + "title": "Naar" + }, "review": { + "modal": { + "title": "Je verzendt" + }, "summary": { + "button": { + "title": "Bevestig het verzenden" + }, "sending": "Bezig met verzenden", "to": "Naar" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Uw {{currencySymbol}} saldo is afgenomen sinds u het bedrag heeft ingevoerd dat u wilt verzenden", - "title": "Niet genoeg {{currencySymbol}}." + "title": "Niet genoeg {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "OK", + "cancel": "Annuleer", + "confirm": "Bevestig" + } + } }, "newAddress": { "message": "U heeft nog niet eerder een transactie met dit adres uitgevoerd. Controleer of het adres correct is voordat u verdergaat.", @@ -1110,12 +1233,16 @@ "viewAll": "Bekijk alles", "viewLess": "Bekijk minder" }, + "label": { + "viewOnly": "Alleen-lezen" + }, "title": "Portemonnee-instellingen" } }, "setting": { "appearance": { "option": { + "auto": "Auto", "dark": { "subtitle": "Gebruik standaard darkmode", "title": "Donkere modus" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} back-up" } }, + "beta": { + "tooltip": "Binnenkort beschikbaar!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Als u {{biometricsMethod}}niet inschakelt, kan iedereen die toegang krijgt tot uw apparaat Uniswap Wallet openen en transacties uitvoeren.", - "ios": "Als u biometrie niet inschakelt, kan iedereen die toegang krijgt tot uw apparaat Uniswap Wallet openen en transacties uitvoeren." + "android": "Als u biometrie niet inschakelt, kan iedereen die toegang krijgt tot uw apparaat Uniswap Wallet openen en transacties uitvoeren.", + "ios": "Als u {{biometricsMethod}}niet inschakelt, kan iedereen die toegang krijgt tot uw apparaat Uniswap Wallet openen en transacties uitvoeren." }, "title": "Weet je het zeker?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Lokale munteenheid" }, + "giveFeedback": { + "title": "Geef feedback" + }, "helpCenter": { "title": "Helpcentrum" }, @@ -1241,6 +1374,7 @@ "navigate": "Ga naar Instellingen" }, "description": "Uniswap is standaard ingesteld op de taalinstellingen van uw apparaat. Om uw voorkeurstaal te wijzigen, gaat u naar “Uniswap” in uw apparaatinstellingen en tikt u op “Taal”", + "exampleTitle": "Engels", "title": "Taal" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Verberg kleine saldi" }, + "theme": { + "title": "Thema" + }, "unknownTokens": { "title": "Verberg onbekende tokens" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Max", + "review": "Recensiewissel", "swap": "Ruil", "unwrap": "Uitpakken", "view": "Transactie bekijken", diff --git a/packages/uniswap/src/i18n/locales/translations/pt-PT.json b/packages/uniswap/src/i18n/locales/translations/pt-PT.json index 6ed31ff71e8..352d5aa6293 100644 --- a/packages/uniswap/src/i18n/locales/translations/pt-PT.json +++ b/packages/uniswap/src/i18n/locales/translations/pt-PT.json @@ -174,6 +174,7 @@ "dismiss": "Dispensar", "done": "Feito", "enable": "Habilitar", + "goBack": "Volte", "hide": "Ocultar", "later": "Talvez mais tarde", "learn": "Saiba mais", @@ -205,6 +206,7 @@ "title": "Ops! Algo deu errado." } }, + "endAdornment": "e", "error": { "general": "Algo deu errado." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Carteira" + "action": "Aprovar", + "fallbackTitle": "Aprovar tokens de gastos", + "helptext": "Permita que este site acesse e gaste este token de sua carteira.", + "label": "Carteira", + "title": "Aprovar gastos {{tokenSymbol}}" + }, + "base": { + "title": "Confirmar transação" + }, + "changeChain": { + "title": "Mude a rede para {{network}}" + }, + "connect": { + "helptext": "Permita que este site visualize o endereço e o saldo da sua carteira e solicite aprovações para transações.", + "title": "Conectar ao site" }, "error": { "none": "Nenhuma aprovação pendente" }, + "fallback": { + "calldata": { + "label": "dados" + }, + "function": { + "label": "função" + }, + "recipient": { + "label": "para" + }, + "sending": { + "label": "enviando" + } + }, "signature": { - "education": { - "description": "É necessária uma assinatura para provar que você possui a carteira sem expor suas chaves privadas", - "title": "O que é um pedido de assinatura?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent recebeu dados para assinatura que não estão em conformidade com a especificação EIP-712." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Sua carteira não está conectada a este site.", - "popupWithButton": "Sua carteira não está conectada a este site. Procure o botão “Conectar carteira” ou “Login”." + "popup": "Sua carteira não está conectada a este site. Procure o botão “Conectar carteira” ou “Login”.", + "popupWithButton": "Sua carteira não está conectada a este site.", + "titleConnected": "Conectado", + "titleNotConnected": "Não conectado" + }, + "dappRequest": { + "signatureRequest": { + "header": "Solicitação de assinatura" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Conectando você a {{serviceProvider}}", - "quote": "Comprando {{amount}} de {{currencySymbol}}" + "quote": "Comprando {{amount}} de {{currencySymbol}}", + "terms": "Ao continuar, você reconhece que estará sujeito aos Termos de Serviço e à Política de Privacidade de {{serviceProvider}}, conforme aplicável." }, "error": { "default": "Algo deu errado.", @@ -427,10 +464,9 @@ "usd": "Disponível apenas para compra em dólares americanos" }, "quote": { - "amount": "Receber {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} após taxas", + "advice": "Você continuará no portal do provedor para ver as taxas associadas à sua transação.", + "others": "outros", "type": { - "best": "Melhor geral", "other": "Outras opções", "recent": "Usado recentemente" } @@ -470,6 +506,11 @@ "title": "Atividade" }, "banner": { + "extension": { + "confirm": "Junte-se à versão beta", + "message": "Seja o primeiro a experimentar a extensão Uniswap em seu navegador", + "title": "A extensão Uniswap está aqui" + }, "offline": "Você está em modo offline" }, "extension": { @@ -491,6 +532,14 @@ "send": "Enviar", "swap": "Trocar" }, + "modal": { + "getExtension": { + "step1": "1. Visite uniswap.org/ext no seu computador", + "step2": "2. Adicione a extensão Uniswap em seu navegador Chrome", + "step3": "3. Digite seu nome de usuário para obter acesso", + "title": "Junte-se à extensão beta do Uniswap" + } + }, "nfts": { "title": "NFTs" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Endereço copiado", + "calldata": "Dados de chamada copiados", "contractAddress": "Endereço do contrato copiado", "failed": "Falha ao copiar para área de transferência", "image": "Imagem copiada", @@ -715,6 +765,10 @@ "button": "Importe do seu telefone", "title": "Possui o aplicativo móvel Uniswap?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Baixe o aplicativo móvel e reivindique um nome de usuário", + "title": "Entre na lista de espera do Beta" + }, "password": { "subtitle": "Você precisará disso para desbloquear sua carteira e acessar sua frase de recuperação", "title": { @@ -764,6 +818,19 @@ }, "title": "Bem-vindo à \nCarteira Uniswap" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Verifique a elegibilidade", + "letsGo": "Vamos" + }, + "checkEligibilityInstructions": "Digite seu nome de usuário uni.eth abaixo para verificar se você é elegível para o Beta.", + "eligible": { + "tagline": "Bem-vindo ao Beta – você é um dos primeiros a experimentar a carteira Uniswap.", + "title": "Você saiu da lista de espera!" + }, + "ineligibleExplanation": "Você ainda está na lista de espera. Iremos notificá-lo no aplicativo móvel Uniswap quando você se tornar elegível!", + "unitagPlaceholder": "nome de usuário" + }, "landing": { "button": { "add": "Adicionar uma carteira existente", @@ -814,12 +881,12 @@ "error": "Desculpe, não é possível carregar o código QR no momento. Tente outro método de integração.", "otp": { "error": "O código que você enviou está incorreto ou ocorreu um erro ao enviar. Por favor, tente novamente.", - "failed": "Tentativas falhadas: {{count}}", + "failed": "Tentativas falhadas: {{number}}", "subtitle": "Verifique seu aplicativo móvel Uniswap para obter o código de 6 caracteres", "title": "Insira o código único" }, - "subtitle": "Digitalize o código QR com o aplicativo Uniswap para importar sua carteira", - "title": "Sincronize do seu telefone" + "subtitle": "Digitalize o código QR com o aplicativo móvel Uniswap para começar a importar sua(s) carteira(s)", + "title": "Importe carteira do aplicativo" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Digitalize o código QR na extensão Uniswap novamente para continuar sincronizando sua carteira.", "title": "Sua conexão expirou" } + }, + "modal": { + "notOnWaitlist": { + "message": "Para se tornar elegível para a extensão Uniswap Beta, entre na lista de espera reivindicando um nome de usuário uni.eth", + "title": "Você não está na lista de espera" + }, + "onWaitlist": { + "message": "Notificaremos você no aplicativo quando você se tornar elegível para ingressar na extensão beta do Uniswap.", + "title": "Você ainda está na lista de espera" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Revisar transferência", "send": "Enviar" }, + "gas": { + "error": { + "title": "N / D" + }, + "networkCost": { + "title": "Custo de rede" + } + }, + "input": { + "token": { + "balance": { + "title": "Saldo: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} transferência anterior", + "previous_one": "1 transferência anterior", "previous_other": "{{count}} transferências anteriores", "section": { "favorite": "Carteiras favoritas", "recent": "Recente", "search": "Resultados da pesquisa", + "viewOnly": "Carteiras somente para visualização", "yours": "Suas carteiras" + }, + "warning": { + "viewOnly": { + "message": "Envie fundos para esta carteira apenas se você tiver a frase de recuperação ou conhecer o dono da carteira.", + "title": "Você tem isso como uma carteira somente para visualização" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Quando você envia tokens para um endereço de carteira, eles aparecerão aqui", + "title": "Nenhuma carteira salva" + } + }, + "title": "Para" + }, "review": { + "modal": { + "title": "Você está enviando" + }, "summary": { + "button": { + "title": "Confirmar envio" + }, "sending": "Enviando", "to": "Para" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Seu saldo de {{currencySymbol}} diminuiu desde que você inseriu o valor que gostaria de enviar", - "title": "Não é suficiente {{currencySymbol}}." + "title": "Não é suficiente {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "OK", + "cancel": "Cancelar", + "confirm": "Confirmar" + } + } }, "newAddress": { "message": "Você nunca fez transações com este endereço antes. Por favor, confirme se o endereço está correto antes de continuar.", @@ -1110,12 +1233,16 @@ "viewAll": "Ver tudo", "viewLess": "Ver menos" }, + "label": { + "viewOnly": "Somente visualização" + }, "title": "Configurações da carteira" } }, "setting": { "appearance": { "option": { + "auto": "Auto", "dark": { "subtitle": "Sempre usar o modo escuro", "title": "Modo escuro" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} cópia de segurança" } }, + "beta": { + "tooltip": "Em breve!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Se você não ativar {{biometricsMethod}}, qualquer pessoa que tenha acesso ao seu dispositivo poderá abrir a Carteira Uniswap e fazer transações.", - "ios": "Se você não ativar a biometria, qualquer pessoa que tenha acesso ao seu dispositivo poderá abrir a Carteira Uniswap e fazer transações." + "android": "Se você não ativar a biometria, qualquer pessoa que tenha acesso ao seu dispositivo poderá abrir a Carteira Uniswap e fazer transações.", + "ios": "Se você não ativar {{biometricsMethod}}, qualquer pessoa que tenha acesso ao seu dispositivo poderá abrir a Carteira Uniswap e fazer transações." }, "title": "Tem certeza?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Moeda local" }, + "giveFeedback": { + "title": "Dê retorno" + }, "helpCenter": { "title": "Centro de ajuda" }, @@ -1241,6 +1374,7 @@ "navigate": "Ir para as configurações" }, "description": "Uniswap usa o idioma do seu dispositivo como padrão. Para alterar seu idioma preferido, vá em “Uniswap” nas configurações do seu dispositivo e toque em “Idioma”", + "exampleTitle": "Inglês", "title": "Linguagem" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Ocultar saldos pequenos" }, + "theme": { + "title": "Tema" + }, "unknownTokens": { "title": "Ocultar tokens desconhecidos" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Máx.", + "review": "Rever troca", "swap": "Trocar", "unwrap": "Desembrulhar", "view": "Ver transação", diff --git a/packages/uniswap/src/i18n/locales/translations/ru-RU.json b/packages/uniswap/src/i18n/locales/translations/ru-RU.json index ea357fd1c01..fbd0d7a3a22 100644 --- a/packages/uniswap/src/i18n/locales/translations/ru-RU.json +++ b/packages/uniswap/src/i18n/locales/translations/ru-RU.json @@ -174,6 +174,7 @@ "dismiss": "Увольнять", "done": "Сделанный", "enable": "Давать возможность", + "goBack": "Возвращаться", "hide": "Скрывать", "later": "Может быть позже", "learn": "Узнать больше", @@ -205,6 +206,7 @@ "title": "Упс! Что-то пошло не так." } }, + "endAdornment": "и", "error": { "general": "Что-то пошло не так." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Кошелек" + "action": "Утвердить", + "fallbackTitle": "Утвердить жетоны расходов", + "helptext": "Разрешите этому сайту получить доступ и потратить этот токен из вашего кошелька.", + "label": "Кошелек", + "title": "Одобрить расходы {{tokenSymbol}}" + }, + "base": { + "title": "Подтвердить транзакцию" + }, + "changeChain": { + "title": "Переключить сеть на {{network}}" + }, + "connect": { + "helptext": "Разрешите этому сайту просматривать адрес вашего кошелька, баланс и запрашивать одобрение транзакций.", + "title": "Подключиться к сайту" }, "error": { "none": "Никаких одобрений не ожидается" }, + "fallback": { + "calldata": { + "label": "данные" + }, + "function": { + "label": "функция" + }, + "recipient": { + "label": "к" + }, + "sending": { + "label": "отправка" + } + }, "signature": { - "education": { - "description": "Подпись необходима для подтверждения того, что вы являетесь владельцем кошелька, не раскрывая при этом свои личные ключи.", - "title": "Что такое запрос на подпись?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent получил данные для подписи, которые не соответствуют спецификации EIP-712." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Ваш кошелек не подключен к этому сайту.", - "popupWithButton": "Ваш кошелек не подключен к этому сайту. Найдите кнопку «Подключить кошелек» или «Войти»." + "popup": "Ваш кошелек не подключен к этому сайту. Найдите кнопку «Подключить кошелек» или «Войти».", + "popupWithButton": "Ваш кошелек не подключен к этому сайту.", + "titleConnected": "Связанный", + "titleNotConnected": "Не подключен" + }, + "dappRequest": { + "signatureRequest": { + "header": "Запрос на подпись" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Соединяю вас с {{serviceProvider}}", - "quote": "Покупка {{amount}} на сумму {{currencySymbol}}" + "quote": "Покупка {{amount}} на сумму {{currencySymbol}}", + "terms": "Продолжая, вы подтверждаете, что на вас распространяются Условия обслуживания и Политика конфиденциальности с {{serviceProvider}}, если применимо." }, "error": { "default": "Что-то пошло не так.", @@ -427,10 +464,9 @@ "usd": "Доступно для покупки только в долларах США." }, "quote": { - "amount": "Получить {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} после комиссии", + "advice": "Вы перейдете на портал провайдера, чтобы увидеть комиссии, связанные с вашей транзакцией.", + "others": "другие", "type": { - "best": "Лучший в целом", "other": "Другие варианты", "recent": "Недавно использовано" } @@ -470,6 +506,11 @@ "title": "Активность" }, "banner": { + "extension": { + "confirm": "Присоединяйтесь к бета-тестированию", + "message": "Будьте первым, кто опробует расширение Uniswap в своем веб-браузере.", + "title": "Расширение Uniswap здесь" + }, "offline": "Вы находитесь в автономном режиме" }, "extension": { @@ -491,6 +532,14 @@ "send": "Отправлять", "swap": "Менять" }, + "modal": { + "getExtension": { + "step1": "1. Посетите uniswap.org/ext на своем компьютере.", + "step2": "2. Добавьте расширение Uniswap в браузер Chrome.", + "step3": "3. Введите свое имя пользователя, чтобы получить доступ.", + "title": "Присоединяйтесь к бета-версии расширения Uniswap" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Адрес скопирован", + "calldata": "Данные вызова скопированы.", "contractAddress": "Адрес контракта скопирован.", "failed": "Не удалось скопировать в буфер обмена", "image": "Изображение скопировано", @@ -715,6 +765,10 @@ "button": "Импортируйте с телефона", "title": "У вас есть мобильное приложение Uniswap?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Загрузите мобильное приложение и запросите имя пользователя", + "title": "Встаньте в список ожидания бета-тестирования" + }, "password": { "subtitle": "Это понадобится вам, чтобы разблокировать кошелек и получить доступ к фразе восстановления.", "title": { @@ -764,6 +818,19 @@ }, "title": "Добро пожаловать в \nкошелек Uniswap" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Проверьте право на участие", + "letsGo": "Пойдем" + }, + "checkEligibilityInstructions": "Введите свое имя пользователя uni.eth ниже, чтобы проверить, имеете ли вы право на участие в бета-тестировании.", + "eligible": { + "tagline": "Добро пожаловать в бета-версию — вы один из первых, кто опробует кошелек Uniswap.", + "title": "Вы исключены из списка ожидания!" + }, + "ineligibleExplanation": "Вы все еще в списке ожидания. Мы уведомим вас в мобильном приложении Uniswap, когда вы получите право на участие!", + "unitagPlaceholder": "имя пользователя" + }, "landing": { "button": { "add": "Добавить существующий кошелек", @@ -814,12 +881,12 @@ "error": "К сожалению, сейчас мы не можем загрузить QR-код. Пожалуйста, попробуйте другой метод регистрации.", "otp": { "error": "Отправленный вами код неверен, или произошла ошибка при отправке. Пожалуйста, попробуйте еще раз.", - "failed": "Неудачные попытки: {{count}}", + "failed": "Неудачные попытки: {{number}}", "subtitle": "Проверьте свое мобильное приложение Uniswap на наличие 6-значного кода.", "title": "Введите одноразовый код" }, - "subtitle": "Отсканируйте QR-код с помощью приложения Uniswap, чтобы импортировать свой кошелек.", - "title": "Синхронизация с телефона" + "subtitle": "Отсканируйте QR-код с помощью мобильного приложения Uniswap, чтобы начать импорт кошельков.", + "title": "Импортировать кошелек из приложения" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Еще раз отсканируйте QR-код на расширении Uniswap, чтобы продолжить синхронизацию вашего кошелька.", "title": "Время ожидания вашего соединения истекло" } + }, + "modal": { + "notOnWaitlist": { + "message": "Чтобы получить право на участие в бета-тестировании расширения Uniswap, присоединитесь к списку ожидания, указав имя пользователя uni.eth.", + "title": "Вас нет в списке ожидания" + }, + "onWaitlist": { + "message": "Мы уведомим вас в приложении, когда вы получите право присоединиться к бета-версии расширения Uniswap.", + "title": "Вы все еще в списке ожидания" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Обзор передачи", "send": "Отправлять" }, + "gas": { + "error": { + "title": "Н/Д" + }, + "networkCost": { + "title": "Стоимость сети" + } + }, + "input": { + "token": { + "balance": { + "title": "Баланс: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} предыдущий перевод", + "previous_one": "1 предыдущий перевод", "previous_other": "{{count}} предыдущие передачи", "section": { "favorite": "Любимые кошельки", "recent": "Недавний", "search": "Результаты поиска", + "viewOnly": "Кошельки только для просмотра", "yours": "Ваши кошельки" + }, + "warning": { + "viewOnly": { + "message": "Отправляйте средства на этот кошелек только в том случае, если у вас есть фраза восстановления или вы знаете владельца кошелька.", + "title": "У вас это кошелек только для просмотра." + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Когда вы отправляете токены на адрес кошелька, они появятся здесь.", + "title": "Кошельки не сохранены" + } + }, + "title": "К" + }, "review": { + "modal": { + "title": "Вы отправляете" + }, "summary": { + "button": { + "title": "Подтвердить отправку" + }, "sending": "Отправка", "to": "К" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Ваш баланс {{currencySymbol}} уменьшился с тех пор, как вы ввели сумму, которую хотите отправить.", - "title": "Недостаточно {{currencySymbol}}." + "title": "Недостаточно {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "ХОРОШО", + "cancel": "Отмена", + "confirm": "Подтверждать" + } + } }, "newAddress": { "message": "Вы еще не совершали транзакций с этого адреса. Прежде чем продолжить, подтвердите правильность адреса.", @@ -1110,12 +1233,16 @@ "viewAll": "Посмотреть все", "viewLess": "Посмотреть меньше" }, + "label": { + "viewOnly": "Только просмотр" + }, "title": "Настройки кошелька" } }, "setting": { "appearance": { "option": { + "auto": "Авто", "dark": { "subtitle": "Всегда используйте темный режим", "title": "Темный режим" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} резервная копия" } }, + "beta": { + "tooltip": "Вскоре!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Если вы не включите {{biometricsMethod}}, любой, кто получит доступ к вашему устройству, сможет открыть Uniswap Wallet и совершать транзакции.", - "ios": "Если вы не включите биометрию, любой, кто получит доступ к вашему устройству, сможет открыть Uniswap Wallet и совершать транзакции." + "android": "Если вы не включите биометрию, любой, кто получит доступ к вашему устройству, сможет открыть Uniswap Wallet и совершать транзакции.", + "ios": "Если вы не включите {{biometricsMethod}}, любой, кто получит доступ к вашему устройству, сможет открыть Uniswap Wallet и совершать транзакции." }, "title": "Вы уверены?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Местная валюта" }, + "giveFeedback": { + "title": "Дать обратную связь" + }, "helpCenter": { "title": "Центр помощи" }, @@ -1241,6 +1374,7 @@ "navigate": "Перейдите в настройки" }, "description": "Uniswap по умолчанию использует языковые настройки вашего устройства. Чтобы изменить предпочитаемый язык, перейдите в «Uniswap» в настройках вашего устройства и нажмите «Язык».", + "exampleTitle": "Английский", "title": "Язык" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Скрыть небольшие остатки" }, + "theme": { + "title": "Тема" + }, "unknownTokens": { "title": "Скрыть неизвестные токены" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Макс", + "review": "Обзор обмена", "swap": "Менять", "unwrap": "Развернуть", "view": "Посмотреть транзакцию", diff --git a/packages/uniswap/src/i18n/locales/translations/th-TH.json b/packages/uniswap/src/i18n/locales/translations/th-TH.json index d7d27c921f7..3c8b1b88066 100644 --- a/packages/uniswap/src/i18n/locales/translations/th-TH.json +++ b/packages/uniswap/src/i18n/locales/translations/th-TH.json @@ -58,7 +58,7 @@ }, "recoveryPhrase": { "education": { - "part1": "วลีกู้คืน (หรือวลีเริ่มต้น) คือชุดของคำ ที่จำเป็นในการเข้าถึงกระเป๋าสตางค์ของคุณ เช่นเดียวกับรหัสผ่าน", + "part1": "วลีกู้คืน (หรือวลีเริ่มต้น) คือชุดของคำ ที่จำเป็นในการเข้าถึงกระเป๋าเงินของคุณ เช่นเดียวกับรหัสผ่าน", "part2": "คุณสามารถ ป้อน วลีการกู้คืนของคุณบนอุปกรณ์ใหม่ เพื่อกู้คืนกระเป๋าเงินของคุณ และเนื้อหาในนั้น", "part3": "แต่ถ้าคุณ สูญเสียวลีกู้คืนคุณจะ สูญเสียการเข้าถึง กระเป๋าเงินของคุณ", "part4": "แทนที่จะจดจำวลีการกู้คืนของคุณ คุณสามารถ สำรองข้อมูลได้ถึง {{cloudProviderName}} และป้องกันด้วยรหัสผ่าน", @@ -174,6 +174,7 @@ "dismiss": "อนุญาตให้ออกไป", "done": "เสร็จแล้ว", "enable": "เปิดใช้งาน", + "goBack": "กลับไป", "hide": "ซ่อน", "later": "ไว้ก่อน", "learn": "เรียนรู้เพิ่มเติม", @@ -205,6 +206,7 @@ "title": "อ๊ะ! บางอย่างผิดพลาด." } }, + "endAdornment": "และ", "error": { "general": "บางอย่างผิดพลาด." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "กระเป๋าสตางค์" + "action": "อนุมัติ", + "fallbackTitle": "อนุมัติโทเค็นการใช้จ่าย", + "helptext": "อนุญาตให้ไซต์นี้เข้าถึงและใช้โทเค็นนี้จากกระเป๋าเงินของคุณ", + "label": "กระเป๋าสตางค์", + "title": "อนุมัติการใช้จ่าย {{tokenSymbol}}" + }, + "base": { + "title": "ยืนยันการทำธุรกรรม" + }, + "changeChain": { + "title": "สลับเครือข่ายเป็น {{network}}" + }, + "connect": { + "helptext": "อนุญาตให้ไซต์นี้ดูที่อยู่กระเป๋าเงินของคุณ ยอดคงเหลือ และขออนุมัติการทำธุรกรรม", + "title": "เชื่อมต่อกับไซต์" }, "error": { "none": "ไม่มีการอนุมัติที่รอดำเนินการ" }, + "fallback": { + "calldata": { + "label": "ข้อมูล" + }, + "function": { + "label": "การทำงาน" + }, + "recipient": { + "label": "ถึง" + }, + "sending": { + "label": "การส่ง" + } + }, "signature": { - "education": { - "description": "จำเป็นต้องมีลายเซ็นเพื่อพิสูจน์ว่าคุณเป็นเจ้าของกระเป๋าเงินโดยไม่ต้องเปิดเผยกุญแจส่วนตัวของคุณ", - "title": "การขอลายเซ็นคืออะไร?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent ได้รับข้อมูลสำหรับการลงนามซึ่งไม่เป็นไปตามข้อกำหนด EIP-712" } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "กระเป๋าเงินของคุณไม่ได้เชื่อมต่อกับไซต์นี้", - "popupWithButton": "กระเป๋าเงินของคุณไม่ได้เชื่อมต่อกับไซต์นี้ มองหาปุ่ม \"เชื่อมต่อ Wallet\" หรือ \"เข้าสู่ระบบ\"" + "popup": "กระเป๋าเงินของคุณไม่ได้เชื่อมต่อกับไซต์นี้ มองหาปุ่ม \"เชื่อมต่อ Wallet\" หรือ \"เข้าสู่ระบบ\"", + "popupWithButton": "กระเป๋าเงินของคุณไม่ได้เชื่อมต่อกับไซต์นี้", + "titleConnected": "เชื่อมต่อแล้ว", + "titleNotConnected": "ไม่ได้เชื่อมต่อ" + }, + "dappRequest": { + "signatureRequest": { + "header": "ขอลายเซ็น" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "กำลังเชื่อมต่อคุณกับ {{serviceProvider}}", - "quote": "ซื้อ {{amount}} มูลค่า {{currencySymbol}}" + "quote": "ซื้อ {{amount}} มูลค่า {{currencySymbol}}", + "terms": "การดำเนินการต่อแสดงว่าคุณรับทราบว่าคุณจะต้องปฏิบัติตามข้อกำหนดในการให้บริการและนโยบายความเป็นส่วนตัวกับ {{serviceProvider}}ตามความเหมาะสม" }, "error": { "default": "บางอย่างผิดพลาด.", @@ -427,10 +464,9 @@ "usd": "สามารถซื้อได้ในสกุลเงิน USD เท่านั้น" }, "quote": { - "amount": "รับ {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} หลังหักค่าธรรมเนียม", + "advice": "คุณจะไปที่พอร์ทัลของผู้ให้บริการเพื่อดูค่าธรรมเนียมที่เกี่ยวข้องกับธุรกรรมของคุณ", + "others": "คนอื่น", "type": { - "best": "โดยรวมดีที่สุด", "other": "ตัวเลือกอื่น", "recent": "ใช้ล่าสุด" } @@ -470,6 +506,11 @@ "title": "กิจกรรม" }, "banner": { + "extension": { + "confirm": "เข้าร่วมเบต้า", + "message": "เป็นคนแรกที่ลองใช้ Uniswap Extension บนเว็บเบราว์เซอร์ของคุณ", + "title": "ส่วนขยาย Uniswap อยู่ที่นี่" + }, "offline": "คุณอยู่ในโหมดออฟไลน์" }, "extension": { @@ -491,6 +532,14 @@ "send": "ส่ง", "swap": "แลกเปลี่ยน" }, + "modal": { + "getExtension": { + "step1": "1. ไปที่ uniswap.org/ext บนคอมพิวเตอร์ของคุณ", + "step2": "2. เพิ่มส่วนขยาย Uniswap บนเบราว์เซอร์ Chrome ของคุณ", + "step3": "3. ป้อนชื่อผู้ใช้ของคุณเพื่อเข้าถึง", + "title": "เข้าร่วมส่วนขยาย Uniswap เบต้า" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "คัดลอกที่อยู่แล้ว", + "calldata": "คัดลอกข้อมูลการโทรแล้ว", "contractAddress": "คัดลอกที่อยู่สัญญาแล้ว", "failed": "คัดลอกไปยังคลิปบอร์ดไม่สำเร็จ", "image": "คัดลอกรูปภาพแล้ว", @@ -715,6 +765,10 @@ "button": "นำเข้าจากโทรศัพท์ของคุณ", "title": "มีแอพมือถือ Uniswap ไหม?" }, + "getOnTheBetaWaitlist": { + "subtitle": "ดาวน์โหลดแอปมือถือและอ้างสิทธิ์ชื่อผู้ใช้", + "title": "เข้าสู่รายชื่อรอเบต้า" + }, "password": { "subtitle": "คุณจะต้องใช้สิ่งนี้เพื่อปลดล็อคกระเป๋าเงินของคุณและเข้าถึงวลีกู้คืนของคุณ", "title": { @@ -764,6 +818,19 @@ }, "title": "ยินดีต้อนรับสู่ \nUniswap Wallet" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "ตรวจสอบสิทธิ์", + "letsGo": "ไปกันเถอะ" + }, + "checkEligibilityInstructions": "ป้อนชื่อผู้ใช้ uni.eth ของคุณด้านล่างเพื่อตรวจสอบว่าคุณมีสิทธิ์ใช้เบต้าหรือไม่", + "eligible": { + "tagline": "ยินดีต้อนรับสู่เบต้า — คุณเป็นคนแรกๆ ที่ลองใช้กระเป๋าเงิน Uniswap", + "title": "คุณออกจากรายชื่อผู้รอแล้ว!" + }, + "ineligibleExplanation": "คุณยังอยู่ในรายชื่อรอ เราจะแจ้งให้คุณทราบในแอปมือถือ Uniswap เมื่อคุณมีสิทธิ์!", + "unitagPlaceholder": "ชื่อผู้ใช้" + }, "landing": { "button": { "add": "เพิ่มกระเป๋าเงินที่มีอยู่", @@ -814,12 +881,12 @@ "error": "ขออภัย เราไม่สามารถโหลดโค้ด QR ได้ในขณะนี้ โปรดลองวิธีการเริ่มต้นใช้งานวิธีอื่น", "otp": { "error": "รหัสที่คุณส่งไม่ถูกต้องหรือมีข้อผิดพลาดในการส่ง กรุณาลองอีกครั้ง.", - "failed": "ความพยายามล้มเหลว: {{count}}", + "failed": "ความพยายามล้มเหลว: {{number}}", "subtitle": "ตรวจสอบแอปมือถือ Uniswap ของคุณเพื่อดูรหัส 6 ตัวอักษร", "title": "ป้อนรหัสครั้งเดียว" }, - "subtitle": "สแกนโค้ด QR ด้วยแอป Uniswap เพื่อนำเข้ากระเป๋าเงินของคุณ", - "title": "ซิงค์จากโทรศัพท์ของคุณ" + "subtitle": "สแกนโค้ด QR ด้วยแอปมือถือ Uniswap เพื่อเริ่มนำเข้ากระเป๋าเงินของคุณ", + "title": "นำเข้ากระเป๋าเงินจากแอพ" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "สแกนโค้ด QR บนส่วนขยาย Uniswap อีกครั้งเพื่อซิงค์กระเป๋าเงินของคุณต่อไป", "title": "การเชื่อมต่อของคุณหมดเวลา" } + }, + "modal": { + "notOnWaitlist": { + "message": "เพื่อให้มีสิทธิ์ได้รับ Uniswap Extension Beta โปรดเข้าร่วมรายชื่อรอโดยอ้างสิทธิ์ในชื่อผู้ใช้ uni.eth", + "title": "คุณไม่ได้อยู่ในรายชื่อผู้รอ" + }, + "onWaitlist": { + "message": "เราจะแจ้งให้คุณทราบในแอปเมื่อคุณมีสิทธิ์เข้าร่วม Uniswap Extension Beta", + "title": "คุณยังอยู่ในรายชื่อรอ" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "ตรวจสอบการโอน", "send": "ส่ง" }, + "gas": { + "error": { + "title": "ไม่มี" + }, + "networkCost": { + "title": "ต้นทุนเครือข่าย" + } + }, + "input": { + "token": { + "balance": { + "title": "ยอดคงเหลือ: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} โอนก่อนหน้า", + "previous_one": "1 การโอนก่อนหน้า", "previous_other": "{{count}} การโอนก่อนหน้า", "section": { "favorite": "กระเป๋าสตางค์ใบโปรด", "recent": "ล่าสุด", "search": "ผลการค้นหา", + "viewOnly": "กระเป๋าเงินดูอย่างเดียว", "yours": "กระเป๋าเงินของคุณ" + }, + "warning": { + "viewOnly": { + "message": "ส่งเงินไปที่กระเป๋าเงินนี้หากคุณมีวลีกู้คืนหรือรู้จักเจ้าของกระเป๋าเงินเท่านั้น", + "title": "คุณมีสิ่งนี้เป็นกระเป๋าเงินแบบดูอย่างเดียว" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "เมื่อคุณส่งโทเค็นไปยังที่อยู่กระเป๋าเงิน โทเค็นเหล่านั้นจะปรากฏที่นี่", + "title": "ไม่มีกระเป๋าเงินที่บันทึกไว้" + } + }, + "title": "ถึง" + }, "review": { + "modal": { + "title": "คุณกำลังส่ง" + }, "summary": { + "button": { + "title": "ยืนยันการส่ง" + }, "sending": "การส่ง", "to": "ถึง" } @@ -1019,6 +1133,15 @@ "message": "ยอดคงเหลือ {{currencySymbol}} ของคุณลดลงนับตั้งแต่คุณป้อนจำนวนเงินที่คุณต้องการส่ง", "title": "ยังไม่พอ {{currencySymbol}}" }, + "modal": { + "button": { + "cta": { + "blocking": "ตกลง", + "cancel": "ยกเลิก", + "confirm": "ยืนยัน" + } + } + }, "newAddress": { "message": "คุณไม่เคยทำธุรกรรมกับที่อยู่นี้มาก่อน โปรดยืนยันว่าที่อยู่ถูกต้องก่อนดำเนินการต่อ", "title": "ที่อยู่ใหม่" @@ -1110,12 +1233,16 @@ "viewAll": "ดูทั้งหมด", "viewLess": "ดูน้อยลง" }, + "label": { + "viewOnly": "สำหรับดูเท่านั้น" + }, "title": "การตั้งค่ากระเป๋าเงิน" } }, "setting": { "appearance": { "option": { + "auto": "อัตโนมัติ", "dark": { "subtitle": "ใช้โหมดมืดเสมอ", "title": "โหมดมืด" @@ -1177,7 +1304,7 @@ "delete": "ลบข้อมูลสำรอง" }, "complete": "สำรองข้อมูลไปที่ {{cloudProviderName}}", - "description": "ด้วยการสำรองวลีการกู้คืนไว้ที่ {{cloudProviderName}}คุณสามารถกู้คืนกระเป๋าเงินของคุณได้เพียงแค่ลงชื่อเข้าใช้บัญชี {{cloudProviderName}} ของคุณบนอุปกรณ์ใดก็ได้", + "description": "ด้วยการสำรองวลีการกู้คืนไว้ที่ {{cloudProviderName}}คุณสามารถกู้คืนกระเป๋าเงินของคุณได้เพียงแค่ลงชื่อเข้าใช้บัญชี {{cloudProviderName}} บนอุปกรณ์ใดก็ได้", "inProgress": "กำลังสำรองข้อมูลไปที่ {{cloudProviderName}}...", "recoveryPhrase": { "backed": "ได้รับการสนุบสนุน, ช่วยเหลือ" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} สำรองข้อมูล" } }, + "beta": { + "tooltip": "เร็วๆ นี้!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "หากคุณไม่เปิด {{biometricsMethod}}ใครก็ตามที่เข้าถึงอุปกรณ์ของคุณจะสามารถเปิด Uniswap Wallet และทำธุรกรรมได้", - "ios": "หากคุณไม่เปิดไบโอเมตริกซ์ ใครก็ตามที่เข้าถึงอุปกรณ์ของคุณจะสามารถเปิด Uniswap Wallet และทำธุรกรรมได้" + "android": "หากคุณไม่เปิดไบโอเมตริกซ์ ใครก็ตามที่เข้าถึงอุปกรณ์ของคุณจะสามารถเปิด Uniswap Wallet และทำธุรกรรมได้", + "ios": "หากคุณไม่เปิด {{biometricsMethod}}ใครก็ตามที่เข้าถึงอุปกรณ์ของคุณจะสามารถเปิด Uniswap Wallet และทำธุรกรรมได้" }, "title": "คุณแน่ใจไหม?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "สกุลเงินท้องถิ่น" }, + "giveFeedback": { + "title": "ให้ข้อเสนอแนะ" + }, "helpCenter": { "title": "ศูนย์ช่วยเหลือ" }, @@ -1241,6 +1374,7 @@ "navigate": "ไปที่การตั้งค่า" }, "description": "Uniswap มีค่าเริ่มต้นเป็นการตั้งค่าภาษาของอุปกรณ์ของคุณ หากต้องการเปลี่ยนภาษาที่คุณต้องการ ให้ไปที่ “Uniswap” ในการตั้งค่าอุปกรณ์ของคุณ และแตะที่ “ภาษา”", + "exampleTitle": "ภาษาอังกฤษ", "title": "ภาษา" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "ซ่อนยอดคงเหลือขนาดเล็ก" }, + "theme": { + "title": "ธีม" + }, "unknownTokens": { "title": "ซ่อนโทเค็นที่ไม่รู้จัก" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "สูงสุด", + "review": "ทบทวนการแลกเปลี่ยน", "swap": "แลกเปลี่ยน", "unwrap": "แกะ", "view": "ดูธุรกรรม", diff --git a/packages/uniswap/src/i18n/locales/translations/tr-TR.json b/packages/uniswap/src/i18n/locales/translations/tr-TR.json index 5ebb802d84a..9322ea0f20a 100644 --- a/packages/uniswap/src/i18n/locales/translations/tr-TR.json +++ b/packages/uniswap/src/i18n/locales/translations/tr-TR.json @@ -174,6 +174,7 @@ "dismiss": "Azletmek", "done": "Tamamlamak", "enable": "Olanak vermek", + "goBack": "Geri gitmek", "hide": "Saklamak", "later": "Belki sonra", "learn": "Daha fazla bilgi edin", @@ -205,6 +206,7 @@ "title": "Hata! Bir şeyler yanlış gitti." } }, + "endAdornment": "Ve", "error": { "general": "Bir şeyler yanlış gitti." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Cüzdan" + "action": "Onaylamak", + "fallbackTitle": "Harcama belirteçlerini onaylayın", + "helptext": "Bu sitenin bu jetona erişmesine ve cüzdanınızdan harcama yapmasına izin verin.", + "label": "Cüzdan", + "title": "Harcamayı onayla {{tokenSymbol}}" + }, + "base": { + "title": "İşlemi onayla" + }, + "changeChain": { + "title": "Ağı {{network}}olarak değiştir" + }, + "connect": { + "helptext": "Bu sitenin cüzdan adresinizi, bakiyenizi görüntülemesine ve işlemler için onay istemesine izin verin.", + "title": "Siteye bağlanın" }, "error": { "none": "Bekleyen onay yok" }, + "fallback": { + "calldata": { + "label": "veri" + }, + "function": { + "label": "i̇şlev" + }, + "recipient": { + "label": "i̇le" + }, + "sending": { + "label": "gönderiliyor" + } + }, "signature": { - "education": { - "description": "Özel anahtarlarınızı açığa çıkarmadan cüzdanın sahibi olduğunuzu kanıtlamak için imza gereklidir", - "title": "İmza isteği nedir?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent, imzalama için EIP-712 spesifikasyonuna uymayan veriler aldı." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Cüzdanınız bu siteye bağlı değil.", - "popupWithButton": "Cüzdanınız bu siteye bağlı değil. “Cüzdanı Bağla” veya “Oturum Aç” düğmesini arayın." + "popup": "Cüzdanınız bu siteye bağlı değil. “Cüzdanı Bağla” veya “Oturum Aç” düğmesini arayın.", + "popupWithButton": "Cüzdanınız bu siteye bağlı değil.", + "titleConnected": "Bağlı", + "titleNotConnected": "Bağlı değil" + }, + "dappRequest": { + "signatureRequest": { + "header": "İmza isteği" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Sizi {{serviceProvider}}bağlıyorum", - "quote": "{{currencySymbol}}değerinde {{amount}} satın almak" + "quote": "{{currencySymbol}}değerinde {{amount}} satın almak", + "terms": "Devam ederek, uygun olduğu şekilde {{serviceProvider}}ile Hizmet Şartları ve Gizlilik Politikasına tabi olacağınızı kabul etmiş olursunuz." }, "error": { "default": "Bir şeyler yanlış gitti.", @@ -427,10 +464,9 @@ "usd": "Yalnızca USD cinsinden satın alınabilir" }, "quote": { - "amount": "{{tokenAmount}}al", - "amountAfterFees": "{{tokenAmount}} ücretlerden sonra", + "advice": "İşleminizle ilişkili ücretleri görmek için sağlayıcının portalına devam edeceksiniz.", + "others": "diğerleri", "type": { - "best": "Genel olarak en iyi", "other": "Diğer seçenekler", "recent": "Son zamanlarda kullanılmış" } @@ -470,11 +506,16 @@ "title": "Aktivite" }, "banner": { + "extension": { + "confirm": "Beta'ya Katılın", + "message": "Uniswap Uzantısını web tarayıcınızda deneyen ilk siz olun", + "title": "Uniswap Uzantısı burada" + }, "offline": "Çevrimdışı moddasınız" }, "extension": { "error": "Hesaplar yüklenirken hata oluştu", - "pin": "simgesine tıklayarak Uniswap Cüzdanı tarayıcı araç çubuğunuza sabitleyin" + "pin": "simgesine tıklayarak Uniswap Cüzdanını tarayıcı araç çubuğunuza sabitleyin" }, "feed": { "empty": { @@ -491,6 +532,14 @@ "send": "Göndermek", "swap": "Takas" }, + "modal": { + "getExtension": { + "step1": "1. Bilgisayarınızda uniswap.org/ext adresini ziyaret edin", + "step2": "2. Uniswap Uzantısını Chrome tarayıcınıza ekleyin", + "step3": "3. Erişim sağlamak için kullanıcı adınızı girin", + "title": "Uniswap Uzantısı Beta'ya katılın" + } + }, "nfts": { "title": "NFT'ler" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Adres kopyalandı", + "calldata": "Arama verileri kopyalandı", "contractAddress": "Sözleşme adresi kopyalandı", "failed": "Panoya kopyalanamadı", "image": "Resim kopyalandı", @@ -715,6 +765,10 @@ "button": "Telefonunuzdan içe aktarın", "title": "Uniswap mobil uygulamasına sahip misiniz?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Mobil uygulamayı indirin ve bir kullanıcı adı talep edin", + "title": "Beta bekleme listesine girin" + }, "password": { "subtitle": "Cüzdanınızın kilidini açmak ve kurtarma ifadenize erişmek için buna ihtiyacınız olacak", "title": { @@ -764,6 +818,19 @@ }, "title": "\nUniswap Cüzdan'a hoş geldiniz" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Uygunluğu kontrol edin", + "letsGo": "Hadi gidelim" + }, + "checkEligibilityInstructions": "Beta için uygun olup olmadığınızı kontrol etmek için aşağıya uni.eth kullanıcı adınızı girin.", + "eligible": { + "tagline": "Beta'ya hoş geldiniz; Uniswap cüzdanını ilk deneyenlerden birisiniz.", + "title": "Bekleme listesinden çıktınız!" + }, + "ineligibleExplanation": "Hala bekleme listesindesin. Uygun olduğunuzda sizi Uniswap mobil uygulamasında bilgilendireceğiz!", + "unitagPlaceholder": "Kullanıcı adı" + }, "landing": { "button": { "add": "Mevcut bir cüzdanı ekleyin", @@ -814,12 +881,12 @@ "error": "Üzgünüz, şu anda QR kodunu yükleyemiyoruz. Lütfen başka bir katılım yöntemini deneyin.", "otp": { "error": "Gönderdiğiniz kod hatalı veya gönderilirken bir hata oluştu. Lütfen tekrar deneyin.", - "failed": "Başarısız denemeler: {{count}}", + "failed": "Başarısız denemeler: {{number}}", "subtitle": "6 karakterli kod için Uniswap mobil uygulamanızı kontrol edin", "title": "Tek kullanımlık kodu girin" }, - "subtitle": "Cüzdanınızı içe aktarmak için QR kodunu Uniswap uygulamasıyla tarayın", - "title": "Telefonunuzdan senkronize edin" + "subtitle": "Cüzdanlarınızı içe aktarmaya başlamak için QR kodunu Uniswap mobil uygulamasıyla tarayın", + "title": "Uygulamadan cüzdanı içe aktar" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Cüzdanınızı senkronize etmeye devam etmek için Uniswap Uzantısındaki QR kodunu tekrar tarayın.", "title": "Bağlantınız zaman aşımına uğradı" } + }, + "modal": { + "notOnWaitlist": { + "message": "Uniswap Extension Beta'ya hak kazanabilmek için uni.eth kullanıcı adını talep ederek bekleme listesine katılın", + "title": "Bekleme listesinde değilsiniz" + }, + "onWaitlist": { + "message": "Uniswap Extension Beta'ya katılmaya uygun olduğunuzda sizi uygulamada bilgilendireceğiz.", + "title": "Hala bekleme listesindesin" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Aktarımı inceleyin", "send": "Göndermek" }, + "gas": { + "error": { + "title": "Yok" + }, + "networkCost": { + "title": "Ağ Maliyeti" + } + }, + "input": { + "token": { + "balance": { + "title": "Bakiye: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} önceki transfer", + "previous_one": "1 önceki transfer", "previous_other": "{{count}} önceki transferler", "section": { "favorite": "Favori cüzdanlar", "recent": "Son", "search": "Arama Sonuçları", + "viewOnly": "Salt görüntülenebilen cüzdanlar", "yours": "Cüzdanlarınız" + }, + "warning": { + "viewOnly": { + "message": "Bu cüzdana yalnızca kurtarma ifadesine sahipseniz veya cüzdanın sahibini tanıyorsanız para gönderin.", + "title": "Bunu salt görüntülenebilir bir cüzdan olarak kullanıyorsunuz" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Tokenları bir cüzdan adresine gönderdiğinizde burada görünürler", + "title": "Kaydedilmiş cüzdan yok" + } + }, + "title": "İle" + }, "review": { + "modal": { + "title": "gönderiyorsun" + }, "summary": { + "button": { + "title": "Göndermeyi onayla" + }, "sending": "Gönderiliyor", "to": "İle" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Göndermek istediğiniz tutarı girdiğiniz andan itibaren {{currencySymbol}} bakiyeniz azaldı", - "title": "Yeterli değil {{currencySymbol}}." + "title": "Yeterli değil {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "TAMAM", + "cancel": "İptal etmek", + "confirm": "Onaylamak" + } + } }, "newAddress": { "message": "Bu adresle daha önce işlem yapmadınız. Lütfen devam etmeden önce adresin doğru olduğunu onaylayın.", @@ -1110,12 +1233,16 @@ "viewAll": "Hepsini gör", "viewLess": "Daha az görüntüle" }, + "label": { + "viewOnly": "Sadece Görüntüle" + }, "title": "Cüzdan ayarları" } }, "setting": { "appearance": { "option": { + "auto": "Oto", "dark": { "subtitle": "Her zaman karanlık modu kullan", "title": "Karanlık mod" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} yedekleme" } }, + "beta": { + "tooltip": "Yakında gelecek!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "{{biometricsMethod}}açmazsanız cihazınıza erişim sağlayan herkes Uniswap Cüzdan'ı açabilir ve işlem yapabilir.", - "ios": "Biyometriyi açmazsanız cihazınıza erişim sağlayan herkes Uniswap Cüzdan'ı açabilir ve işlem yapabilir." + "android": "Biyometriyi açmazsanız cihazınıza erişim sağlayan herkes Uniswap Cüzdan'ı açabilir ve işlem yapabilir.", + "ios": "{{biometricsMethod}}açmazsanız cihazınıza erişim sağlayan herkes Uniswap Cüzdan'ı açabilir ve işlem yapabilir." }, "title": "Emin misin?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Yerel para birimi" }, + "giveFeedback": { + "title": "Geri bildirimde bulunun" + }, "helpCenter": { "title": "Yardım Merkezi" }, @@ -1241,6 +1374,7 @@ "navigate": "Ayarlara git" }, "description": "Uniswap varsayılan olarak cihazınızın dil ayarlarını kullanır. Tercih ettiğiniz dili değiştirmek için cihaz ayarlarınızda \"Uniswap\"e gidin ve \"Dil\"e dokunun", + "exampleTitle": "İngilizce", "title": "Dil" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Küçük bakiyeleri gizle" }, + "theme": { + "title": "Tema" + }, "unknownTokens": { "title": "Bilinmeyen belirteçleri gizle" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Maksimum", + "review": "Takası inceleyin", "swap": "Takas", "unwrap": "Paketi aç", "view": "İşlemi görüntüle", diff --git a/packages/uniswap/src/i18n/locales/translations/uk-UA.json b/packages/uniswap/src/i18n/locales/translations/uk-UA.json index 428f65009a4..5a871374ffc 100644 --- a/packages/uniswap/src/i18n/locales/translations/uk-UA.json +++ b/packages/uniswap/src/i18n/locales/translations/uk-UA.json @@ -63,7 +63,7 @@ "part3": "Але якщо ви втратите фразу відновлення, ви втратите доступ до свого гаманця.", "part4": "Замість того, щоб запам’ятовувати фразу відновлення, ви можете створити її резервну копію на {{cloudProviderName}} і захистити паролем.", "part5": "Ви також можете вручну створити резервну копію фрази відновлення, записавши її і зберігши в безпечному місці.", - "part6": "Ми рекомендуємо використовувати обидва типи резервних копій, оскільки, якщо ви втратите фразу відновлення, ви не зможете відновити свій гаманець." + "part6": "Ми рекомендуємо використовувати обидва типи резервних копій, тому що якщо ви втратите фразу відновлення, ви не зможете відновити свій гаманець." }, "error": { "invalid": "Недійсна фраза", @@ -174,6 +174,7 @@ "dismiss": "Відхилити", "done": "Готово", "enable": "Увімкнути", + "goBack": "Повертайся", "hide": "Сховати", "later": "Можливо пізніше", "learn": "Вивчайте більше", @@ -205,6 +206,7 @@ "title": "Ой! Щось пішло не так." } }, + "endAdornment": "і", "error": { "general": "Щось пішло не так." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Гаманець" + "action": "Затвердити", + "fallbackTitle": "Затвердити маркери витрат", + "helptext": "Дозвольте цьому сайту отримати доступ і витратити цей маркер із вашого гаманця.", + "label": "Гаманець", + "title": "Затвердити витрати {{tokenSymbol}}" + }, + "base": { + "title": "Підтвердити транзакцію" + }, + "changeChain": { + "title": "Перемкніть мережу на {{network}}" + }, + "connect": { + "helptext": "Дозвольте цьому сайту переглядати адресу вашого гаманця, баланс і запитувати схвалення транзакцій.", + "title": "Підключіться до сайту" }, "error": { "none": "Схвалення не очікуються" }, + "fallback": { + "calldata": { + "label": "даних" + }, + "function": { + "label": "функція" + }, + "recipient": { + "label": "до" + }, + "sending": { + "label": "відправка" + } + }, "signature": { - "education": { - "description": "Підпис потрібен, щоб підтвердити, що ви володієте гаманцем, не розкриваючи свої особисті ключі", - "title": "Що таке запит на підпис?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent отримав дані для підписання, які не відповідають специфікації EIP-712." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Ваш гаманець не підключений до цього сайту.", - "popupWithButton": "Ваш гаманець не підключений до цього сайту. Знайдіть кнопку «Підключити гаманець» або «Увійти»." + "popup": "Ваш гаманець не підключений до цього сайту. Знайдіть кнопку «Підключити гаманець» або «Увійти».", + "popupWithButton": "Ваш гаманець не підключений до цього сайту.", + "titleConnected": "Підключено", + "titleNotConnected": "Не з'єднано" + }, + "dappRequest": { + "signatureRequest": { + "header": "Запит на підпис" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Підключення до {{serviceProvider}}", - "quote": "Купівля {{amount}} коштує {{currencySymbol}}" + "quote": "Купівля {{amount}} коштує {{currencySymbol}}", + "terms": "Продовжуючи, ви підтверджуєте, що на вас поширюватимуться Загальні положення та умови та Політика конфіденційності з {{serviceProvider}}, залежно від обставин." }, "error": { "default": "Щось пішло не так.", @@ -427,10 +464,9 @@ "usd": "Доступно лише для покупки в доларах США" }, "quote": { - "amount": "Отримайте {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} після комісії", + "advice": "Ви перейдете на портал постачальника, щоб побачити комісії, пов’язані з вашою транзакцією.", + "others": "інші", "type": { - "best": "Найкраще в цілому", "other": "Інші варіанти", "recent": "Нещодавно використаний" } @@ -470,11 +506,16 @@ "title": "діяльність" }, "banner": { + "extension": { + "confirm": "Приєднатися до бета-версії", + "message": "Будьте першим, хто спробує розширення Uniswap у своєму веб-переглядачі", + "title": "Розширення Uniswap тут" + }, "offline": "Ви в автономному режимі" }, "extension": { "error": "Помилка завантаження облікових записів", - "pin": "Закріпіть Uniswap Wallet на панелі інструментів браузера, натиснувши на " + "pin": "Закріпіть гаманець Uniswap на панелі інструментів браузера, натиснувши " }, "feed": { "empty": { @@ -491,6 +532,14 @@ "send": "Надіслати", "swap": "Обмін" }, + "modal": { + "getExtension": { + "step1": "1. Відвідайте uniswap.org/ext на своєму комп’ютері", + "step2": "2. Додайте розширення Uniswap у свій браузер Chrome", + "step3": "3. Введіть своє ім'я користувача, щоб отримати доступ", + "title": "Приєднуйтесь до бета-версії розширення Uniswap" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Адресу скопійовано", + "calldata": "Дані виклику скопійовано", "contractAddress": "Адресу договору скопійовано", "failed": "Не вдалося скопіювати в буфер обміну", "image": "Зображення скопійовано", @@ -715,6 +765,10 @@ "button": "Імпортуйте з телефону", "title": "Маєте мобільний додаток Uniswap?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Завантажте мобільний додаток і вимагайте ім’я користувача", + "title": "Запишіться в список очікування бета-версії" + }, "password": { "subtitle": "Це знадобиться, щоб розблокувати гаманець і отримати доступ до фрази відновлення", "title": { @@ -764,6 +818,19 @@ }, "title": "Ласкаво просимо до \nUniswap Wallet" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Перевірте відповідність вимогам", + "letsGo": "Ходімо" + }, + "checkEligibilityInstructions": "Введіть нижче своє ім’я користувача uni.eth , щоб перевірити, чи маєте ви право на бета-версію.", + "eligible": { + "tagline": "Ласкаво просимо до бета-версії — ви один із перших, хто спробував гаманець Uniswap.", + "title": "Ви виключені зі списку очікування!" + }, + "ineligibleExplanation": "Ви все ще в списку очікування. Ми повідомимо вас у мобільному додатку Uniswap, коли ви отримаєте право!", + "unitagPlaceholder": "ім'я користувача" + }, "landing": { "button": { "add": "Додайте наявний гаманець", @@ -814,12 +881,12 @@ "error": "На жаль, зараз ми не можемо завантажити QR-код. Спробуйте інший метод реєстрації.", "otp": { "error": "Код, який ви надіслали, неправильний, або під час надсилання сталася помилка. Будь ласка спробуйте ще раз.", - "failed": "Невдалі спроби: {{count}}", + "failed": "Невдалі спроби: {{number}}", "subtitle": "Перевірте свій 6-значний код у мобільному додатку Uniswap", "title": "Введіть одноразовий код" }, - "subtitle": "Відскануйте QR-код за допомогою програми Uniswap, щоб імпортувати свій гаманець", - "title": "Синхронізація з телефону" + "subtitle": "Відскануйте QR-код за допомогою мобільного додатка Uniswap, щоб розпочати імпорт ваших гаманців.", + "title": "Імпорт гаманця з програми" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Ще раз відскануйте QR-код розширення Uniswap, щоб продовжити синхронізацію вашого гаманця.", "title": "Час очікування підключення минув" } + }, + "modal": { + "notOnWaitlist": { + "message": "Щоб отримати право на бета-версію розширення Uniswap, приєднайтеся до списку очікування, отримавши ім’я користувача uni.eth", + "title": "Ви не в списку очікування" + }, + "onWaitlist": { + "message": "Ми повідомимо вас у додатку, коли ви отримаєте право приєднатися до бета-версії розширення Uniswap.", + "title": "Ви все ще в списку очікування" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Огляд передачі", "send": "Надіслати" }, + "gas": { + "error": { + "title": "N/A" + }, + "networkCost": { + "title": "Вартість мережі" + } + }, + "input": { + "token": { + "balance": { + "title": "Баланс: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} попередня передача", + "previous_one": "1 попередня передача", "previous_other": "{{count}} попередні перекази", "section": { "favorite": "Улюблені гаманці", "recent": "Останні", "search": "Результати пошуку", + "viewOnly": "Гаманці лише для перегляду", "yours": "Ваші гаманці" + }, + "warning": { + "viewOnly": { + "message": "Надсилайте кошти на цей гаманець, лише якщо у вас є фраза для відновлення або ви знаєте власника гаманця.", + "title": "Ви маєте це як гаманець лише для перегляду" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Коли ви надсилаєте токени на адресу гаманця, вони відображатимуться тут", + "title": "Гаманці не збережено" + } + }, + "title": "до" + }, "review": { + "modal": { + "title": "Ви надсилаєте" + }, "summary": { + "button": { + "title": "Підтвердити відправку" + }, "sending": "Відправка", "to": "до" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Ваш баланс на {{currencySymbol}} зменшився, оскільки ви ввели суму, яку хочете надіслати", - "title": "Недостатньо {{currencySymbol}}." + "title": "Недостатньо {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "в порядку", + "cancel": "Скасувати", + "confirm": "Підтвердити" + } + } }, "newAddress": { "message": "Ви раніше не здійснювали операції з цією адресою. Перш ніж продовжити, переконайтеся, що адреса правильна.", @@ -1110,12 +1233,16 @@ "viewAll": "Подивитись все", "viewLess": "Переглянути менше" }, + "label": { + "viewOnly": "Лише перегляд" + }, "title": "Налаштування гаманця" } }, "setting": { "appearance": { "option": { + "auto": "Авто", "dark": { "subtitle": "Завжди використовуйте темний режим", "title": "Темний режим" @@ -1145,7 +1272,7 @@ }, "error": { "message": { - "full": "Не вдається створити резервну копію фрази відновлення на {{cloudProviderName}}. Будь ласка, переконайтеся, що {{cloudProviderName}} увімкнено з доступним місцем для зберігання, і повторіть спробу.", + "full": "Неможливо створити резервну копію фрази відновлення на {{cloudProviderName}}. Будь ласка, переконайтеся, що {{cloudProviderName}} увімкнено з доступним місцем для зберігання, і повторіть спробу.", "short": "Не вдалося видалити резервну копію" }, "title": "{{cloudProviderName}} помилка" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} резервна копія" } }, + "beta": { + "tooltip": "Незабаром!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Якщо ви не ввімкнете {{biometricsMethod}}, кожен, хто отримає доступ до вашого пристрою, зможе відкрити Uniswap Wallet і здійснювати транзакції.", - "ios": "Якщо ви не ввімкнете біометрію, кожен, хто отримає доступ до вашого пристрою, зможе відкрити Uniswap Wallet і здійснювати транзакції." + "android": "Якщо ви не ввімкнете біометрію, кожен, хто отримає доступ до вашого пристрою, зможе відкрити Uniswap Wallet і здійснювати транзакції.", + "ios": "Якщо ви не ввімкнете {{biometricsMethod}}, кожен, хто отримає доступ до вашого пристрою, зможе відкрити Uniswap Wallet і здійснювати транзакції." }, "title": "Ти впевнений?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Місцева валюта" }, + "giveFeedback": { + "title": "Дайте відгук" + }, "helpCenter": { "title": "Центр допомоги" }, @@ -1241,6 +1374,7 @@ "navigate": "Перейдіть до налаштувань" }, "description": "Uniswap за замовчуванням використовує мовні налаштування вашого пристрою. Щоб змінити бажану мову, перейдіть до «Uniswap» у налаштуваннях пристрою та натисніть «Language»", + "exampleTitle": "англійська", "title": "Мова" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Приховайте невеликі залишки" }, + "theme": { + "title": "Тема" + }, "unknownTokens": { "title": "Приховати невідомі маркери" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Макс", + "review": "Огляд обміну", "swap": "Обмін", "unwrap": "Розгорніть", "view": "Переглянути транзакцію", @@ -1756,7 +1894,7 @@ }, "subtitle": "Створіть персоналізований профіль web3 і легко діліться своєю адресою з друзями.", "title": { - "compact": "Отримайте своє {{unitagDomain}} ім’я користувача та створіть свій настроюваний профіль.", + "compact": "Отримайте своє {{unitagDomain}} ім’я користувача та створіть свій налаштовуваний профіль.", "full": "Отримайте ім'я користувача {{unitagDomain}}" } }, diff --git a/packages/uniswap/src/i18n/locales/translations/ur-PK.json b/packages/uniswap/src/i18n/locales/translations/ur-PK.json index c3af212db24..58425e29a9d 100644 --- a/packages/uniswap/src/i18n/locales/translations/ur-PK.json +++ b/packages/uniswap/src/i18n/locales/translations/ur-PK.json @@ -89,7 +89,7 @@ "title": "آپ {{walletName}}کو ہٹا رہے ہیں" }, "mnemonic": { - "description": "یہ {{walletNames}}کے طور پر ایک ہی بازیابی کے جملہ کا اشتراک کرتا ہے۔ آپ کا بازیابی کا جملہ اس وقت تک محفوظ رہے گا جب تک آپ باقی تمام بٹوے حذف نہیں کر دیتے۔" + "description": "یہ {{walletNames}}کے طور پر ایک ہی بازیابی کے جملے کا اشتراک کرتا ہے۔ آپ کا بازیابی کا جملہ اس وقت تک محفوظ رہے گا جب تک آپ باقی تمام بٹوے حذف نہیں کر دیتے۔" } }, "subtitle": { @@ -174,6 +174,7 @@ "dismiss": "برطرف کرنا", "done": "ہو گیا", "enable": "فعال", + "goBack": "واپس جاو", "hide": "چھپائیں", "later": "شاید بعد میں", "learn": "اورجانیے", @@ -205,6 +206,7 @@ "title": "افوہ! کچھ غلط ہو گیا." } }, + "endAdornment": "اور", "error": { "general": "کچھ غلط ہو گیا." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "پرس" + "action": "منظور کرو", + "fallbackTitle": "اخراجات کے ٹوکن کو منظور کریں۔", + "helptext": "اس سائٹ کو اپنے بٹوے سے اس ٹوکن تک رسائی اور خرچ کرنے کی اجازت دیں۔", + "label": "پرس", + "title": "{{tokenSymbol}}خرچ کی منظوری دیں۔" + }, + "base": { + "title": "لین دین کی تصدیق کریں۔" + }, + "changeChain": { + "title": "نیٹ ورک کو {{network}}پر سوئچ کریں۔" + }, + "connect": { + "helptext": "اس سائٹ کو آپ کے بٹوے کا پتہ، بیلنس دیکھنے اور لین دین کے لیے منظوری کی درخواست کرنے کی اجازت دیں۔", + "title": "سائٹ سے جڑیں۔" }, "error": { "none": "کوئی منظوری زیر التوا نہیں ہے۔" }, + "fallback": { + "calldata": { + "label": "ڈیٹا" + }, + "function": { + "label": "فنکشن" + }, + "recipient": { + "label": "کو" + }, + "sending": { + "label": "بھیج رہا ہے۔" + } + }, "signature": { - "education": { - "description": "یہ ثابت کرنے کے لیے ایک دستخط کی ضرورت ہے کہ آپ پرس کے مالک ہیں اپنی نجی چابیاں ظاہر کیے بغیر", - "title": "دستخط کی درخواست کیا ہے؟" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent کو دستخط کرنے کے لیے ڈیٹا موصول ہوا جو EIP-712 کے مطابق نہیں ہے۔" } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "آپ کا بٹوہ اس سائٹ سے منسلک نہیں ہے۔", - "popupWithButton": "آپ کا بٹوہ اس سائٹ سے منسلک نہیں ہے۔ ایک \"کنیکٹ والیٹ\" یا \"لاگ ان\" بٹن تلاش کریں۔" + "popup": "آپ کا بٹوہ اس سائٹ سے منسلک نہیں ہے۔ ایک \"کنیکٹ والیٹ\" یا \"لاگ ان\" بٹن تلاش کریں۔", + "popupWithButton": "آپ کا بٹوہ اس سائٹ سے منسلک نہیں ہے۔", + "titleConnected": "جڑا ہوا", + "titleNotConnected": "منسلک نہیں" + }, + "dappRequest": { + "signatureRequest": { + "header": "دستخط کی درخواست" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "آپ کو {{serviceProvider}}سے جوڑ رہا ہے۔", - "quote": "{{currencySymbol}}کی قیمت {{amount}} خریدنا" + "quote": "{{currencySymbol}}کی قیمت {{amount}} خریدنا", + "terms": "جاری رکھ کر، آپ تسلیم کرتے ہیں کہ آپ {{serviceProvider}}کے ساتھ سروس کی شرائط اور رازداری کی پالیسی کے تابع ہوں گے، جیسا کہ قابل اطلاق ہے۔" }, "error": { "default": "کچھ غلط ہو گیا.", @@ -427,10 +464,9 @@ "usd": "صرف USD میں خریدنے کے لیے دستیاب ہے۔" }, "quote": { - "amount": "{{tokenAmount}}وصول کریں۔", - "amountAfterFees": "فیس کے بعد {{tokenAmount}}", + "advice": "آپ اپنے لین دین سے وابستہ فیس دیکھنے کے لیے فراہم کنندہ کے پورٹل پر جائیں گے۔", + "others": "دوسرے", "type": { - "best": "مجموعی طور پر بہترین", "other": "دوسرے اختیارات", "recent": "حال ہی میں استعمال کیا گیا۔" } @@ -470,6 +506,11 @@ "title": "سرگرمی" }, "banner": { + "extension": { + "confirm": "بیٹا میں شامل ہوں۔", + "message": "اپنے ویب براؤزر پر Uniswap ایکسٹینشن کو آزمانے والے پہلے فرد بنیں۔", + "title": "Uniswap ایکسٹینشن یہاں ہے۔" + }, "offline": "آپ آف لائن موڈ میں ہیں۔" }, "extension": { @@ -491,6 +532,14 @@ "send": "بھیجیں", "swap": "تبادلہ" }, + "modal": { + "getExtension": { + "step1": "1. اپنے کمپیوٹر پر uniswap.org/ext ملاحظہ کریں", + "step2": "2. اپنے کروم براؤزر پر یونی سویپ ایکسٹینشن شامل کریں۔", + "step3": "3. رسائی حاصل کرنے کے لیے اپنا صارف نام درج کریں۔", + "title": "یونی سویپ ایکسٹینشن بیٹا میں شامل ہوں۔" + } + }, "nfts": { "title": "NFTs" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "پتہ کاپی ہو گیا۔", + "calldata": "کالڈیٹا کاپی ہو گیا۔", "contractAddress": "کنٹریکٹ ایڈریس کاپی ہو گیا۔", "failed": "کلپ بورڈ پر کاپی کرنے میں ناکام", "image": "تصویر کاپی ہو گئی۔", @@ -715,6 +765,10 @@ "button": "اپنے فون سے درآمد کریں۔", "title": "Unswap موبائل ایپ ہے؟" }, + "getOnTheBetaWaitlist": { + "subtitle": "موبائل ایپ ڈاؤن لوڈ کریں اور صارف نام کا دعوی کریں۔", + "title": "بیٹا انتظار کی فہرست میں شامل ہوں۔" + }, "password": { "subtitle": "آپ کو اپنے بٹوے کو غیر مقفل کرنے اور اپنے بازیابی کے جملہ تک رسائی حاصل کرنے کے لیے اس کی ضرورت ہوگی۔", "title": { @@ -764,6 +818,19 @@ }, "title": "\nUnswap Wallet میں خوش آمدید" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "اہلیت چیک کریں۔", + "letsGo": "چلو" + }, + "checkEligibilityInstructions": "ذیل میں اپنا uni.eth صارف نام درج کریں تاکہ یہ معلوم ہو سکے کہ آیا آپ بیٹا کے لیے اہل ہیں۔", + "eligible": { + "tagline": "بیٹا میں خوش آمدید — آپ Unswap والیٹ کو آزمانے والے پہلے لوگوں میں سے ایک ہیں۔", + "title": "آپ انتظار کی فہرست سے باہر ہیں!" + }, + "ineligibleExplanation": "آپ ابھی بھی انتظار کی فہرست میں ہیں۔ جب آپ اہل ہو جائیں گے تو ہم آپ کو Uniswap موبائل ایپ میں مطلع کریں گے!", + "unitagPlaceholder": "صارف نام" + }, "landing": { "button": { "add": "ایک موجودہ پرس شامل کریں۔", @@ -814,12 +881,12 @@ "error": "معذرت، ہم ابھی QR کوڈ لوڈ کرنے سے قاصر ہیں۔ براہ کرم آن بورڈنگ کا دوسرا طریقہ آزمائیں۔", "otp": { "error": "آپ کا جمع کردہ کوڈ غلط ہے، یا جمع کرانے میں ایک خامی تھی۔ دوبارہ کوشش کریں.", - "failed": "ناکام کوششیں: {{count}}", + "failed": "ناکام کوششیں: {{number}}", "subtitle": "6-حروف کے کوڈ کے لیے اپنا یونیسیاپ موبائل ایپ چیک کریں۔", "title": "ایک بار کا کوڈ درج کریں۔" }, - "subtitle": "اپنے بٹوے کو درآمد کرنے کے لیے یونی سویپ ایپ سے QR کوڈ اسکین کریں۔", - "title": "اپنے فون سے مطابقت پذیری کریں۔" + "subtitle": "اپنے بٹوے کو درآمد کرنا شروع کرنے کے لیے یونی سویپ موبائل ایپ کے ساتھ QR کوڈ اسکین کریں۔", + "title": "ایپ سے پرس درآمد کریں۔" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "اپنے بٹوے کی مطابقت پذیری جاری رکھنے کے لیے یونی سویپ ایکسٹینشن پر QR کوڈ دوبارہ اسکین کریں۔", "title": "آپ کے کنکشن کا وقت ختم ہو گیا۔" } + }, + "modal": { + "notOnWaitlist": { + "message": "Uniswap ایکسٹینشن بیٹا کے اہل ہونے کے لیے، uni.eth صارف نام کا دعوی کرتے ہوئے انتظار کی فہرست میں شامل ہوں۔", + "title": "آپ انتظار کی فہرست میں نہیں ہیں۔" + }, + "onWaitlist": { + "message": "جب آپ Unswap Extension Beta میں شامل ہونے کے اہل ہو جائیں گے تو ہم آپ کو ایپ میں مطلع کریں گے۔", + "title": "آپ ابھی بھی انتظار کی فہرست میں ہیں۔" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "منتقلی کا جائزہ لیں۔", "send": "بھیجیں" }, + "gas": { + "error": { + "title": "N / A" + }, + "networkCost": { + "title": "نیٹ ورک لاگت" + } + }, + "input": { + "token": { + "balance": { + "title": "بیلنس: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} پچھلی منتقلی۔", + "previous_one": "1 پچھلی منتقلی۔", "previous_other": "{{count}} پچھلی منتقلیاں", "section": { "favorite": "پسندیدہ بٹوے", "recent": "حالیہ", "search": "تلاش کے نتائج", + "viewOnly": "صرف دیکھنے کے بٹوے", "yours": "آپ کے بٹوے" + }, + "warning": { + "viewOnly": { + "message": "صرف اس بٹوے میں فنڈز بھیجیں اگر آپ کے پاس بازیابی کا جملہ ہے یا آپ کے پاس بٹوے کے مالک کو جانتے ہیں۔", + "title": "آپ کے پاس یہ صرف دیکھنے کے لیے والیٹ کے طور پر ہے۔" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "جب آپ بٹوے کے پتے پر ٹوکن بھیجتے ہیں، تو وہ یہاں نظر آئیں گے۔", + "title": "کوئی بٹوہ محفوظ نہیں ہوا۔" + } + }, + "title": "کو" + }, "review": { + "modal": { + "title": "آپ بھیج رہے ہیں۔" + }, "summary": { + "button": { + "title": "بھیجنے کی تصدیق کریں۔" + }, "sending": "بھیج رہا ہے۔", "to": "کو" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "آپ کے {{currencySymbol}} بیلنس میں کمی آئی ہے جب سے آپ نے وہ رقم داخل کی ہے جو آپ بھیجنا چاہتے ہیں۔", - "title": "کافی نہیں {{currencySymbol}}." + "title": "کافی نہیں {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "ٹھیک ہے", + "cancel": "منسوخ کریں۔", + "confirm": "تصدیق کریں۔" + } + } }, "newAddress": { "message": "آپ نے پہلے اس ایڈریس سے لین دین نہیں کیا ہے۔ جاری رکھنے سے پہلے براہ کرم تصدیق کریں کہ پتہ درست ہے۔", @@ -1110,12 +1233,16 @@ "viewAll": "سب دیکھیں", "viewLess": "کم دیکھیں" }, + "label": { + "viewOnly": "صرف دیکھنے کے لیے" + }, "title": "والیٹ کی ترتیبات" } }, "setting": { "appearance": { "option": { + "auto": "آٹو", "dark": { "subtitle": "ہمیشہ ڈارک موڈ استعمال کریں۔", "title": "ڈارک موڈ" @@ -1133,7 +1260,7 @@ }, "backup": { "create": { - "description": "پاس ورڈ ترتیب دینے سے آپ کی بازیابی کے فقرے کا بیک اپ انکرپٹ ہو جائے گا، اگر آپ کے {{cloudProviderName}} اکاؤنٹ سے کبھی سمجھوتہ کیا جاتا ہے تو تحفظ کی ایک اضافی سطح کا اضافہ ہو گا۔", + "description": "پاس ورڈ سیٹ کرنے سے آپ کی بازیابی کے فقرے کا بیک اپ انکرپٹ ہو جائے گا، اگر آپ کے {{cloudProviderName}} اکاؤنٹ سے کبھی سمجھوتہ کیا جاتا ہے تو تحفظ کی ایک اضافی سطح کا اضافہ ہو گا۔", "title": "{{cloudProviderName}}تک بیک اپ کریں۔" }, "delete": { @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} بیک اپ" } }, + "beta": { + "tooltip": "جلد آ رہا ہے!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "اگر آپ {{biometricsMethod}}کو آن نہیں کرتے ہیں، تو کوئی بھی جو آپ کے آلے تک رسائی حاصل کرتا ہے وہ Unswap Wallet کھول سکتا ہے اور لین دین کرسکتا ہے۔", - "ios": "اگر آپ بایومیٹرکس کو آن نہیں کرتے ہیں، تو کوئی بھی جو آپ کے آلے تک رسائی حاصل کرتا ہے وہ Unswap Wallet کھول سکتا ہے اور لین دین کرسکتا ہے۔" + "android": "اگر آپ بایومیٹرکس کو آن نہیں کرتے ہیں، تو کوئی بھی جو آپ کے آلے تک رسائی حاصل کرتا ہے وہ Unswap Wallet کھول سکتا ہے اور لین دین کرسکتا ہے۔", + "ios": "اگر آپ {{biometricsMethod}}کو آن نہیں کرتے ہیں، تو کوئی بھی جو آپ کے آلے تک رسائی حاصل کرتا ہے وہ Unswap Wallet کھول سکتا ہے اور لین دین کرسکتا ہے۔" }, "title": "کیا تمہیں یقین ہے؟" } @@ -1233,6 +1363,9 @@ "currency": { "title": "مقامی کرنسی" }, + "giveFeedback": { + "title": "رائے دیں۔" + }, "helpCenter": { "title": "مدداور تعاون کا مرکز" }, @@ -1241,6 +1374,7 @@ "navigate": "ترتیبات پر جائیں۔" }, "description": "اپنے آلے کی لینگویج سیٹنگز میں ڈیفالٹس کو تبدیل کریں۔ اپنی ترجیحی زبان کو تبدیل کرنے کے لیے، اپنے آلے کی ترتیبات میں \"Uniswap\" پر جائیں اور \"Language\" پر ٹیپ کریں۔", + "exampleTitle": "انگریزی", "title": "زبان" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "چھوٹے بیلنس چھپائیں۔" }, + "theme": { + "title": "خیالیہ" + }, "unknownTokens": { "title": "نامعلوم ٹوکنز چھپائیں۔" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "زیادہ سے زیادہ", + "review": "تبادلہ کا جائزہ لیں۔", "swap": "تبادلہ", "unwrap": "کھولنا", "view": "لین دین دیکھیں", @@ -1398,7 +1536,7 @@ "button": "کافی نہیں {{currencySymbol}}", "cta": { "button": "{{currencySymbol}}خریدیں۔", - "message": "آپ کو مزید {{currencySymbol}} اس لین دین کی نیٹ ورک لاگت کو پورا کرنے کی ضرورت ہے۔" + "message": "اس لین دین کے لیے نیٹ ورک کی لاگت کو پورا کرنے کے لیے آپ کو مزید {{currencySymbol}} کی ضرورت ہے۔" }, "title": "آپ کے پاس نیٹ ورک کی لاگت کو پورا کرنے کے لیے کافی {{currencySymbol}} نہیں ہے۔" }, diff --git a/packages/uniswap/src/i18n/locales/translations/vi-VN.json b/packages/uniswap/src/i18n/locales/translations/vi-VN.json index 043583baa09..5de7639e2b0 100644 --- a/packages/uniswap/src/i18n/locales/translations/vi-VN.json +++ b/packages/uniswap/src/i18n/locales/translations/vi-VN.json @@ -174,6 +174,7 @@ "dismiss": "Miễn nhiệm", "done": "Xong", "enable": "Cho phép", + "goBack": "Quay lại", "hide": "Trốn", "later": "Có lẽ sau này", "learn": "Tìm hiểu thêm", @@ -205,6 +206,7 @@ "title": "Ối! Đã xảy ra lỗi." } }, + "endAdornment": "Và", "error": { "general": "Đã xảy ra lỗi." }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "Cái ví" + "action": "Chấp thuận", + "fallbackTitle": "Phê duyệt mã thông báo chi tiêu", + "helptext": "Cho phép trang web này truy cập và chi tiêu mã thông báo này từ ví của bạn.", + "label": "Cái ví", + "title": "Phê duyệt chi tiêu {{tokenSymbol}}" + }, + "base": { + "title": "Xác nhận giao dịch" + }, + "changeChain": { + "title": "Chuyển mạng sang {{network}}" + }, + "connect": { + "helptext": "Cho phép trang web này xem địa chỉ ví, số dư của bạn và yêu cầu phê duyệt các giao dịch.", + "title": "Kết nối với trang web" }, "error": { "none": "Không có phê duyệt nào đang chờ xử lý" }, + "fallback": { + "calldata": { + "label": "dữ liệu" + }, + "function": { + "label": "chức năng" + }, + "recipient": { + "label": "đẾN" + }, + "sending": { + "label": "gửi" + } + }, "signature": { - "education": { - "description": "Cần có chữ ký để chứng minh rằng bạn sở hữu ví mà không để lộ khóa riêng của mình", - "title": "Yêu cầu chữ ký là gì?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent đã nhận được dữ liệu để ký không tuân thủ thông số EIP-712." } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "Ví của bạn không được kết nối với trang web này.", - "popupWithButton": "Ví của bạn không được kết nối với trang web này. Tìm nút “Kết nối ví” hoặc “Đăng nhập”." + "popup": "Ví của bạn không được kết nối với trang web này. Tìm nút “Kết nối ví” hoặc “Đăng nhập”.", + "popupWithButton": "Ví của bạn không được kết nối với trang web này.", + "titleConnected": "Đã kết nối", + "titleNotConnected": "Không kết nối" + }, + "dappRequest": { + "signatureRequest": { + "header": "Yêu cầu chữ ký" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "Kết nối bạn với {{serviceProvider}}", - "quote": "Mua {{amount}} trị giá {{currencySymbol}}" + "quote": "Mua {{amount}} trị giá {{currencySymbol}}", + "terms": "Bằng cách tiếp tục, bạn xác nhận rằng bạn sẽ tuân theo Điều khoản dịch vụ và Chính sách quyền riêng tư với {{serviceProvider}}, nếu có." }, "error": { "default": "Đã xảy ra lỗi.", @@ -427,10 +464,9 @@ "usd": "Chỉ có thể mua bằng USD" }, "quote": { - "amount": "Nhận {{tokenAmount}}", - "amountAfterFees": "{{tokenAmount}} sau phí", + "advice": "Bạn sẽ tiếp tục đến cổng thông tin của nhà cung cấp để xem các khoản phí liên quan đến giao dịch của mình.", + "others": "người khác", "type": { - "best": "Tổng thể tốt nhất", "other": "Sự lựa chọn khác", "recent": "Được sử dụng gần đây" } @@ -470,6 +506,11 @@ "title": "Hoạt động" }, "banner": { + "extension": { + "confirm": "Tham gia Beta", + "message": "Hãy là người đầu tiên dùng thử Tiện ích mở rộng Uniswap trên trình duyệt web của bạn", + "title": "Tiện ích mở rộng Uniswap có tại đây" + }, "offline": "Bạn đang ở chế độ ngoại tuyến" }, "extension": { @@ -491,6 +532,14 @@ "send": "Gửi", "swap": "Tráo đổi" }, + "modal": { + "getExtension": { + "step1": "1. Truy cập uniswap.org/ext trên máy tính của bạn", + "step2": "2. Thêm Tiện ích mở rộng Uniswap trên trình duyệt Chrome của bạn", + "step3": "3. Nhập tên người dùng của bạn để có quyền truy cập", + "title": "Tham gia bản Beta tiện ích mở rộng Uniswap" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "Đã sao chép địa chỉ", + "calldata": "Đã sao chép dữ liệu cuộc gọi", "contractAddress": "Đã sao chép địa chỉ hợp đồng", "failed": "Không sao chép được vào bảng nhớ tạm", "image": "Đã sao chép hình ảnh", @@ -715,6 +765,10 @@ "button": "Nhập từ điện thoại của bạn", "title": "Bạn có ứng dụng di động Uniswap không?" }, + "getOnTheBetaWaitlist": { + "subtitle": "Tải xuống ứng dụng di động và yêu cầu tên người dùng", + "title": "Tham gia danh sách chờ Beta" + }, "password": { "subtitle": "Bạn sẽ cần thông tin này để mở khóa ví và truy cập cụm từ khôi phục của mình", "title": { @@ -764,6 +818,19 @@ }, "title": "Chào mừng bạn đến với \nVí Uniswap" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "Kiểm tra tính đủ điều kiện", + "letsGo": "Đi nào" + }, + "checkEligibilityInstructions": "Nhập tên người dùng uni.eth của bạn bên dưới để kiểm tra xem bạn có đủ điều kiện tham gia bản Beta hay không.", + "eligible": { + "tagline": "Chào mừng bạn đến với bản Beta — bạn là một trong những người đầu tiên dùng thử ví Uniswap.", + "title": "Bạn đã ra khỏi danh sách chờ!" + }, + "ineligibleExplanation": "Bạn vẫn còn trong danh sách chờ. Chúng tôi sẽ thông báo cho bạn trong ứng dụng di động Uniswap khi bạn đủ điều kiện!", + "unitagPlaceholder": "tên tài khoản" + }, "landing": { "button": { "add": "Thêm ví hiện có", @@ -814,12 +881,12 @@ "error": "Rất tiếc, chúng tôi không thể tải mã QR ngay bây giờ. Vui lòng thử một phương pháp giới thiệu khác.", "otp": { "error": "Mã bạn gửi không chính xác hoặc đã xảy ra lỗi khi gửi. Vui lòng thử lại.", - "failed": "Nỗ lực thất bại: {{count}}", + "failed": "Nỗ lực thất bại: {{number}}", "subtitle": "Kiểm tra ứng dụng di động Uniswap của bạn để tìm mã 6 ký tự", "title": "Nhập mã một lần" }, - "subtitle": "Quét mã QR bằng ứng dụng Uniswap để nhập ví của bạn", - "title": "Đồng bộ hóa từ điện thoại của bạn" + "subtitle": "Quét mã QR bằng ứng dụng di động Uniswap để bắt đầu nhập (các) ví của bạn", + "title": "Nhập ví từ ứng dụng" }, "security": { "alert": { @@ -959,6 +1026,16 @@ "message": "Quét lại mã QR trên Tiện ích mở rộng Uniswap để tiếp tục đồng bộ hóa ví của bạn.", "title": "Kết nối của bạn đã hết thời gian chờ" } + }, + "modal": { + "notOnWaitlist": { + "message": "Để đủ điều kiện tham gia Uniswap Extension Beta, hãy tham gia danh sách chờ bằng cách yêu cầu tên người dùng uni.eth", + "title": "Bạn không có tên trong danh sách chờ" + }, + "onWaitlist": { + "message": "Chúng tôi sẽ thông báo cho bạn trong ứng dụng khi bạn đủ điều kiện tham gia Bản mở rộng Uniswap Beta.", + "title": "Bạn vẫn đang trong danh sách chờ" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "Xem lại chuyển khoản", "send": "Gửi" }, + "gas": { + "error": { + "title": "không áp dụng" + }, + "networkCost": { + "title": "Chi phí mạng" + } + }, + "input": { + "token": { + "balance": { + "title": "Số dư: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "{{count}} lần chuyển tiền trước", + "previous_one": "1 lần chuyển trước đó", "previous_other": "{{count}} lần chuyển tiền trước", "section": { "favorite": "Ví yêu thích", "recent": "Gần đây", "search": "Kết quả tìm kiếm", + "viewOnly": "Ví chỉ xem", "yours": "Ví của bạn" + }, + "warning": { + "viewOnly": { + "message": "Chỉ gửi tiền vào ví này nếu bạn có cụm từ khôi phục hoặc biết chủ sở hữu của ví.", + "title": "Bạn có ví này dưới dạng ví chỉ xem" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "Khi bạn gửi mã thông báo đến địa chỉ ví, chúng sẽ hiển thị ở đây", + "title": "Không có ví nào được lưu" + } + }, + "title": "ĐẾN" + }, "review": { + "modal": { + "title": "Bạn đang gửi" + }, "summary": { + "button": { + "title": "Xác nhận gửi" + }, "sending": "Gửi", "to": "ĐẾN" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "Số dư {{currencySymbol}} của bạn đã giảm kể từ khi bạn nhập số tiền bạn muốn gửi", - "title": "Không đủ {{currencySymbol}}." + "title": "Không đủ {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "ĐƯỢC RỒI", + "cancel": "Hủy bỏ", + "confirm": "Xác nhận" + } + } }, "newAddress": { "message": "Bạn chưa từng giao dịch với địa chỉ này trước đây. Vui lòng xác nhận rằng địa chỉ là chính xác trước khi tiếp tục.", @@ -1110,12 +1233,16 @@ "viewAll": "Xem tất cả", "viewLess": "Xem ít hơn" }, + "label": { + "viewOnly": "Chỉ xem" + }, "title": "Cài đặt ví" } }, "setting": { "appearance": { "option": { + "auto": "Tự động", "dark": { "subtitle": "Luôn sử dụng chế độ tối", "title": "Chế độ tối" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} dự phòng" } }, + "beta": { + "tooltip": "Sắp ra mắt!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "Nếu bạn không bật {{biometricsMethod}}, bất kỳ ai có quyền truy cập vào thiết bị của bạn đều có thể mở Ví Uniswap và thực hiện giao dịch.", - "ios": "Nếu bạn không bật sinh trắc học, bất kỳ ai có quyền truy cập vào thiết bị của bạn đều có thể mở Ví Uniswap và thực hiện giao dịch." + "android": "Nếu bạn không bật sinh trắc học, bất kỳ ai có quyền truy cập vào thiết bị của bạn đều có thể mở Ví Uniswap và thực hiện giao dịch.", + "ios": "Nếu bạn không bật {{biometricsMethod}}, bất kỳ ai có quyền truy cập vào thiết bị của bạn đều có thể mở Ví Uniswap và thực hiện giao dịch." }, "title": "Bạn có chắc không?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "Nội tệ" }, + "giveFeedback": { + "title": "Cung cấp thông tin phản hồi" + }, "helpCenter": { "title": "Trung tâm trợ giúp" }, @@ -1241,6 +1374,7 @@ "navigate": "Đi tới cài đặt" }, "description": "Uniswap mặc định cài đặt ngôn ngữ trên thiết bị của bạn. Để thay đổi ngôn ngữ ưa thích của bạn, hãy truy cập “Uniswap” trong cài đặt thiết bị của bạn và nhấn vào “Ngôn ngữ”", + "exampleTitle": "Tiếng Anh", "title": "Ngôn ngữ" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "Ẩn số dư nhỏ" }, + "theme": { + "title": "chủ đề" + }, "unknownTokens": { "title": "Ẩn mã thông báo không xác định" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "Tối đa", + "review": "Đánh giá trao đổi", "swap": "Tráo đổi", "unwrap": "Mở gói", "view": "Xem giao dịch", diff --git a/packages/uniswap/src/i18n/locales/translations/zh-CN.json b/packages/uniswap/src/i18n/locales/translations/zh-CN.json index 2906452056f..a254246826f 100644 --- a/packages/uniswap/src/i18n/locales/translations/zh-CN.json +++ b/packages/uniswap/src/i18n/locales/translations/zh-CN.json @@ -174,6 +174,7 @@ "dismiss": "关闭", "done": "完成", "enable": "开启", + "goBack": "回去", "hide": "隐藏", "later": "稍后再说", "learn": "了解更多", @@ -205,6 +206,7 @@ "title": "哎呀!出了些问题。" } }, + "endAdornment": "和", "error": { "general": "出了些问题。" }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "钱包" + "action": "批准", + "fallbackTitle": "批准支出代币", + "helptext": "允许该网站访问并使用您钱包中的此代币。", + "label": "钱包", + "title": "批准支出 {{tokenSymbol}}" + }, + "base": { + "title": "确认交易" + }, + "changeChain": { + "title": "将网络切换至 {{network}}" + }, + "connect": { + "helptext": "允许该网站查看您的钱包地址、余额并请求交易批准。", + "title": "连接到站点" }, "error": { "none": "没有等待批准的情况" }, + "fallback": { + "calldata": { + "label": "数据" + }, + "function": { + "label": "功能" + }, + "recipient": { + "label": "接受者" + }, + "sending": { + "label": "发送" + } + }, "signature": { - "education": { - "description": "需要签名来证明你拥有该钱包而不暴露你的私钥", - "title": "什么是签名请求?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent 收到的签名数据不符合 EIP-712 规范。" } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "您的钱包未连接到此网站。", - "popupWithButton": "您的钱包未连接到此网站。寻找“连接钱包”或“登录”按钮。" + "popup": "您的钱包未连接到此网站。寻找“连接钱包”或“登录”按钮。", + "popupWithButton": "您的钱包未连接到此网站。", + "titleConnected": "已连接", + "titleNotConnected": "未连接" + }, + "dappRequest": { + "signatureRequest": { + "header": "签名请求" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "将您连接至 {{serviceProvider}}", - "quote": "买入 {{amount}} 价值 {{currencySymbol}}" + "quote": "买入 {{amount}} 价值 {{currencySymbol}}", + "terms": "继续操作即表示您承认您将遵守 {{serviceProvider}}的服务条款和隐私政策(如果适用)。" }, "error": { "default": "出了些问题。", @@ -427,10 +464,9 @@ "usd": "只能以美元买入" }, "quote": { - "amount": "接收 {{tokenAmount}}", - "amountAfterFees": "费用后{{tokenAmount}}", + "advice": "您将继续访问提供商的门户以查看与您的交易相关的费用。", + "others": "其他的", "type": { - "best": "最佳整体", "other": "其他选项", "recent": "最近使用过" } @@ -470,6 +506,11 @@ "title": "动态" }, "banner": { + "extension": { + "confirm": "加入测试版", + "message": "成为第一个在网络浏览器上试用 Uniswap 扩展的人", + "title": "Uniswap 扩展在这里" + }, "offline": "你处于离线模式" }, "extension": { @@ -491,6 +532,14 @@ "send": "发送", "swap": "兑换" }, + "modal": { + "getExtension": { + "step1": "1. 在电脑上访问 uniswap.org/ext", + "step2": "2. 在 Chrome 浏览器上添加 Uniswap 扩展程序", + "step3": "3. 输入您的用户名进行访问", + "title": "加入 Uniswap 扩展测试版" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "地址已复制", + "calldata": "通话数据已复制", "contractAddress": "复制合约地址", "failed": "复制到剪贴板失败", "image": "图像已复制", @@ -708,13 +758,17 @@ "label": "昵称", "subtitle": "给你的钱包起个昵称", "title": "此昵称只有你自己可见", - "walletAddress": "Your public address will be {{walletAddress}}" + "walletAddress": "您的公共地址将是{{walletAddress}}" }, "extension": { "connectMobile": { "button": "从您的手机导入", "title": "有 Uniswap 应用程序吗?" }, + "getOnTheBetaWaitlist": { + "subtitle": "下载移动应用程序并索取用户名", + "title": "进入测试版候补名单" + }, "password": { "subtitle": "您需要它来解锁您的钱包并访问您的恢复短语", "title": { @@ -764,6 +818,19 @@ }, "title": "欢迎来到<> Uniswap 钱包" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "检查资格", + "letsGo": "我们走吧" + }, + "checkEligibilityInstructions": "在下面输入您的 uni.eth 用户名,检查您是否有资格参加测试版。", + "eligible": { + "tagline": "欢迎来到 Beta 版——您是第一批尝试 Uniswap 钱包的人之一。", + "title": "您已退出候补名单!" + }, + "ineligibleExplanation": "您仍在候补名单上。当您符合资格时,我们将在 Uniswap 移动应用程序中通知您!", + "unitagPlaceholder": "用户名" + }, "landing": { "button": { "add": "添加已有钱包", @@ -814,12 +881,12 @@ "error": "抱歉,我们现在无法加载二维码。请尝试其他入职方法。", "otp": { "error": "您提交的代码不正确,或者提交时出错。请再试一次。", - "failed": "失败尝试: {{count}}", + "failed": "失败尝试: {{number}}", "subtitle": "检查您的 Uniswap 移动应用程序中的 6 字符代码", "title": "输入一次性代码" }, - "subtitle": "使用 Uniswap 应用扫描二维码导入钱包", - "title": "从您的手机同步" + "subtitle": "使用 Uniswap 移动应用扫描二维码开始导入您的钱包", + "title": "从应用程序导入钱包" }, "security": { "alert": { @@ -843,7 +910,7 @@ }, "subtitle": { "android": "通过要求生物识别技术发送交易来添加额外的安全层。", - "ios": "通过要求 {{biometricsMethod}} 发送交易来添加额外的安全层。" + "ios": "{{biometricsMethod}} 让你的交易更加安全" }, "title": "保护你的钱包" }, @@ -854,7 +921,7 @@ "error": "导入钱包时出错" } }, - "termsOfService": "继续操作即表示我同意 服务条款 并同意 隐私政策。", + "termsOfService": "我同意服务条款隐私政策。", "tooltip": { "recoveryPhrase": { "trigger": "什么是恢复短语?" @@ -909,7 +976,7 @@ }, "method": { "default": "来自 {{dappNameOrUrl}}的请求", - "signature": "Signature request from {{dappNameOrUrl}}", + "signature": "来自 {{dappNameOrUrl}} 的签名请求", "transaction": "来自 {{dappNameOrUrl}}的交易请求" }, "withAmount": "允许 {{dappName}} 使用最多 {{amount}} {{currencySymbol}}?", @@ -959,6 +1026,16 @@ "message": "再次扫描 Uniswap Extension 上的二维码以继续同步您的钱包。", "title": "您的连接超时" } + }, + "modal": { + "notOnWaitlist": { + "message": "为了获得 Uniswap Extension Beta 的资格,请通过申请 uni.eth 用户名加入候补名单", + "title": "您不在候补名单上" + }, + "onWaitlist": { + "message": "当您有资格加入 Uniswap Extension Beta 时,我们会在应用程序中通知您。", + "title": "您仍在候补名单上" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "预览", "send": "发送" }, + "gas": { + "error": { + "title": "不适用" + }, + "networkCost": { + "title": "网络费用" + } + }, + "input": { + "token": { + "balance": { + "title": "余额: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "之前 {{count}} 次发送", + "previous_one": "1 次先前转账", "previous_other": "之前 {{count}} 次发送", "section": { "favorite": "收藏钱包", "recent": "最近", "search": "搜索结果", + "viewOnly": "仅查看钱包", "yours": "你的钱包" + }, + "warning": { + "viewOnly": { + "message": "仅当您有恢复短语或知道钱包所有者时才将资金发送至此钱包。", + "title": "您将其作为仅供查看的钱包" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "当您将代币发送到钱包地址时,它们会显示在此处", + "title": "没有保存钱包" + } + }, + "title": "接受者" + }, "review": { + "modal": { + "title": "你正在发送" + }, "summary": { + "button": { + "title": "确认发送" + }, "sending": "发送", "to": "接受者" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "自从你输入要发送的金额以来,你的余额已减少 {{currencySymbol}}", - "title": "不够 {{currencySymbol}}." + "title": "不够 {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "好的", + "cancel": "取消", + "confirm": "确认" + } + } }, "newAddress": { "message": "你以前没有与这地址交易过。请确认地址是否正确。", @@ -1110,12 +1233,16 @@ "viewAll": "显示全部", "viewLess": "显示更少" }, + "label": { + "viewOnly": "仅查看" + }, "title": "钱包设定" } }, "setting": { "appearance": { "option": { + "auto": "自动", "dark": { "subtitle": "一直使用深色主题", "title": "深色主题" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} 备份" } }, + "beta": { + "tooltip": "即将推出!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "如果你不开启 {{biometricsMethod}},任何有权访问你设备的人都可以打开 Uniswap 钱包并进行交易。", - "ios": "如果你不开启生物识别,任何有权访问你设备的人都可以打开 Uniswap 钱包并进行交易。" + "android": "如果你不开启生物识别,任何有权访问你设备的人都可以打开 Uniswap 钱包并进行交易。", + "ios": "如果你不开启 {{biometricsMethod}},任何有权访问你设备的人都可以打开 Uniswap 钱包并进行交易。" }, "title": "你确定吗?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "当地货币" }, + "giveFeedback": { + "title": "给予反馈" + }, "helpCenter": { "title": "帮助中心" }, @@ -1241,6 +1374,7 @@ "navigate": "前往设置" }, "description": "Uniswap 默认为您设备的语言设置。要更改您的首选语言,请转到设备设置中的“Uniswap”,然后点击“语言”", + "exampleTitle": "英语", "title": "语言" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "隐藏小额余额" }, + "theme": { + "title": "主题" + }, "unknownTokens": { "title": "隐藏未知代币" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "总余额", + "review": "审查交换", "swap": "兑换", "unwrap": "拆封", "view": "查看交易", @@ -1680,7 +1818,7 @@ "failed": "接收失败", "pending": "接收中", "success": "已接收", - "successDapp": "收到于 {{externalDappName}}" + "successDapp": "收到 {{externalDappName}}" }, "revoke": { "canceled": "已取消撤销授权", diff --git a/packages/uniswap/src/i18n/locales/translations/zh-TW.json b/packages/uniswap/src/i18n/locales/translations/zh-TW.json index 1dfbfa8b1b4..61f1a858c78 100644 --- a/packages/uniswap/src/i18n/locales/translations/zh-TW.json +++ b/packages/uniswap/src/i18n/locales/translations/zh-TW.json @@ -174,6 +174,7 @@ "dismiss": "關閉", "done": "完畢", "enable": "開啟", + "goBack": "回去", "hide": "隱藏", "later": "稍後再說", "learn": "了解更多", @@ -205,6 +206,7 @@ "title": "哎呀!出了些問題。" } }, + "endAdornment": "和", "error": { "general": "出了些問題。" }, @@ -266,15 +268,42 @@ "dapp": { "request": { "approve": { - "label": "錢包" + "action": "核准", + "fallbackTitle": "批准支出代幣", + "helptext": "允許該網站存取並使用您錢包中的此代幣。", + "label": "錢包", + "title": "核准支出 {{tokenSymbol}}" + }, + "base": { + "title": "確認交易" + }, + "changeChain": { + "title": "將網路切換至 {{network}}" + }, + "connect": { + "helptext": "允許網站查看您的錢包地址、餘額並請求交易批准。", + "title": "連接到站點" }, "error": { "none": "沒有等待批准的情況" }, + "fallback": { + "calldata": { + "label": "數據" + }, + "function": { + "label": "函數" + }, + "recipient": { + "label": "接收者" + }, + "sending": { + "label": "發送" + } + }, "signature": { - "education": { - "description": "需要簽名來證明你擁有該錢包而不暴露你的私鑰", - "title": "什麼是簽名請求?" + "error": { + "712-spec-compliance": "SignTypedDataRequestContent 收到的簽章資料不符合 EIP-712 規格。" } }, "warning": { @@ -370,8 +399,15 @@ }, "extension": { "connection": { - "popup": "您的錢包未連接到此網站。", - "popupWithButton": "您的錢包未連接到此網站。尋找“連接錢包”或“登入”按鈕。" + "popup": "您的錢包未連接到此網站。尋找“連接錢包”或“登入”按鈕。", + "popupWithButton": "您的錢包未連接到此網站。", + "titleConnected": "已連接", + "titleNotConnected": "未連接" + }, + "dappRequest": { + "signatureRequest": { + "header": "簽名請求" + } }, "lock": { "button": { @@ -416,7 +452,8 @@ }, "connection": { "message": "將您連接至 {{serviceProvider}}", - "quote": "買入 {{amount}} 價值 {{currencySymbol}}" + "quote": "買入 {{amount}} 價值 {{currencySymbol}}", + "terms": "繼續操作即表示您承認您將遵守 {{serviceProvider}}的服務條款和隱私權政策(如果適用)。" }, "error": { "default": "出了些問題。", @@ -427,10 +464,9 @@ "usd": "只能以美元買入" }, "quote": { - "amount": "接收 {{tokenAmount}}", - "amountAfterFees": "費用後{{tokenAmount}}", + "advice": "您將繼續訪問提供者的入口網站以查看與您的交易相關的費用。", + "others": "其他的", "type": { - "best": "最佳整體", "other": "其他選項", "recent": "最近使用過" } @@ -470,6 +506,11 @@ "title": "動態" }, "banner": { + "extension": { + "confirm": "加入測試版", + "message": "成為第一個在網頁瀏覽器上試用 Uniswap 擴充功能的人", + "title": "Uniswap 擴充功能在這裡" + }, "offline": "你處於離線模式" }, "extension": { @@ -491,6 +532,14 @@ "send": "傳送", "swap": "兌換" }, + "modal": { + "getExtension": { + "step1": "1. 在電腦上訪問 uniswap.org/ext", + "step2": "2. 在 Chrome 瀏覽器上新增 Uniswap 擴充功能", + "step3": "3. 輸入您的使用者名稱進行訪問", + "title": "加入 Uniswap 擴充測試版" + } + }, "nfts": { "title": "NFT" }, @@ -558,6 +607,7 @@ }, "copied": { "address": "地址已複製", + "calldata": "通話資料已複製", "contractAddress": "複製合約地址", "failed": "無法複製到剪貼簿", "image": "圖像已複製", @@ -708,13 +758,17 @@ "label": "暱稱", "subtitle": "給你的錢包個暱稱", "title": "此暱稱只有你自己可見", - "walletAddress": "Your public address will be {{walletAddress}}" + "walletAddress": "您的公共地址將是{{walletAddress}}" }, "extension": { "connectMobile": { "button": "從您的手機匯入", "title": "有 Uniswap 應用程式嗎?" }, + "getOnTheBetaWaitlist": { + "subtitle": "下載行動應用程式並索取用戶名", + "title": "進入測試版候補名單" + }, "password": { "subtitle": "您需要它來解鎖您的錢包並訪問您的恢復短語", "title": { @@ -764,6 +818,19 @@ }, "title": "歡迎來到 \nUniswap 錢包" }, + "introBetaWaitlist": { + "button": { + "checkEligibility": "檢查資格", + "letsGo": "我們走吧" + }, + "checkEligibilityInstructions": "在下面輸入您的 uni.eth 用戶名,檢查您是否有資格參加測試版。", + "eligible": { + "tagline": "歡迎來到 Beta 版——您是第一批嘗試 Uniswap 錢包的人之一。", + "title": "您已登出候補名單!" + }, + "ineligibleExplanation": "您仍在候補名單上。當您符合資格時,我們將在 Uniswap 行動應用程式中通知您!", + "unitagPlaceholder": "使用者名稱" + }, "landing": { "button": { "add": "添加已有錢包", @@ -814,12 +881,12 @@ "error": "抱歉,我們現在無法載入二維碼。請嘗試其他入職方法。", "otp": { "error": "您提交的程式碼不正確,或提交時發生錯誤。請再試一次。", - "failed": "失敗嘗試: {{count}}", + "failed": "失敗嘗試: {{number}}", "subtitle": "檢查您的 Uniswap 行動應用程式中的 6 字元代碼", "title": "輸入一次性代碼" }, - "subtitle": "使用 Uniswap 應用程式掃描二維碼導入錢包", - "title": "從您的手機同步" + "subtitle": "使用 Uniswap 行動應用程式掃描二維碼開始匯入您的錢包", + "title": "從應用程式導入錢包" }, "security": { "alert": { @@ -909,7 +976,7 @@ }, "method": { "default": "來自 {{dappNameOrUrl}}的請求", - "signature": "Signature request from {{dappNameOrUrl}}", + "signature": "從 {{dappNameOrUrl}} 的簽名請求", "transaction": "來自 {{dappNameOrUrl}}的交易請求" }, "withAmount": "允許 {{dappName}} 使用最多 {{amount}} {{currencySymbol}}?", @@ -959,6 +1026,16 @@ "message": "再次掃描 Uniswap Extension 上的二維碼以繼續同步您的錢包。", "title": "您的連線逾時" } + }, + "modal": { + "notOnWaitlist": { + "message": "為了獲得 Uniswap Extension Beta 的資格,請透過申請 uni.eth 使用者名稱加入候補名單", + "title": "您不在候補名單上" + }, + "onWaitlist": { + "message": "當您有資格加入 Uniswap Extension Beta 時,我們會在應用程式中通知您。", + "title": "您仍在候補名單上" + } } }, "send": { @@ -966,18 +1043,55 @@ "review": "預覽", "send": "傳送" }, + "gas": { + "error": { + "title": "不適用" + }, + "networkCost": { + "title": "網路成本" + } + }, + "input": { + "token": { + "balance": { + "title": "餘額: {{balance}} {{symbol}}" + } + } + }, "recipient": { - "previous_one": "之前 {{count}} 次發送", + "previous_one": "1 次先前轉賬", "previous_other": "之前 {{count}} 次發送", "section": { "favorite": "收藏錢包", "recent": "最近", "search": "搜尋結果", + "viewOnly": "僅供查看的錢包", "yours": "你的錢包" + }, + "warning": { + "viewOnly": { + "message": "只有當您有恢復短語或知道錢包所有者時才將資金發送至此錢包。", + "title": "您將其作為僅供查看的錢包" + } } }, + "recipientSelect": { + "search": { + "empty": { + "message": "當您將代幣發送到錢包地址時,它們會顯示在此處", + "title": "沒有保存錢包" + } + }, + "title": "接收者" + }, "review": { + "modal": { + "title": "你正在發送" + }, "summary": { + "button": { + "title": "確認發送" + }, "sending": "發送", "to": "接收者" } @@ -1017,7 +1131,16 @@ }, "insufficientFunds": { "message": "自從你輸入要發送的金額以來,你的餘額已減少 {{currencySymbol}}", - "title": "不夠 {{currencySymbol}}." + "title": "不夠 {{currencySymbol}}" + }, + "modal": { + "button": { + "cta": { + "blocking": "好的", + "cancel": "取消", + "confirm": "確認" + } + } }, "newAddress": { "message": "你以前沒有與這地址交易過。請確認地址是否正確。", @@ -1110,12 +1233,16 @@ "viewAll": "顯示全部", "viewLess": "顯示更少" }, + "label": { + "viewOnly": "僅查看" + }, "title": "錢包設定" } }, "setting": { "appearance": { "option": { + "auto": "自動", "dark": { "subtitle": "一直使用深色主題", "title": "深色主題" @@ -1185,6 +1312,9 @@ "title": "{{cloudProviderName}} 備份" } }, + "beta": { + "tooltip": "即將推出!" + }, "biometrics": { "appAccess": { "subtitle": { @@ -1224,8 +1354,8 @@ }, "warning": { "message": { - "android": "如果不開啟 {{biometricsMethod}},任何有權存取你設備的人都可以打開 Uniswap 錢包並進行交易。", - "ios": "如果不開啟生物識別,任何有權存取你設備的人都可以打開 Uniswap 錢包並進行交易。" + "android": "如果不開啟生物識別,任何有權存取你設備的人都可以打開 Uniswap 錢包並進行交易。", + "ios": "如果不開啟 {{biometricsMethod}},任何有權存取你設備的人都可以打開 Uniswap 錢包並進行交易。" }, "title": "你確定嗎?" } @@ -1233,6 +1363,9 @@ "currency": { "title": "當地貨幣" }, + "giveFeedback": { + "title": "給予回饋" + }, "helpCenter": { "title": "幫助中心" }, @@ -1241,6 +1374,7 @@ "navigate": "前往設定" }, "description": "Uniswap 預設為您裝置的語言設定。若要變更您的首選語言,請前往裝置設定中的“Uniswap”,然後點擊“語言”", + "exampleTitle": "英語", "title": "語言" }, "password": { @@ -1261,6 +1395,9 @@ "smallBalances": { "title": "隱藏小額餘額" }, + "theme": { + "title": "主題" + }, "unknownTokens": { "title": "隱藏未知代幣" }, @@ -1294,6 +1431,7 @@ "swap": { "button": { "max": "總餘額", + "review": "審查交換", "swap": "兌換", "unwrap": "拆封", "view": "查看交易", @@ -1945,7 +2083,7 @@ }, "details": { "label": { - "function": "功能: ", + "function": "函數: ", "recipient": "到: ", "sending": "發送: " } diff --git a/packages/uniswap/src/types/currency.ts b/packages/uniswap/src/types/currency.ts new file mode 100644 index 00000000000..f675182f015 --- /dev/null +++ b/packages/uniswap/src/types/currency.ts @@ -0,0 +1 @@ +export type CurrencyId = string diff --git a/packages/utilities/package.json b/packages/utilities/package.json index e6b16270f21..2ad1cea6a1b 100644 --- a/packages/utilities/package.json +++ b/packages/utilities/package.json @@ -17,7 +17,7 @@ "@sentry/react-native": "5.5.0", "@uniswap/analytics": "1.7.0", "@uniswap/analytics-events": "2.32.0", - "@uniswap/sdk-core": "4.1.2", + "@uniswap/sdk-core": "4.2.0", "aws-appsync-auth-link": "3.0.7", "aws-appsync-subscription-link": "3.1.3", "dayjs": "1.11.7", diff --git a/packages/utilities/src/format/urls.test.ts b/packages/utilities/src/format/urls.test.ts index d6a3b649e82..681e25237fd 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 { isSVGUri, uriToHttp } from './urls' +import { isGifUri, isSVGUri, uriToHttp } from './urls' describe(uriToHttp, () => { it('returns .eth.link for ens names', () => { @@ -48,4 +48,103 @@ describe(isSVGUri, () => { expect(isSVGUri(null)).toEqual(false) expect(isSVGUri(undefined)).toEqual(false) }) + it('returns true for an SVG URI with uppercase extension', () => { + expect(isSVGUri('http://example.com/image.SVG')).toEqual(true) + }) + + it('returns false for a non-SVG URI', () => { + expect(isSVGUri('http://example.com/image.png')).toEqual(false) + }) + + it('returns true for an SVG URI with query parameters', () => { + expect(isSVGUri('http://example.com/image.svg?query=123')).toEqual(true) + }) + + it('returns true for an SVG URI with a fragment', () => { + expect(isSVGUri('http://example.com/image.svg#fragment')).toEqual(true) + }) + + it('returns false for a URI that contains ".svg" but is not an SVG file', () => { + expect(isSVGUri('http://example.com/.svg/image.png')).toEqual(false) + }) + + it('returns false for a URI ending with ".svg" in the path but is actually a directory', () => { + expect(isSVGUri('http://example.com/assets/svg/')).toEqual(false) + }) + + it('returns false for an undefined URI', () => { + expect(isSVGUri(undefined)).toEqual(false) + }) + + it('returns false for a null URI', () => { + expect(isSVGUri(null)).toEqual(false) + }) + + it('handles invalid URIs gracefully', () => { + expect(isSVGUri('http:///example.com:::image.svg')).toEqual(false) + }) + + it('returns false for an empty string', () => { + expect(isSVGUri('')).toEqual(false) + }) + + it('returns false for a non-URI string that ends with ".svg"', () => { + expect(isSVGUri('This is not a URI.svg')).toEqual(false) + }) +}) + +describe(isGifUri, () => { + it('detects gif', () => { + expect(isGifUri('http://test.com/x.gif')).toEqual(true) + }) + it(`doesn't see http`, () => { + expect(isGifUri('http://test.com')).toEqual(false) + }) + it('null and undefined handled the same way', () => { + expect(isGifUri(null)).toEqual(false) + expect(isGifUri(undefined)).toEqual(false) + }) + it('returns true for an GIF URI with uppercase extension', () => { + expect(isGifUri('http://example.com/image.GIF')).toEqual(true) + }) + + it('returns false for a non-GIF URI', () => { + expect(isGifUri('http://example.com/image.png')).toEqual(false) + }) + + it('returns true for an gif URI with query parameters', () => { + expect(isGifUri('http://example.com/image.gif?query=123')).toEqual(true) + }) + + it('returns true for an gif URI with a fragment', () => { + expect(isGifUri('http://example.com/image.gif#fragment')).toEqual(true) + }) + + it('returns false for a URI that contains ".gif" but is not an gif file', () => { + expect(isGifUri('http://example.com/.gif/image.png')).toEqual(false) + }) + + it('returns false for a URI ending with ".gif" in the path but is actually a directory', () => { + expect(isGifUri('http://example.com/assets/gif/')).toEqual(false) + }) + + it('returns false for an undefined URI', () => { + expect(isGifUri(undefined)).toEqual(false) + }) + + it('returns false for a null URI', () => { + expect(isGifUri(null)).toEqual(false) + }) + + it('handles invalid URIs gracefully', () => { + expect(isGifUri('http:///example.com:::image.gif')).toEqual(false) + }) + + it('returns false for an empty string', () => { + expect(isGifUri('')).toEqual(false) + }) + + it('returns false for a non-URI string that ends with ".gif"', () => { + expect(isGifUri('This is not a URI.gif')).toEqual(false) + }) }) diff --git a/packages/utilities/src/format/urls.ts b/packages/utilities/src/format/urls.ts index 0e845869cb8..ae7bce610b6 100644 --- a/packages/utilities/src/format/urls.ts +++ b/packages/utilities/src/format/urls.ts @@ -28,6 +28,50 @@ export function uriToHttp(uri: string): string[] { return [] } +export function isSegmentUri(uri: Maybe, extension: string): boolean { + if (typeof uri !== 'string' || !uri.trim()) { + return false + } + + try { + // Validate URI structure by checking for presence of scheme + if (!/^https?:\/\/.+/i.test(uri)) { + return false + } + + const url = new URL(uri) + const pathname = url.pathname + + // Check if pathname ends with an '.svg' (or other) extension, case-insensitive + return pathname.toLowerCase().endsWith(extension) + } catch { + // URI parsing failed, indicating an invalid URI + return false + } +} + +/** + * Checks if the provided URI points to an SVG file. + * + * This examines the path of a URI to determine if it ends with an ".svg" extension, + * accounting for potential query parameters or anchors. The check is case-insensitive. + * + * @param {Maybe} uri The URI to check. + * @returns {boolean} True if the URI points to an SVG file, false otherwise. + */ export function isSVGUri(uri: Maybe): boolean { - return uri?.includes('.svg') ?? false + return isSegmentUri(uri, '.svg') +} + +/** + * Checks if the provided URI points to a GIF file. + * + * This examines the path of a URI to determine if it ends with an ".gif" extension, + * accounting for potential query parameters or anchors. The check is case-insensitive. + * + * @param {Maybe} uri The URI to check. + * @returns {boolean} True if the URI points to an GIF file, false otherwise. + */ +export function isGifUri(uri: Maybe): boolean { + return isSegmentUri(uri, '.gif') } diff --git a/packages/utilities/src/primitives/string.ts b/packages/utilities/src/primitives/string.ts index 0f6d8129950..2d3d696951d 100644 --- a/packages/utilities/src/primitives/string.ts +++ b/packages/utilities/src/primitives/string.ts @@ -31,3 +31,16 @@ export function concatStrings(list: string[], endAdornmentText: string): string } return result } + +export function containsNonPrintableChars(msg: string): boolean { + // Matches control characters and space separators. + 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 false +} diff --git a/packages/wallet/package.json b/packages/wallet/package.json index 1cd75b2f5ce..95a35ec4749 100644 --- a/packages/wallet/package.json +++ b/packages/wallet/package.json @@ -28,11 +28,11 @@ "@shopify/flash-list": "1.6.3", "@uniswap/analytics-events": "2.32.0", "@uniswap/permit2-sdk": "1.2.0", - "@uniswap/router-sdk": "1.8.0", - "@uniswap/sdk-core": "4.1.2", - "@uniswap/universal-router-sdk": "1.7.1", - "@uniswap/v2-sdk": "4.1.0", - "@uniswap/v3-sdk": "3.10.2", + "@uniswap/router-sdk": "1.9.0", + "@uniswap/sdk-core": "4.2.0", + "@uniswap/universal-router-sdk": "1.8.2", + "@uniswap/v2-sdk": "4.3.0", + "@uniswap/v3-sdk": "3.11.0", "apollo-link-rest": "0.9.0", "axios": "1.6.5", "dayjs": "1.11.7", @@ -53,7 +53,6 @@ "react-native-device-info": "10.0.2", "react-native-fast-image": "8.6.3", "react-native-gesture-handler": "2.15.0", - "react-native-image-colors": "1.5.2", "react-native-localize": "2.2.6", "react-native-reanimated": "3.3.0", "react-native-restart": "0.0.27", diff --git a/packages/wallet/src/components/CurrencyLogo/CurrencyLogo.tsx b/packages/wallet/src/components/CurrencyLogo/CurrencyLogo.tsx index 0fc63d7da23..8cad4e63a0d 100644 --- a/packages/wallet/src/components/CurrencyLogo/CurrencyLogo.tsx +++ b/packages/wallet/src/components/CurrencyLogo/CurrencyLogo.tsx @@ -1,6 +1,6 @@ import { iconSizes } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { TokenLogo } from 'wallet/src/components/CurrencyLogo/TokenLogo' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' interface CurrencyLogoProps { currencyInfo: Maybe diff --git a/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.tsx b/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.tsx index d60ebbed73a..36606a32e5a 100644 --- a/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.tsx +++ b/packages/wallet/src/components/CurrencyLogo/LogoWithTxStatus.tsx @@ -5,13 +5,13 @@ import { Flex, Icons, useSporeColors } from 'ui/src' import WalletConnectLogo from 'ui/src/assets/icons/walletconnect.svg' import MoonpayLogo from 'ui/src/assets/logos/svg/moonpay.svg' import { borderRadii } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { logger } from 'utilities/src/logger/logger' import { CurrencyLogo, STATUS_RATIO } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' import { TransactionSummaryNetworkLogo } from 'wallet/src/components/CurrencyLogo/NetworkLogo' import { DappIconPlaceholder } from 'wallet/src/components/WalletConnect/DappIconPlaceholder' import { ChainId } from 'wallet/src/constants/chains' import { AssetType } from 'wallet/src/entities/assets' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { ImageUri } from 'wallet/src/features/images/ImageUri' import { NFTViewer } from 'wallet/src/features/images/NFTViewer' import { RemoteImage } from 'wallet/src/features/images/RemoteImage' diff --git a/packages/wallet/src/components/CurrencyLogo/SplitLogo.tsx b/packages/wallet/src/components/CurrencyLogo/SplitLogo.tsx index 64156252ab5..06268f7247c 100644 --- a/packages/wallet/src/components/CurrencyLogo/SplitLogo.tsx +++ b/packages/wallet/src/components/CurrencyLogo/SplitLogo.tsx @@ -1,8 +1,8 @@ import { Flex } from 'ui/src' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { CurrencyLogo, STATUS_RATIO } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' import { TransactionSummaryNetworkLogo } from 'wallet/src/components/CurrencyLogo/NetworkLogo' import { ChainId } from 'wallet/src/constants/chains' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' interface Props { inputCurrencyInfo: Maybe diff --git a/packages/wallet/src/components/CurrencyLogo/TokenLogo.tsx b/packages/wallet/src/components/CurrencyLogo/TokenLogo.tsx index 07895c708c2..2019875e710 100644 --- a/packages/wallet/src/components/CurrencyLogo/TokenLogo.tsx +++ b/packages/wallet/src/components/CurrencyLogo/TokenLogo.tsx @@ -2,12 +2,12 @@ import { memo, useMemo } from 'react' import { Image } from 'react-native' import { Flex, Text, useIsDarkMode, useSporeColors } from 'ui/src' import { fonts, iconSizes, spacing } from 'ui/src/theme' +import { useLogolessColorScheme } from 'ui/src/utils/colors' import { isSVGUri, uriToHttp } from 'utilities/src/format/urls' import { STATUS_RATIO } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' import { THIN_BORDER, style } from 'wallet/src/components/CurrencyLogo/styles' import { ChainId } from 'wallet/src/constants/chains' import { RemoteSvg } from 'wallet/src/features/images/RemoteSvg' -import { useLogolessColorScheme } from 'wallet/src/utils/colors' import { NetworkLogo } from './NetworkLogo' interface TokenLogoProps { diff --git a/packages/wallet/src/components/RecipientSearch/hooks.ts b/packages/wallet/src/components/RecipientSearch/hooks.ts index 3651e06b7e2..97c45ef141c 100644 --- a/packages/wallet/src/components/RecipientSearch/hooks.ts +++ b/packages/wallet/src/components/RecipientSearch/hooks.ts @@ -6,6 +6,7 @@ import { SearchableRecipient } from 'wallet/src/features/address/types' import { uniqueAddressesOnly } from 'wallet/src/features/address/utils' import { useENS } from 'wallet/src/features/ens/useENS' import { selectWatchedAddressSet } from 'wallet/src/features/favorites/selectors' +import { DEFAULT_WATCHED_ADDRESSES } from 'wallet/src/features/favorites/slice' import { selectRecipientsByRecency } from 'wallet/src/features/transactions/selectors' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { selectInactiveAccounts } from 'wallet/src/features/wallet/selectors' @@ -139,6 +140,11 @@ export function useRecipients(): { const sections = useMemo(() => { const sectionsArr = [] + // Don't show default favorites as search result for recipient + for (const address of DEFAULT_WATCHED_ADDRESSES) { + watchedWallets.delete(address) + } + if (validatedAddressRecipients.length) { sectionsArr.push({ title: t('send.recipient.section.search'), diff --git a/packages/wallet/src/components/TokenSelector/SelectTokenButton.tsx b/packages/wallet/src/components/TokenSelector/SelectTokenButton.tsx index 5719aa0c7d2..b78ce30f09b 100644 --- a/packages/wallet/src/components/TokenSelector/SelectTokenButton.tsx +++ b/packages/wallet/src/components/TokenSelector/SelectTokenButton.tsx @@ -1,8 +1,8 @@ import { useTranslation } from 'react-i18next' import { Flex, Icons, Text, TouchableArea, isWeb } from 'ui/src' import { iconSizes } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { CurrencyLogo } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { getSymbolDisplayText } from 'wallet/src/utils/currency' interface SelectTokenButtonProps { diff --git a/packages/wallet/src/components/TokenSelector/TokenSelector.tsx b/packages/wallet/src/components/TokenSelector/TokenSelector.tsx index 7d32bca6cb2..f7f0a2f9dca 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSelector.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenSelector.tsx @@ -5,22 +5,22 @@ import { useTranslation } from 'react-i18next' import { Keyboard } from 'react-native' import { Flex, isWeb, useSporeColors } from 'ui/src' import { zIndices } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { Trace } from 'utilities/src/telemetry/trace/Trace' import { useDebounce } from 'utilities/src/time/timing' -import PasteButton from 'wallet/src/components/buttons/PasteButton' -import { useBottomSheetContext } from 'wallet/src/components/modals/BottomSheetContext' -import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' -import { NetworkFilter } from 'wallet/src/components/network/NetworkFilter' -import { useFilterCallbacks } from 'wallet/src/components/TokenSelector/hooks' import { TokenSelectorEmptySearchList } from 'wallet/src/components/TokenSelector/TokenSelectorEmptySearchList' import { TokenSelectorSearchResultsList } from 'wallet/src/components/TokenSelector/TokenSelectorSearchResultsList' import { TokenSelectorSendList } from 'wallet/src/components/TokenSelector/TokenSelectorSendList' 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 { useBottomSheetContext } from 'wallet/src/components/modals/BottomSheetContext' +import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' +import { NetworkFilter } from 'wallet/src/components/network/NetworkFilter' import { ChainId } from 'wallet/src/constants/chains' import { useWalletNavigation } from 'wallet/src/contexts/WalletNavigationContext' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { SearchContext } from 'wallet/src/features/search/SearchContext' import { SearchTextInput } from 'wallet/src/features/search/SearchTextInput' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' diff --git a/packages/wallet/src/components/TokenSelector/TokenSelectorEmptySearchList.tsx b/packages/wallet/src/components/TokenSelector/TokenSelectorEmptySearchList.tsx index 5bd15586d6e..8e7b4c174e3 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSelectorEmptySearchList.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenSelectorEmptySearchList.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next' import { Text, TouchableArea } from 'ui/src' 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 { TokenSelectorList } from 'wallet/src/components/TokenSelector/TokenSelectorList' import { OnSelectCurrency, @@ -10,7 +11,6 @@ import { TokenSection, } from 'wallet/src/components/TokenSelector/types' import { getTokenOptionsSection } from 'wallet/src/components/TokenSelector/utils' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' 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' diff --git a/packages/wallet/src/components/TokenSelector/TokenSelectorList.tsx b/packages/wallet/src/components/TokenSelector/TokenSelectorList.tsx index 35caf509cdd..5fcaab1dd93 100644 --- a/packages/wallet/src/components/TokenSelector/TokenSelectorList.tsx +++ b/packages/wallet/src/components/TokenSelector/TokenSelectorList.tsx @@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next' import { FadeIn, FadeOut } from 'react-native-reanimated' import { AnimatedFlex, Flex, isWeb, Loader, Skeleton, Text } from 'ui/src' import { fonts } from 'ui/src/theme' +import { CurrencyId } from 'uniswap/src/types/currency' import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard' import { useBottomSheetFocusHook } from 'wallet/src/components/modals/hooks' import { renderSuggestedTokenItem } from 'wallet/src/components/TokenSelector/renderSuggestedTokenItem' @@ -21,7 +22,6 @@ import { TokenSelectorListSections, } from 'wallet/src/components/TokenSelector/types' import { ChainId } from 'wallet/src/constants/chains' -import { CurrencyId } from 'wallet/src/utils/currencyId' function isSuggestedTokenItem(data: TokenOption | TokenOption[]): data is TokenOption[] { return Array.isArray(data) diff --git a/packages/wallet/src/components/TokenSelector/hooks.ts b/packages/wallet/src/components/TokenSelector/hooks.ts index a151f1b1ac4..c913ddf0803 100644 --- a/packages/wallet/src/components/TokenSelector/hooks.ts +++ b/packages/wallet/src/components/TokenSelector/hooks.ts @@ -1,5 +1,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react' import { GqlResult } from 'uniswap/src/data/types' +import { CurrencyInfo, PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { filter } from 'wallet/src/components/TokenSelector/filter' import { flowToModalName } from 'wallet/src/components/TokenSelector/flowToModalName' import { TokenOption } from 'wallet/src/components/TokenSelector/types' @@ -14,7 +15,6 @@ import { } from 'wallet/src/features/dataApi/balances' import { useTokenProjects } from 'wallet/src/features/dataApi/tokenProjects' import { usePopularTokens } from 'wallet/src/features/dataApi/topTokens' -import { CurrencyInfo, PortfolioBalance } from 'wallet/src/features/dataApi/types' 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' @@ -42,7 +42,11 @@ const baseCurrencyIds = [ ] export function useAllCommonBaseCurrencies(): GqlResult { - const { data: baseCurrencyInfos, loading, error, refetch } = useTokenProjects(baseCurrencyIds) + return useCurrencies(baseCurrencyIds) +} + +export function useCurrencies(currencyIds: string[]): GqlResult { + const { data: baseCurrencyInfos, loading, error, refetch } = useTokenProjects(currencyIds) const persistedError = usePersistedError(loading, error) // TokenProjects returns tokens on every network, so filter out native assets that have a diff --git a/packages/wallet/src/components/TokenSelector/types.ts b/packages/wallet/src/components/TokenSelector/types.ts index 961f5b02519..77620ac39bf 100644 --- a/packages/wallet/src/components/TokenSelector/types.ts +++ b/packages/wallet/src/components/TokenSelector/types.ts @@ -1,4 +1,4 @@ -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' export type TokenOption = { currencyInfo: CurrencyInfo diff --git a/packages/wallet/src/components/TokenSelector/utils.ts b/packages/wallet/src/components/TokenSelector/utils.ts index fde73601dc3..70349e8b4c6 100644 --- a/packages/wallet/src/components/TokenSelector/utils.ts +++ b/packages/wallet/src/components/TokenSelector/utils.ts @@ -1,6 +1,6 @@ +import { CurrencyInfo, PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { differenceWith } from 'utilities/src/primitives/array' import { TokenOption, TokenSection } from 'wallet/src/components/TokenSelector/types' -import { CurrencyInfo, PortfolioBalance } from 'wallet/src/features/dataApi/types' import { areCurrencyIdsEqual } from 'wallet/src/utils/currencyId' export function createEmptyBalanceOption(currencyInfo: CurrencyInfo): TokenOption { diff --git a/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.tsx b/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.tsx index 7617bd3f4d4..e07da769c7e 100644 --- a/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.tsx +++ b/packages/wallet/src/components/WalletPreviewCard/WalletPreviewCard.tsx @@ -1,10 +1,8 @@ import { Flex, Icons, Text, TouchableArea } from 'ui/src' import { iconSizes } from 'ui/src/theme' import { NumberType } from 'utilities/src/format/types' -import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon' -import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText' +import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' -import { useAvatar, useDisplayName } from 'wallet/src/features/wallet/hooks' import { ElementNameType } from 'wallet/src/telemetry/constants' interface Props { @@ -18,7 +16,6 @@ interface Props { } export const ADDRESS_WRAPPER_HEIGHT = 36 -const ICON_SIZE = 32 export default function WalletPreviewCard({ address, @@ -28,9 +25,6 @@ export default function WalletPreviewCard({ hideSelectionCircle, ...rest }: Props): JSX.Element { - const { avatar } = useAvatar(address) - - const displayName = useDisplayName(address, { showLocalName: true }) const { convertFiatAmountFormatted } = useLocalizationContext() const balanceFormatted = convertFiatAmountFormatted(balance, NumberType.FiatTokenQuantity) @@ -46,25 +40,17 @@ export default function WalletPreviewCard({ onPress={(): void => onSelect(address)} {...rest}> - - - - - {balance ? ( - - {balanceFormatted} - - ) : null} - + + + {Boolean(balance) && ( + + {balanceFormatted} + + )} + {!hideSelectionCircle && selected && ( + + )} - {!hideSelectionCircle && selected && ( - - )} ) 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 c40c5f9bbed..b0ba21e94bd 100644 --- a/packages/wallet/src/components/WalletPreviewCard/__snapshots__/WalletPreviewCard.test.tsx.snap +++ b/packages/wallet/src/components/WalletPreviewCard/__snapshots__/WalletPreviewCard.test.tsx.snap @@ -58,9 +58,8 @@ exports[`renders wallet preview card 1`] = ` { "alignItems": "center", "flexDirection": "row", + "flexShrink": 1, "gap": 12, - "height": 36, - "justifyContent": "flex-start", } } > @@ -96,42 +95,42 @@ exports[`renders wallet preview card 1`] = ` "borderTopLeftRadius": 999999, "borderTopRightRadius": 999999, "flexDirection": "column", - "height": 32, + "height": 36, "justifyContent": "center", "paddingBottom": 0, "paddingLeft": 0, "paddingRight": 0, "paddingTop": 0, - "width": 32, + "width": 36, } } >

@@ -289,6 +288,8 @@ exports[`renders wallet preview card 1`] = ` { "alignItems": "flex-start", "flexDirection": "column", + "flexShrink": 1, + "gap": 0, } } > @@ -297,99 +298,123 @@ exports[`renders wallet preview card 1`] = ` { "alignItems": "center", "flexDirection": "row", + "gap": 12, "justifyContent": "center", } } > - - 0x​82D5...3Fa6 - + + 0x​82D5...3Fa6 + + - - - + - - + propList={ + [ + "stroke", + "strokeWidth", + "strokeLinecap", + "strokeLinejoin", + ] + } + stroke={ + { + "type": 2, + } + } + strokeLinecap={1} + strokeLinejoin={1} + strokeWidth="2" + /> + + + `; diff --git a/packages/wallet/src/components/accounts/AccountDetails.tsx b/packages/wallet/src/components/accounts/AccountDetails.tsx index b2f02cb3cfe..bc69fb2c88d 100644 --- a/packages/wallet/src/components/accounts/AccountDetails.tsx +++ b/packages/wallet/src/components/accounts/AccountDetails.tsx @@ -1,4 +1,4 @@ -import { Flex, Icons, Text } from 'ui/src' +import { ColorTokens, Flex, Icons, Text } from 'ui/src' import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' import { shortenAddress } from 'wallet/src/utils/addresses' @@ -7,11 +7,13 @@ export function AccountDetails({ allowFontScaling = true, iconSize = 20, chevron = false, + chevronColor = '$neutral2', }: { address: string allowFontScaling?: boolean iconSize?: number chevron?: boolean + chevronColor?: ColorTokens | undefined }): JSX.Element { return ( @@ -31,7 +33,7 @@ export function AccountDetails({ {chevron && ( | null | undefined onSetMax: (amount: string) => void style?: StyleProp + currencyField: CurrencyField } export function MaxAmountButton({ @@ -18,6 +20,7 @@ export function MaxAmountButton({ currencyBalance, onSetMax, style, + currencyField, }: MaxAmountButtonProps): JSX.Element { const { t } = useTranslation() @@ -30,19 +33,32 @@ export function MaxAmountButton({ currencyAmount?.toExact() === maxInputAmount.toExact() const onPress = (event: GestureResponderEvent): void => { - event.stopPropagation() - - if (disableMaxButton) { - return + if (!disableMaxButton) { + event.stopPropagation() + onSetMax(maxInputAmount.toExact()) } - - onSetMax(maxInputAmount.toExact()) } return ( - - - + + + {t('swap.button.max')} diff --git a/packages/wallet/src/components/legacy/CurrencyInputPanelLegacy.tsx b/packages/wallet/src/components/legacy/CurrencyInputPanelLegacy.tsx index 4ffd9f0de30..74111808c93 100644 --- a/packages/wallet/src/components/legacy/CurrencyInputPanelLegacy.tsx +++ b/packages/wallet/src/components/legacy/CurrencyInputPanelLegacy.tsx @@ -9,13 +9,14 @@ import { } from 'react-native' import { Flex, FlexProps, SpaceTokens, Text } from 'ui/src' import { fonts } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { NumberType } from 'utilities/src/format/types' +import { SelectTokenButton } from 'wallet/src/components/TokenSelector/SelectTokenButton' import { AmountInput } from 'wallet/src/components/input/AmountInput' import { MaxAmountButton } from 'wallet/src/components/input/MaxAmountButton' -import { SelectTokenButton } from 'wallet/src/components/TokenSelector/SelectTokenButton' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { Warning, WarningLabel } from 'wallet/src/features/transactions/WarningModal/types' +import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { ElementName } from 'wallet/src/telemetry/constants' import { useDynamicFontSizing } from 'wallet/src/utils/useDynamicFontSizing' @@ -270,6 +271,7 @@ export function _CurrencyInputPanel(props: CurrentInputPanelProps): JSX.Element )} diff --git a/packages/wallet/src/components/text/RelativeChange.tsx b/packages/wallet/src/components/text/RelativeChange.tsx index fbdd8777c07..449bab00230 100644 --- a/packages/wallet/src/components/text/RelativeChange.tsx +++ b/packages/wallet/src/components/text/RelativeChange.tsx @@ -48,7 +48,8 @@ export function RelativeChange(props: RelativeChangeProps): JSX.Element { row alignItems="center" gap="$spacing2" - justifyContent={alignRight ? 'flex-end' : 'flex-start'}> + justifyContent={alignRight ? 'flex-end' : 'flex-start'} + testID="relative-change"> {change !== undefined && ( )} diff --git a/packages/wallet/src/components/text/__snapshots__/RelativeChange.test.tsx.snap b/packages/wallet/src/components/text/__snapshots__/RelativeChange.test.tsx.snap index b84a86a1162..4a6bcd4039d 100644 --- a/packages/wallet/src/components/text/__snapshots__/RelativeChange.test.tsx.snap +++ b/packages/wallet/src/components/text/__snapshots__/RelativeChange.test.tsx.snap @@ -10,6 +10,7 @@ exports[`renders a relative change 1`] = ` "justifyContent": "flex-start", } } + testID="relative-change" > @@ -14,3 +15,6 @@ export const selectHasCompletedUnitagsIntroModal = (state: SharedState): boolean export const selectHasViewedUniconV2IntroModal = (state: SharedState): boolean => state.behaviorHistory.hasViewedUniconV2IntroModal + +export const selectExtensionOnboardingState = (state: SharedState): ExtensionOnboardingState => + state.behaviorHistory.extensionOnboardingState diff --git a/packages/wallet/src/features/behaviorHistory/slice.ts b/packages/wallet/src/features/behaviorHistory/slice.ts index 0de337efc7a..8869b99a8ca 100644 --- a/packages/wallet/src/features/behaviorHistory/slice.ts +++ b/packages/wallet/src/features/behaviorHistory/slice.ts @@ -1,5 +1,11 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' +export enum ExtensionOnboardingState { + Undefined, // We'll query for the status at every app launch + ReadyToOnboard, // User ready to onboard, should see promo banner + Completed, // User has onboarded or dismissed call to action +} + /** * Used to store persisted info about a users interactions with UI. * We use this to show conditional UI, usually only for the first time a user views a new feature. @@ -10,6 +16,7 @@ export interface BehaviorHistoryState { hasSkippedUnitagPrompt: boolean hasCompletedUnitagsIntroModal: boolean hasViewedUniconV2IntroModal: boolean + extensionOnboardingState: ExtensionOnboardingState } export const initialBehaviorHistoryState: BehaviorHistoryState = { @@ -18,6 +25,7 @@ export const initialBehaviorHistoryState: BehaviorHistoryState = { hasSkippedUnitagPrompt: false, hasCompletedUnitagsIntroModal: false, hasViewedUniconV2IntroModal: false, + extensionOnboardingState: ExtensionOnboardingState.Undefined, } const slice = createSlice({ @@ -39,6 +47,9 @@ const slice = createSlice({ setHasViewedUniconV2IntroModal: (state, action: PayloadAction) => { state.hasViewedUniconV2IntroModal = action.payload }, + setExtensionOnboardingState: (state, action: PayloadAction) => { + state.extensionOnboardingState = action.payload + }, }, }) @@ -48,6 +59,7 @@ export const { setHasSkippedUnitagPrompt, setHasCompletedUnitagsIntroModal, setHasViewedUniconV2IntroModal, + setExtensionOnboardingState, } = slice.actions export const behaviorHistoryReducer = slice.reducer diff --git a/packages/wallet/src/features/chains/utils.test.ts b/packages/wallet/src/features/chains/utils.test.ts index 2c40db77f11..787faadc775 100644 --- a/packages/wallet/src/features/chains/utils.test.ts +++ b/packages/wallet/src/features/chains/utils.test.ts @@ -8,7 +8,6 @@ import { fromUniswapWebAppLink, getPollingIntervalByBlocktime, isTestnet, - toGraphQLChain, toSupportedChainId, toUniswapWebAppLink, } from 'wallet/src/features/chains/utils' @@ -51,16 +50,6 @@ describe(fromGraphQLChain, () => { }) }) -describe(toGraphQLChain, () => { - it('handles supported chain', () => { - expect(toGraphQLChain(ChainId.Mainnet)).toEqual(Chain.Ethereum) - }) - - it('handle unsupported chain', () => { - expect(toGraphQLChain(ChainId.PolygonMumbai)).toEqual(undefined) - }) -}) - describe(fromMoonpayNetwork, () => { it('handles supported chain', () => { expect(fromMoonpayNetwork(undefined)).toEqual(ChainId.Mainnet) diff --git a/packages/wallet/src/features/chains/utils.ts b/packages/wallet/src/features/chains/utils.ts index 81d753dc8f6..6f9226c5803 100644 --- a/packages/wallet/src/features/chains/utils.ts +++ b/packages/wallet/src/features/chains/utils.ts @@ -50,35 +50,11 @@ export function fromGraphQLChain(chain: Chain | undefined): ChainId | null { return ChainId.Base case Chain.Bnb: return ChainId.Bnb - case Chain.Blast: - return ChainId.Blast } return null } -export function toGraphQLChain(chainId: ChainId): Chain | undefined { - switch (chainId) { - case ChainId.Mainnet: - return Chain.Ethereum - case ChainId.ArbitrumOne: - return Chain.Arbitrum - case ChainId.Goerli: - return Chain.EthereumGoerli - case ChainId.Optimism: - return Chain.Optimism - case ChainId.Polygon: - return Chain.Polygon - case ChainId.Base: - return Chain.Base - case ChainId.Bnb: - return Chain.Bnb - case ChainId.Blast: - return Chain.Blast - } - return undefined -} - export function getPollingIntervalByBlocktime(chainId?: ChainId): PollingInterval { return isL2Chain(chainId) ? PollingInterval.LightningMcQueen : PollingInterval.Fast } @@ -119,8 +95,6 @@ export function fromUniswapWebAppLink(network: string | null): ChainId | null { return ChainId.Base case Chain.Bnb.toLowerCase(): return ChainId.Bnb - case Chain.Blast.toLowerCase(): - return ChainId.Blast default: throw new Error(`Network "${network}" can not be mapped`) } @@ -140,8 +114,6 @@ export function toUniswapWebAppLink(chainId: ChainId): string | null { return Chain.Base.toLowerCase() case ChainId.Bnb: return Chain.Bnb.toLowerCase() - case ChainId.Blast: - return Chain.Blast.toLowerCase() default: throw new Error(`ChainID "${chainId}" can not be mapped`) } diff --git a/packages/wallet/src/features/dataApi/balances.test.ts b/packages/wallet/src/features/dataApi/balances.test.ts index 44af8488afa..38311d8b465 100644 --- a/packages/wallet/src/features/dataApi/balances.test.ts +++ b/packages/wallet/src/features/dataApi/balances.test.ts @@ -5,7 +5,7 @@ import { Chain, PortfolioBalanceDocument, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { PortfolioBalance } from 'wallet/src/features/dataApi/types' +import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { FavoritesState, initialFavoritesState } from 'wallet/src/features/favorites/slice' import { WalletState, initialWalletState } from 'wallet/src/features/wallet/slice' import { diff --git a/packages/wallet/src/features/dataApi/balances.ts b/packages/wallet/src/features/dataApi/balances.ts index 3e588e8b5c9..b7064b3547e 100644 --- a/packages/wallet/src/features/dataApi/balances.ts +++ b/packages/wallet/src/features/dataApi/balances.ts @@ -9,10 +9,11 @@ import { usePortfolioBalancesQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GqlResult } from 'uniswap/src/data/types' +import { CurrencyInfo, PortfolioBalance } from 'uniswap/src/features/dataApi/types' +import { CurrencyId } from 'uniswap/src/types/currency' import { logger } from 'utilities/src/logger/logger' import { PollingInterval } from 'wallet/src/constants/misc' import { fromGraphQLChain } from 'wallet/src/features/chains/utils' -import { CurrencyInfo, PortfolioBalance } from 'wallet/src/features/dataApi/types' import { buildCurrency, currencyIdToContractInput, @@ -23,7 +24,7 @@ import { useHideSmallBalancesSetting, useHideSpamTokensSetting, } from 'wallet/src/features/wallet/hooks' -import { CurrencyId, currencyId } from 'wallet/src/utils/currencyId' +import { currencyId } from 'wallet/src/utils/currencyId' type SortedPortfolioBalances = { balances: PortfolioBalance[] diff --git a/packages/wallet/src/features/dataApi/searchTokens.ts b/packages/wallet/src/features/dataApi/searchTokens.ts index b633f009735..7e45d0e4185 100644 --- a/packages/wallet/src/features/dataApi/searchTokens.ts +++ b/packages/wallet/src/features/dataApi/searchTokens.ts @@ -4,9 +4,9 @@ import { 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' import { ChainId } from 'wallet/src/constants/chains' -import { toGraphQLChain } from 'wallet/src/features/chains/utils' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { gqlTokenToCurrencyInfo, usePersistedError } from 'wallet/src/features/dataApi/utils' export const ALL_GQL_CHAINS = Object.values(Chain) diff --git a/packages/wallet/src/features/dataApi/tokenProjects.ts b/packages/wallet/src/features/dataApi/tokenProjects.ts index fb6492ebad1..20f9ba7ca58 100644 --- a/packages/wallet/src/features/dataApi/tokenProjects.ts +++ b/packages/wallet/src/features/dataApi/tokenProjects.ts @@ -1,12 +1,12 @@ import { useCallback, useMemo } from 'react' import { useTokenProjectsQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GqlResult } from 'uniswap/src/data/types' -import { CurrencyInfo } from 'wallet/src/features/dataApi/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 { CurrencyId } from 'wallet/src/utils/currencyId' /** * Fetches token information as CurrencyInfo from currencyIds. When used, wrap component diff --git a/packages/wallet/src/features/dataApi/topTokens.ts b/packages/wallet/src/features/dataApi/topTokens.ts index dca835c2834..60c23958bfd 100644 --- a/packages/wallet/src/features/dataApi/topTokens.ts +++ b/packages/wallet/src/features/dataApi/topTokens.ts @@ -4,9 +4,9 @@ import { useTopTokensQuery, } 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' import { ChainId } from 'wallet/src/constants/chains' -import { toGraphQLChain } from 'wallet/src/features/chains/utils' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { gqlTokenToCurrencyInfo, usePersistedError } from 'wallet/src/features/dataApi/utils' export function usePopularTokens(chainFilter: ChainId): GqlResult { diff --git a/packages/wallet/src/features/dataApi/utils.test.ts b/packages/wallet/src/features/dataApi/utils.test.ts index 85213d020a7..91c7eee6793 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 { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { ChainId } from 'wallet/src/constants/chains' import { fromGraphQLChain } from 'wallet/src/features/chains/utils' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { SAMPLE_CURRENCY_ID_1, diff --git a/packages/wallet/src/features/dataApi/utils.ts b/packages/wallet/src/features/dataApi/utils.ts index f460a1742b9..a07a90249ee 100644 --- a/packages/wallet/src/features/dataApi/utils.ts +++ b/packages/wallet/src/features/dataApi/utils.ts @@ -8,17 +8,18 @@ import { TokenProjectsQuery, TopTokensQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { toGraphQLChain } from 'uniswap/src/features/chains/utils' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' +import { CurrencyId } from 'uniswap/src/types/currency' import { ChainId } from 'wallet/src/constants/chains' -import { fromGraphQLChain, toGraphQLChain } from 'wallet/src/features/chains/utils' +import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { - CurrencyId, currencyId, currencyIdToChain, currencyIdToGraphQLAddress, isNativeCurrencyAddress, } from 'wallet/src/utils/currencyId' -import { CurrencyInfo } from './types' type BuildCurrencyParams = { chainId?: Nullable diff --git a/packages/wallet/src/features/favorites/slice.ts b/packages/wallet/src/features/favorites/slice.ts index 641625c62fd..7ced412e2f1 100644 --- a/packages/wallet/src/features/favorites/slice.ts +++ b/packages/wallet/src/features/favorites/slice.ts @@ -1,9 +1,10 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { Ether } from '@uniswap/sdk-core' +import { CurrencyId } from 'uniswap/src/types/currency' import { logger } from 'utilities/src/logger/logger' import { ChainId } from 'wallet/src/constants/chains' import { WBTC } from 'wallet/src/constants/tokens' -import { CurrencyId, currencyId as idFromCurrency } from 'wallet/src/utils/currencyId' +import { currencyId as idFromCurrency } from 'wallet/src/utils/currencyId' export type Visibility = { isVisible: boolean } export type CurrencyIdToVisibility = Record @@ -22,10 +23,11 @@ const ETH_CURRENCY_ID = idFromCurrency(Ether.onChain(ChainId.Mainnet)).toLowerCa const VITALIK_ETH_ADDRESS = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' const HAYDEN_ETH_ADDRESS = '0x50EC05ADe8280758E2077fcBC08D878D4aef79C3' +export const DEFAULT_WATCHED_ADDRESSES = [VITALIK_ETH_ADDRESS, HAYDEN_ETH_ADDRESS] export const initialFavoritesState: FavoritesState = { tokens: [ETH_CURRENCY_ID, WBTC_CURRENCY_ID], - watchedAddresses: [VITALIK_ETH_ADDRESS, HAYDEN_ETH_ADDRESS], + watchedAddresses: DEFAULT_WATCHED_ADDRESSES, tokensVisibility: {}, nftsVisibility: {}, } diff --git a/packages/wallet/src/features/images/NFTViewer.tsx b/packages/wallet/src/features/images/NFTViewer.tsx index 5324f4ccffd..da96b4fe74a 100644 --- a/packages/wallet/src/features/images/NFTViewer.tsx +++ b/packages/wallet/src/features/images/NFTViewer.tsx @@ -2,7 +2,7 @@ import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { StyleSheet } from 'react-native' import { Flex, Text } from 'ui/src' -import { isSVGUri, uriToHttp } from 'utilities/src/format/urls' +import { isGifUri, isSVGUri, uriToHttp } from 'utilities/src/format/urls' import { NFTPreviewImage } from 'wallet/src/features/images/NFTPreviewImage' import { WebSvgUri } from 'wallet/src/features/images/WebSvgUri' import { ImageUri, ImageUriProps } from './ImageUri' @@ -75,7 +75,7 @@ export function NFTViewer(props: Props): JSX.Element { * TODO: Ideally we need to find a way to get compressed images without having to change * source in data response. */ - const isGif = imageHttpUri.includes('.gif') + const isGif = isGifUri(imageHttpUri) const formattedUri = isGif && limitGIFSize diff --git a/packages/wallet/src/features/notifications/types.ts b/packages/wallet/src/features/notifications/types.ts index b208d8ea6fc..a7d68ee4694 100644 --- a/packages/wallet/src/features/notifications/types.ts +++ b/packages/wallet/src/features/notifications/types.ts @@ -1,7 +1,7 @@ import { TradeType } from '@uniswap/sdk-core' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { ChainId } from 'wallet/src/constants/chains' import { AssetType } from 'wallet/src/entities/assets' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { FinalizedTransactionStatus, TransactionType, diff --git a/packages/wallet/src/features/portfolio/PortfolioBalance.tsx b/packages/wallet/src/features/portfolio/PortfolioBalance.tsx index 37354b162c0..677e2a01319 100644 --- a/packages/wallet/src/features/portfolio/PortfolioBalance.tsx +++ b/packages/wallet/src/features/portfolio/PortfolioBalance.tsx @@ -1,6 +1,8 @@ import { Flex, Icons, Shine, Skeleton, Text, isWeb, useSporeColors } from 'ui/src' +import { NumberType } from 'utilities/src/format/types' import { isWarmLoadingStatus } from 'wallet/src/data/utils' import { usePortfolioTotalValue } from 'wallet/src/features/dataApi/balances' +import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' type WalletBalanceProps = { address: Address } @@ -15,6 +17,7 @@ export function PortfolioBalance({ address }: WalletBalanceProps): JSX.Element { const isPositiveChange = change !== undefined ? change >= 0 : undefined const colors = useSporeColors() const arrowColor = isPositiveChange ? colors.statusSuccess : colors.statusCritical + const { convertFiatAmountFormatted } = useLocalizationContext() const formattedChange = change !== undefined ? `${Math.abs(change).toFixed(2)}%` : '-' return ( @@ -46,7 +49,9 @@ export function PortfolioBalance({ address }: WalletBalanceProps): JSX.Element { - ${balanceUSD?.toFixed(2)} + + {convertFiatAmountFormatted(balanceUSD, NumberType.FiatTokenDetails)} + ( @@ -69,6 +72,7 @@ export const SearchTextInput = forwardRef showShadow, value, hideIcon, + minHeight = DEFAULT_MIN_HEIGHT, } = props const inputRef = useRef(null) @@ -129,7 +133,7 @@ export const SearchTextInput = forwardRef backgroundColor={backgroundColor} borderRadius="$roundedFull" gap="$spacing8" - minHeight={48} + minHeight={minHeight} mr={showCancelButton && isFocus ? cancelButtonWidth + spacing.spacing12 : 0} px={px} py={py} diff --git a/packages/wallet/src/features/tokens/safetyHooks.ts b/packages/wallet/src/features/tokens/safetyHooks.ts index f4f87a98c8b..46759ede7d5 100644 --- a/packages/wallet/src/features/tokens/safetyHooks.ts +++ b/packages/wallet/src/features/tokens/safetyHooks.ts @@ -1,10 +1,10 @@ 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' import { useAppDispatch, useAppSelector } from 'wallet/src/state' -import { CurrencyId } from 'wallet/src/utils/currencyId' export function useTokenWarningDismissed(currencyId: Maybe): { tokenWarningDismissed: boolean // user dismissed warning diff --git a/packages/wallet/src/features/tokens/tokensSlice.ts b/packages/wallet/src/features/tokens/tokensSlice.ts index 97cb0481f39..e8b7757f231 100644 --- a/packages/wallet/src/features/tokens/tokensSlice.ts +++ b/packages/wallet/src/features/tokens/tokensSlice.ts @@ -1,6 +1,6 @@ // Shares similarities with https://github.com/Uniswap/interface/blob/main/src/state/user/reducer.ts import { createSlice, PayloadAction } from '@reduxjs/toolkit' -import { CurrencyId } from 'wallet/src/utils/currencyId' +import { CurrencyId } from 'uniswap/src/types/currency' export interface Tokens { dismissedWarningTokens: { diff --git a/packages/wallet/src/features/tokens/useCurrencyInfo.ts b/packages/wallet/src/features/tokens/useCurrencyInfo.ts index 5e18311d8c7..689e35cd7c2 100644 --- a/packages/wallet/src/features/tokens/useCurrencyInfo.ts +++ b/packages/wallet/src/features/tokens/useCurrencyInfo.ts @@ -1,7 +1,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 { ChainId } from 'wallet/src/constants/chains' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { currencyIdToContractInput, gqlTokenToCurrencyInfo, diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal.tsx index 1d350571280..63a24f11d12 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionActionsModal.tsx @@ -2,6 +2,7 @@ import dayjs from 'dayjs' import { useCallback, useMemo } from 'react' import { useTranslation } from 'react-i18next' import { ColorTokens, Flex, Separator, Text } from 'ui/src' +import { CurrencyId } from 'uniswap/src/types/currency' import { ActionSheetModalContent, MenuItemProp, @@ -20,7 +21,6 @@ import { import { useAppDispatch } from 'wallet/src/state' import { ElementName, ModalName } from 'wallet/src/telemetry/constants' import { setClipboard } from 'wallet/src/utils/clipboard' -import { CurrencyId } from 'wallet/src/utils/currencyId' import { openMoonpayHelpLink, openUniswapHelpLink } from 'wallet/src/utils/linking' function renderOptionItem(label: string, textColorOverride?: ColorTokens): () => JSX.Element { diff --git a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout.tsx b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout.tsx index 878ed097f1e..24922c356f0 100644 --- a/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout.tsx +++ b/packages/wallet/src/features/transactions/SummaryCards/SummaryItems/TransactionSummaryLayout.tsx @@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next' import { Flex, Text, TouchableArea, 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 { CurrencyId } from 'uniswap/src/types/currency' import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText' import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader' import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal' @@ -25,7 +26,6 @@ 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 { ModalName } from 'wallet/src/telemetry/constants' -import { CurrencyId } from 'wallet/src/utils/currencyId' import { openMoonpayTransactionLink, openTransactionLink } from 'wallet/src/utils/linking' const LOADING_SPINNER_SIZE = 20 diff --git a/packages/wallet/src/features/transactions/TransactionReview/TransactionReview.tsx b/packages/wallet/src/features/transactions/TransactionReview/TransactionReview.tsx index c72510dfa7e..2ed37882e91 100644 --- a/packages/wallet/src/features/transactions/TransactionReview/TransactionReview.tsx +++ b/packages/wallet/src/features/transactions/TransactionReview/TransactionReview.tsx @@ -2,18 +2,18 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { ReactNode } from 'react' import { useTranslation } from 'react-i18next' import { FadeInUp, FadeOut } from 'react-native-reanimated' -import { AnimatedFlex, Button, Flex, isWeb, Text, useDeviceDimensions, useMedia } from 'ui/src' +import { AnimatedFlex, Button, Flex, Text, isWeb, useDeviceDimensions, useMedia } from 'ui/src' import { BackArrow } from 'ui/src/components/icons/BackArrow' import { fonts, iconSizes } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { NumberType } from 'utilities/src/format/types' +import { CurrencyLogo } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' +import { NFTTransfer } from 'wallet/src/components/NFT/NFTTransfer' import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay' import { TransferArrowButton } from 'wallet/src/components/buttons/TransferArrowButton' -import { CurrencyLogo } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' import { AmountInput } from 'wallet/src/components/input/AmountInput' import { RecipientPrevTransfers } from 'wallet/src/components/input/RecipientInputPanel' import { TextInputProps } from 'wallet/src/components/input/TextInput' -import { NFTTransfer } from 'wallet/src/components/NFT/NFTTransfer' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { GQLNftAsset } from 'wallet/src/features/nfts/hooks' import { ElementName, ElementNameType } from 'wallet/src/telemetry/constants' diff --git a/packages/wallet/src/features/transactions/history/utils.ts b/packages/wallet/src/features/transactions/history/utils.ts index 6f9591da05d..495be8aafc0 100644 --- a/packages/wallet/src/features/transactions/history/utils.ts +++ b/packages/wallet/src/features/transactions/history/utils.ts @@ -8,6 +8,7 @@ import { TokenStandard, TransactionListQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { CurrencyId } from 'uniswap/src/types/currency' import { getNativeAddress } from 'wallet/src/constants/addresses' import { fromGraphQLChain } from 'wallet/src/features/chains/utils' import { CurrencyIdToVisibility } from 'wallet/src/features/favorites/slice' @@ -24,7 +25,7 @@ import { TransactionStatus, TransactionType, } from 'wallet/src/features/transactions/types' -import { CurrencyId, buildCurrencyId } from 'wallet/src/utils/currencyId' +import { buildCurrencyId } from 'wallet/src/utils/currencyId' import { ValueType, getCurrencyAmount } from 'wallet/src/utils/getCurrencyAmount' export interface AllFormattedTransactions { diff --git a/packages/wallet/src/features/transactions/hooks.ts b/packages/wallet/src/features/transactions/hooks.ts index 178808dfc2d..18c4590486f 100644 --- a/packages/wallet/src/features/transactions/hooks.ts +++ b/packages/wallet/src/features/transactions/hooks.ts @@ -21,6 +21,7 @@ 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( @@ -144,7 +145,7 @@ export function useMergeLocalAndRemoteTransactions( const remoteTxMap: Map = new Map() remoteTransactions.forEach((tx) => { if (tx.hash) { - const txHash = tx.hash.toLowerCase() + const txHash = ensureLeading0x(tx.hash.toLowerCase()) remoteTxMap.set(txHash, tx) txHashes.add(txHash) } else { @@ -155,7 +156,7 @@ export function useMergeLocalAndRemoteTransactions( const localTxMap: Map = new Map() localTransactions.forEach((tx) => { if (tx.hash) { - const txHash = tx.hash.toLowerCase() + const txHash = ensureLeading0x(tx.hash.toLowerCase()) localTxMap.set(txHash, tx) txHashes.add(txHash) } else { diff --git a/packages/wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts.tsx b/packages/wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts.tsx index 6d8730a7606..af50e0eed27 100644 --- a/packages/wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts.tsx +++ b/packages/wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts.tsx @@ -1,7 +1,7 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { useMemo } from 'react' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { NumberType } from 'utilities/src/format/types' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { useAppFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { getSymbolDisplayText } from 'wallet/src/utils/currency' diff --git a/packages/wallet/src/features/transactions/refetchGQLQueriesSaga.ts b/packages/wallet/src/features/transactions/refetchGQLQueriesSaga.ts index 6812f6d832d..1d43b74b5ab 100644 --- a/packages/wallet/src/features/transactions/refetchGQLQueriesSaga.ts +++ b/packages/wallet/src/features/transactions/refetchGQLQueriesSaga.ts @@ -5,6 +5,7 @@ import { PortfolioBalancesQuery, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' import { GQLQueries } from 'uniswap/src/data/graphql/uniswap-data-api/queries' +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' @@ -12,7 +13,6 @@ import { TransactionDetails, TransactionType } from 'wallet/src/features/transac import { sendWalletAnalyticsEvent } from 'wallet/src/telemetry' import { WalletEventName } from 'wallet/src/telemetry/constants' import { - CurrencyId, buildCurrencyId, buildNativeCurrencyId, buildWrappedNativeCurrencyId, diff --git a/packages/wallet/src/features/transactions/swap/CurrencyInputPanel.tsx b/packages/wallet/src/features/transactions/swap/CurrencyInputPanel.tsx index f85cc776a16..bae3e06960d 100644 --- a/packages/wallet/src/features/transactions/swap/CurrencyInputPanel.tsx +++ b/packages/wallet/src/features/transactions/swap/CurrencyInputPanel.tsx @@ -20,22 +20,22 @@ import { Flex, FlexProps, Icons, - isWeb, Text, TouchableArea, + isWeb, useSporeColors, } from 'ui/src' import { fonts } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { isDetoxBuild } from 'utilities/src/environment' import { NumberType } from 'utilities/src/format/types' import { useForwardRef, usePrevious } from 'utilities/src/react/hooks' -import { AmountInput } from 'wallet/src/components/input/AmountInput' import { SelectTokenButton } from 'wallet/src/components/TokenSelector/SelectTokenButton' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' +import { AmountInput } from 'wallet/src/components/input/AmountInput' +import { MaxAmountButton } from 'wallet/src/components/input/MaxAmountButton' import { useAppFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { useTokenAndFiatDisplayAmounts } from 'wallet/src/features/transactions/hooks/useTokenAndFiatDisplayAmounts' -import { MaxAmountButton } from 'wallet/src/features/transactions/swap/MaxAmountButton' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { ElementName } from 'wallet/src/telemetry/constants' import { useDynamicFontSizing } from 'wallet/src/utils/useDynamicFontSizing' @@ -205,6 +205,13 @@ export const CurrencyInputPanel = memo( const { symbol: fiatCurrencySymbol } = useAppFiatCurrencyInfo() + const handleSetMax = useCallback( + (amount: string) => { + onSetMax?.(amount, currencyField) + }, + [currencyField, onSetMax] + ) + // TODO: Remove this when fiat mode is ready to be integrated, to small for feature flag. const fiatModeFeatureEnabled = false return ( @@ -306,7 +313,12 @@ export const CurrencyInputPanel = memo( )} {showMaxButton && onSetMax && ( - + )} diff --git a/packages/wallet/src/features/transactions/swap/GasAndWarningRows.web.tsx b/packages/wallet/src/features/transactions/swap/GasAndWarningRows.web.tsx index 82132d1096a..ffbd26ffe5a 100644 --- a/packages/wallet/src/features/transactions/swap/GasAndWarningRows.web.tsx +++ b/packages/wallet/src/features/transactions/swap/GasAndWarningRows.web.tsx @@ -91,7 +91,7 @@ export function GasAndWarningRows({ {showGasFee && ( - + {gasFeeFormatted} diff --git a/packages/wallet/src/features/transactions/swap/MaxAmountButton.tsx b/packages/wallet/src/features/transactions/swap/MaxAmountButton.tsx deleted file mode 100644 index 913c719555d..00000000000 --- a/packages/wallet/src/features/transactions/swap/MaxAmountButton.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { useTranslation } from 'react-i18next' -import { GestureResponderEvent, StyleProp, ViewStyle } from 'react-native' -import { Text, TouchableArea } from 'ui/src' -import { Trace } from 'utilities/src/telemetry/trace/Trace' -import { useSwapFormContext } from 'wallet/src/features/transactions/contexts/SwapFormContext' -import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' -import { ElementName } from 'wallet/src/telemetry/constants' -import { maxAmountSpend } from 'wallet/src/utils/balance' - -interface MaxAmountButtonProps { - onSetMax: (amount: string, currencyField: CurrencyField) => void - style?: StyleProp - currencyField: CurrencyField -} - -export function MaxAmountButton({ - onSetMax, - style, - currencyField, -}: MaxAmountButtonProps): JSX.Element { - const { t } = useTranslation() - - const { derivedSwapInfo } = useSwapFormContext() - const { currencyBalances, currencyAmounts } = derivedSwapInfo - - // We always reference the input balance, as we calculate the max amount spend based on exact-in trade. - const inputCurrencyAmount = currencyAmounts[CurrencyField.INPUT] - const inputCurrencyBalance = currencyBalances[CurrencyField.INPUT] - - const maxInputAmount = maxAmountSpend(inputCurrencyBalance) - - // Disable max button if max already set or when balance is not sufficient - const disableMaxButton = - !maxInputAmount || - !maxInputAmount.greaterThan(0) || - inputCurrencyAmount?.toExact() === maxInputAmount.toExact() - - const onPress = (event: GestureResponderEvent): void => { - if (!disableMaxButton) { - event.stopPropagation() - onSetMax(maxInputAmount.toExact(), currencyField) - } - } - - return ( - - - - {t('swap.button.max')} - - - - ) -} diff --git a/packages/wallet/src/features/transactions/swap/SwapDetails.tsx b/packages/wallet/src/features/transactions/swap/SwapDetails.tsx index de86dd59aba..facd66b6708 100644 --- a/packages/wallet/src/features/transactions/swap/SwapDetails.tsx +++ b/packages/wallet/src/features/transactions/swap/SwapDetails.tsx @@ -2,9 +2,9 @@ import { Currency, TradeType } from '@uniswap/sdk-core' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import { Flex, Icons, Text, TouchableArea } from 'ui/src' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { NumberType } from 'utilities/src/format/types' import { Trace } from 'utilities/src/telemetry/trace/Trace' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { GasFeeResult } from 'wallet/src/features/gas/types' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { FeeOnTransferFeeGroupProps } from 'wallet/src/features/transactions/TransactionDetails/FeeOnTransferFee' diff --git a/packages/wallet/src/features/transactions/swap/TransactionAmountsReview.tsx b/packages/wallet/src/features/transactions/swap/TransactionAmountsReview.tsx index 990c222a133..da75533fdbf 100644 --- a/packages/wallet/src/features/transactions/swap/TransactionAmountsReview.tsx +++ b/packages/wallet/src/features/transactions/swap/TransactionAmountsReview.tsx @@ -1,9 +1,9 @@ import { useTranslation } from 'react-i18next' import { Button, Flex, Icons, Text, isWeb, useSporeColors } from 'ui/src' import { iconSizes } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { NumberType } from 'utilities/src/format/types' import { CurrencyLogo } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { DerivedSwapInfo } from 'wallet/src/features/transactions/swap/types' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' diff --git a/packages/wallet/src/features/transactions/swap/hooks/useSwapPrefilledState.ts b/packages/wallet/src/features/transactions/swap/hooks/useSwapPrefilledState.ts index 0973fb8129b..f4a9415f777 100644 --- a/packages/wallet/src/features/transactions/swap/hooks/useSwapPrefilledState.ts +++ b/packages/wallet/src/features/transactions/swap/hooks/useSwapPrefilledState.ts @@ -1,9 +1,13 @@ import { useMemo } from 'react' +import { getNativeAddress } from 'wallet/src/constants/addresses' +import { ChainId } from 'wallet/src/constants/chains' +import { AssetType, CurrencyAsset } from 'wallet/src/entities/assets' import { SwapFormState } from 'wallet/src/features/transactions/contexts/SwapFormContext' import { CurrencyField, TransactionState, } from 'wallet/src/features/transactions/transactionState/types' +import { areAddressesEqual } from 'wallet/src/utils/addresses' export function useSwapPrefilledState( initialState: TransactionState | undefined @@ -51,3 +55,38 @@ export function getFocusOnCurrencyFieldFromInitialState({ return undefined } + +export function getSwapPrefilledState({ + currencyAddress, + currencyChainId, + currencyField, +}: { + currencyAddress: Address + currencyChainId: ChainId + currencyField: CurrencyField +}): TransactionState { + const nativeTokenAddress = getNativeAddress(currencyChainId) + + const nativeToken: CurrencyAsset = { + address: nativeTokenAddress, + chainId: currencyChainId, + type: AssetType.Currency, + } + + const chosenToken: CurrencyAsset = { + address: currencyAddress, + chainId: currencyChainId, + type: AssetType.Currency, + } + + const opposedToken = areAddressesEqual(nativeTokenAddress, currencyAddress) ? null : nativeToken + + const swapFormState: TransactionState = { + exactCurrencyField: currencyField, + exactAmountToken: '', + [CurrencyField.INPUT]: currencyField === CurrencyField.INPUT ? chosenToken : opposedToken, + [CurrencyField.OUTPUT]: currencyField === CurrencyField.OUTPUT ? chosenToken : opposedToken, + } + + return swapFormState +} 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 2b77b41228c..6a3e9b9144c 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDCPrice.ts @@ -3,7 +3,6 @@ import { useMemo } from 'react' import { ChainId } from 'wallet/src/constants/chains' import { PollingInterval } from 'wallet/src/constants/misc' import { - USDB, USDBC_BASE, USDC, USDC_ARBITRUM, @@ -25,7 +24,6 @@ export const STABLECOIN_AMOUNT_OUT: { [chainId: number]: CurrencyAmount } [ChainId.Bnb]: CurrencyAmount.fromRawAmount(USDT_BNB, 10_000e6), [ChainId.Polygon]: CurrencyAmount.fromRawAmount(USDC_POLYGON, 10_000e6), [ChainId.Optimism]: CurrencyAmount.fromRawAmount(USDC_OPTIMISM, 10_000e6), - [ChainId.Blast]: CurrencyAmount.fromRawAmount(USDB, 10_000e18), } /** 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 c451e59a883..0b130e603bf 100644 --- a/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDTokenUpdater.ts +++ b/packages/wallet/src/features/transactions/swap/trade/hooks/useUSDTokenUpdater.ts @@ -68,7 +68,7 @@ export function useUSDTokenUpdater( const fiatPrice = parseFloat(usdPrice?.toExact() ?? '0') * conversionRate return dispatch( - updateExactAmountFiat({ amount: fiatPrice ? fiatPrice.toFixed(NUM_DECIMALS_DISPLAY) : '0' }) + updateExactAmountFiat({ amount: fiatPrice ? fiatPrice.toFixed(NUM_DECIMALS_DISPLAY) : '' }) ) }, [ dispatch, diff --git a/packages/wallet/src/features/transactions/swap/types.ts b/packages/wallet/src/features/transactions/swap/types.ts index 472916b1a56..4f3f13e0ba8 100644 --- a/packages/wallet/src/features/transactions/swap/types.ts +++ b/packages/wallet/src/features/transactions/swap/types.ts @@ -1,6 +1,6 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { ChainId } from 'wallet/src/constants/chains' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { useTrade } from 'wallet/src/features/transactions/swap/trade/legacy/hooks/useTrade' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { BaseDerivedInfo } from 'wallet/src/features/transactions/transfer/types' diff --git a/packages/wallet/src/features/transactions/swap/utils.ts b/packages/wallet/src/features/transactions/swap/utils.ts index 056c94436f8..f5a571db30d 100644 --- a/packages/wallet/src/features/transactions/swap/utils.ts +++ b/packages/wallet/src/features/transactions/swap/utils.ts @@ -8,6 +8,7 @@ import { import { FeeOptions } from '@uniswap/v3-sdk' import { BigNumber } from 'ethers' import { AppTFunction } from 'ui/src/i18n/types' +import { CurrencyId } from 'uniswap/src/types/currency' import { NumberType } from 'utilities/src/format/types' import { ChainId } from 'wallet/src/constants/chains' import { AssetType } from 'wallet/src/entities/assets' @@ -29,7 +30,6 @@ import { QuoteType } from 'wallet/src/features/transactions/utils' import { ElementName, ElementNameType } from 'wallet/src/telemetry/constants' import { getSymbolDisplayText } from 'wallet/src/utils/currency' import { - CurrencyId, areCurrencyIdsEqual, buildWrappedNativeCurrencyId, currencyId, diff --git a/packages/wallet/src/features/transactions/transactionWatcherSaga.ts b/packages/wallet/src/features/transactions/transactionWatcherSaga.ts index bd152946ea3..04c17332b62 100644 --- a/packages/wallet/src/features/transactions/transactionWatcherSaga.ts +++ b/packages/wallet/src/features/transactions/transactionWatcherSaga.ts @@ -200,9 +200,7 @@ export function* watchFiatOnRampTransaction(transaction: FiatOnRampTransactionDe // try again after a waiting period or when we've come back WebView yield* race({ forceFetch: take(forceFetchFiatOnRampTransactions), - timeout: delay( - useOldMoonpayIntegration ? PollingInterval.Normal : PollingInterval.KindaFast - ), + timeout: delay(useOldMoonpayIntegration ? PollingInterval.Normal : PollingInterval.Fast), }) } } catch (error) { diff --git a/packages/wallet/src/features/transactions/transfer/TokenSelectorPanel.tsx b/packages/wallet/src/features/transactions/transfer/TokenSelectorPanel.tsx index e1654c66f8e..51c66cccaca 100644 --- a/packages/wallet/src/features/transactions/transfer/TokenSelectorPanel.tsx +++ b/packages/wallet/src/features/transactions/transfer/TokenSelectorPanel.tsx @@ -2,6 +2,7 @@ import { Currency, CurrencyAmount } from '@uniswap/sdk-core' import { useTranslation } from 'react-i18next' import { Flex, Icons, Text, TouchableArea } from 'ui/src' import { iconSizes } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { NumberType } from 'utilities/src/format/types' import { CurrencyLogo } from 'wallet/src/components/CurrencyLogo/CurrencyLogo' import { @@ -9,7 +10,6 @@ import { TokenSelectorVariation, } from 'wallet/src/components/TokenSelector/TokenSelector' import { MaxAmountButton } from 'wallet/src/components/input/MaxAmountButton' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext' import { SearchContext } from 'wallet/src/features/search/SearchContext' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' @@ -84,6 +84,7 @@ export function TokenSelectorPanel({ )} diff --git a/packages/wallet/src/features/transactions/transfer/TransferAmountInput.tsx b/packages/wallet/src/features/transactions/transfer/TransferAmountInput.tsx index 022278acade..8c0a2e56974 100644 --- a/packages/wallet/src/features/transactions/transfer/TransferAmountInput.tsx +++ b/packages/wallet/src/features/transactions/transfer/TransferAmountInput.tsx @@ -3,8 +3,8 @@ import { useCallback, useEffect } from 'react' import { NativeSyntheticEvent, TextInputSelectionChangeEventData } from 'react-native' import { Flex, FlexProps, Icons, Text, TouchableArea } from 'ui/src' import { fonts } from 'ui/src/theme' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { AmountInput } from 'wallet/src/components/input/AmountInput' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { useAppFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks' import { WarningLabel } from 'wallet/src/features/transactions/WarningModal/types' import { ParsedWarnings } from 'wallet/src/features/transactions/hooks/useParsedTransactionWarnings' @@ -26,9 +26,9 @@ type TransferAmountInputProps = { onSelectionChange?: (start: number, end: number) => void } & FlexProps -const MAX_INPUT_FONT_SIZE = 70 +const MAX_INPUT_FONT_SIZE = 52 const MIN_INPUT_FONT_SIZE = 24 -const MAX_CHAR_PIXEL_WIDTH = 60 +const MAX_CHAR_PIXEL_WIDTH = 52 export function TransferAmountInput({ currencyInfo, @@ -114,7 +114,6 @@ export function TransferAmountInput({ {currencyInfo ? ( > => { return { + ...BASE_HEADERS, 'x-uni-sig': signature, - 'x-request-source': REQUEST_SOURCE, - 'x-app-version': getVersionHeader(), - Origin: uniswapUrls.apiBaseUrl, ...(firebaseAppCheckToken && { 'x-firebase-app-check': firebaseAppCheckToken }), } } @@ -181,3 +188,65 @@ export async function changeUnitag({ headers, }) } + +export async function fetchUnitagByAddresses(addresses: Address[]): Promise<{ + data?: { + [address: Address]: UnitagAddressResponse + } + error?: unknown +}> { + const unitagAddressesUrl = `${uniswapUrls.unitagsApiUrl}/addresses?addresses=${encodeURIComponent( + addresses.join(',') + )}` + + try { + const response = await axios.get(unitagAddressesUrl, { + headers: BASE_HEADERS, + }) + return { + data: response.data.usernames, + } + } catch (error) { + return { error } + } +} + +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(','))}` + + try { + const response = await axios.get(unitagWaitlistPositionUrl, { + headers: BASE_HEADERS, + }) + return { + data: response.data, + } + } catch (error) { + return { error } + } +} diff --git a/packages/wallet/src/features/unitags/hooks.ts b/packages/wallet/src/features/unitags/hooks.ts index 43d0cee7d47..58ec25246b0 100644 --- a/packages/wallet/src/features/unitags/hooks.ts +++ b/packages/wallet/src/features/unitags/hooks.ts @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next' import { getUniqueId } from 'react-native-device-info' import { FeatureFlags } from 'uniswap/src/features/experiments/flags' import { useFeatureFlag } from 'uniswap/src/features/experiments/hooks' -import { useUnitagQuery } from 'uniswap/src/features/unitags/api' +import { useUnitagQuery, useWaitlistPositionQuery } from 'uniswap/src/features/unitags/api' import { useUnitagUpdater } from 'uniswap/src/features/unitags/context' import { UseUnitagAddressResponse, @@ -23,6 +23,11 @@ import { useAsyncData } from 'utilities/src/react/hooks' import { ONE_SECOND_MS } from 'utilities/src/time/time' import { ChainId } from 'wallet/src/constants/chains' 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 { useENS } from 'wallet/src/features/ens/useENS' import { pushNotification } from 'wallet/src/features/notifications/slice' import { AppNotificationType } from 'wallet/src/features/notifications/types' @@ -40,15 +45,16 @@ import { UNITAG_VALID_REGEX, } from 'wallet/src/features/unitags/constants' import { parseUnitagErrorCode } from 'wallet/src/features/unitags/utils' -import { Account } from 'wallet/src/features/wallet/accounts/types' +import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { useWalletSigners } from 'wallet/src/features/wallet/context' import { useAccounts, + useActiveAccount, useActiveAccountAddressWithThrow, usePendingAccounts, } from 'wallet/src/features/wallet/hooks' import { SignerManager } from 'wallet/src/features/wallet/signing/SignerManager' -import { useAppDispatch } from 'wallet/src/state' +import { useAppDispatch, useAppSelector } from 'wallet/src/state' import { sendWalletAnalyticsEvent } from 'wallet/src/telemetry' import { UnitagEventName } from 'wallet/src/telemetry/constants' import { areAddressesEqual } from 'wallet/src/utils/addresses' @@ -307,3 +313,39 @@ export const useAvatarUploadCredsWithRefresh = ({ return { avatarUploadUrlLoading, avatarUploadUrlResponse } } + +export const useShowExtensionPromoBanner = (): { + error: string | undefined + loading: boolean + showExtensionPromoBanner: boolean +} => { + const dispatch = useAppDispatch() + const extensionOnboardingEnabled = useFeatureFlag(FeatureFlags.ExtensionOnboarding) + const extensionOnboardingState = useAppSelector(selectExtensionOnboardingState) + const activeAccount = useActiveAccount() + + const skip = + !extensionOnboardingEnabled || + extensionOnboardingState === ExtensionOnboardingState.Completed || + !activeAccount || + activeAccount.type !== AccountType.SignerMnemonic + + const { data, error, loading } = useWaitlistPositionQuery([activeAccount?.address || ''], skip) + + if (skip) { + return { + error: undefined, + loading: false, + showExtensionPromoBanner: false, + } + } + + const canOnboardToExtension = data?.isAccepted ?? false + + if (canOnboardToExtension && 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 } +} diff --git a/packages/wallet/src/features/wallet/hooks.ts b/packages/wallet/src/features/wallet/hooks.ts index 37c40353a16..39e49ca2223 100644 --- a/packages/wallet/src/features/wallet/hooks.ts +++ b/packages/wallet/src/features/wallet/hooks.ts @@ -113,6 +113,13 @@ export function useHideSpamTokensSetting(): boolean { return useAppSelector(selectWalletHideSpamTokensSetting) } +type DisplayNameOptions = { + showShortenedEns?: boolean + includeUnitagSuffix?: boolean + showLocalName?: boolean + overrideDisplayName?: string +} + /** * Displays the ENS name if one is available otherwise displays the local name and if neither are available it shows the address. * @@ -121,14 +128,6 @@ export function useHideSpamTokensSetting(): boolean { * @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 */ - -type DisplayNameOptions = { - showShortenedEns?: boolean - includeUnitagSuffix?: boolean - showLocalName?: boolean - overrideDisplayName?: string -} - export function useDisplayName( address: Maybe, options?: DisplayNameOptions diff --git a/packages/wallet/src/telemetry/constants.ts b/packages/wallet/src/telemetry/constants.ts index 7fb4ea6d0be..082afabaac1 100644 --- a/packages/wallet/src/telemetry/constants.ts +++ b/packages/wallet/src/telemetry/constants.ts @@ -21,6 +21,10 @@ export enum UnitagEventName { UnitagRemoved = 'Unitag Removed', } +export enum ExtensionOnboardingEventName { + PromoBannerActionTaken = 'Extension Promo Banner Action Taken', +} + export enum FiatOnRampEventName { FiatOnRampAmountEntered = 'Fiat OnRamp Amount Entered', FiatOnRampTransactionUpdated = 'Fiat OnRamp Transaction Updated', @@ -51,6 +55,8 @@ export const ModalName = { ExchangeTransferModal: 'exchange-transfer-modal', Experiments: 'experiments', Explore: 'explore-modal', + ExtensionPromoModal: 'extension-promo', + ExtensionWaitlistModal: 'extension-waitlist', FaceIDWarning: 'face-id-warning', FOTInfo: 'fee-on-transfer', FiatCurrencySelector: 'fiat-currency-selector', diff --git a/packages/wallet/src/telemetry/types.ts b/packages/wallet/src/telemetry/types.ts index 8b550b3605c..f80aa5dec47 100644 --- a/packages/wallet/src/telemetry/types.ts +++ b/packages/wallet/src/telemetry/types.ts @@ -11,6 +11,7 @@ import { ImportType } from 'wallet/src/features/onboarding/types' import { CurrencyField } from 'wallet/src/features/transactions/transactionState/types' import { QuoteType } from 'wallet/src/features/transactions/utils' import { + ExtensionOnboardingEventName, FiatOnRampEventName, InstitutionTransferEventName, UnitagEventName, @@ -95,6 +96,7 @@ export type WalletEventProperties = { } [FiatOnRampEventName.FiatOnRampWidgetOpened]: TraceProps & { countryCode: string + countryState?: string cryptoCurrency: string externalTransactionId: string fiatCurrency: string @@ -179,6 +181,9 @@ export type WalletEventProperties = { twitter: boolean } [UnitagEventName.UnitagRemoved]: undefined + [ExtensionOnboardingEventName.PromoBannerActionTaken]: { + action: 'join' | 'dismiss' + } } export type WalletAppsFlyerEventProperties = { diff --git a/packages/wallet/src/test/fixtures/gql/assets/tokens.ts b/packages/wallet/src/test/fixtures/gql/assets/tokens.ts index fe03843f3f5..c52034f71a5 100644 --- a/packages/wallet/src/test/fixtures/gql/assets/tokens.ts +++ b/packages/wallet/src/test/fixtures/gql/assets/tokens.ts @@ -11,14 +11,14 @@ import { TokenProject, TokenProjectMarket, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' -import { toGraphQLChain } from 'wallet/src/features/chains/utils' +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 { GQL_CHAINS } from 'wallet/src/test/fixtures/gql/misc' +import { GQL_CHAINS, image } from 'wallet/src/test/fixtures/gql/misc' import { DAI, ETH, @@ -98,15 +98,20 @@ export const tokenProjectMarket = createFixture()(() => ({ - __typename: 'TokenProject', - id: faker.datatype.uuid(), - name: faker.lorem.word(), - tokens: [] as Token[], - safetyLevel: randomEnumValue(SafetyLevel), - logoUrl: faker.image.imageUrl(), - isSpam: faker.datatype.boolean(), -})) +const tokenProjectBase = createFixture()(() => { + const logoUrl = faker.image.imageUrl() + return { + __typename: 'TokenProject', + id: faker.datatype.uuid(), + name: faker.lorem.word(), + tokens: [] as Token[], + safetyLevel: randomEnumValue(SafetyLevel), + // @deprecated + logoUrl, + isSpam: faker.datatype.boolean(), + logo: image({ url: logoUrl }), + } +}) type TokenProjectOptions = { priceHistory: (TimestampedAmount | undefined)[] diff --git a/packages/wallet/src/test/fixtures/wallet/balances.ts b/packages/wallet/src/test/fixtures/wallet/balances.ts index 7e2c98c1ba7..b1a25707053 100644 --- a/packages/wallet/src/test/fixtures/wallet/balances.ts +++ b/packages/wallet/src/test/fixtures/wallet/balances.ts @@ -3,8 +3,8 @@ import { Token, TokenBalance, } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { fromGraphQLChain } from 'wallet/src/features/chains/utils' -import { PortfolioBalance } from 'wallet/src/features/dataApi/types' 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' diff --git a/packages/wallet/src/test/fixtures/wallet/currencies.ts b/packages/wallet/src/test/fixtures/wallet/currencies.ts index ab5394df8a8..21c3cd1ff70 100644 --- a/packages/wallet/src/test/fixtures/wallet/currencies.ts +++ b/packages/wallet/src/test/fixtures/wallet/currencies.ts @@ -1,6 +1,6 @@ import { SafetyLevel } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks' +import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { ChainId } from 'wallet/src/constants/chains' -import { CurrencyInfo } from 'wallet/src/features/dataApi/types' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { faker } from 'wallet/src/test/shared' import { createFixture } from 'wallet/src/test/utils' diff --git a/packages/wallet/src/test/utils/wallet/balances.ts b/packages/wallet/src/test/utils/wallet/balances.ts index 0ef89b2644b..a37bd47b848 100644 --- a/packages/wallet/src/test/utils/wallet/balances.ts +++ b/packages/wallet/src/test/utils/wallet/balances.ts @@ -1,4 +1,4 @@ -import { PortfolioBalance } from 'wallet/src/features/dataApi/types' +import { PortfolioBalance } from 'uniswap/src/features/dataApi/types' import { portfolioBalances } from 'wallet/src/test/fixtures' export function portfolioBalancesById( diff --git a/packages/wallet/src/utils/balance.ts b/packages/wallet/src/utils/balance.ts index 0ea370a49a0..48fd9f34a64 100644 --- a/packages/wallet/src/utils/balance.ts +++ b/packages/wallet/src/utils/balance.ts @@ -23,12 +23,10 @@ export const MIN_ARBITRUM_FOR_GAS: JSBI = JSBI.multiply( export const MIN_OPTIMISM_FOR_GAS: JSBI = MIN_ARBITRUM_FOR_GAS -export const MIN_BASE_FOR_GAS: JSBI = MIN_ARBITRUM_FOR_GAS +export const MIN_BASE_FOR_GAS: JSBI = MIN_OPTIMISM_FOR_GAS export const MIN_BNB_FOR_GAS: JSBI = MIN_ARBITRUM_FOR_GAS -export const MIN_BLAST_FOR_GAS: JSBI = MIN_ARBITRUM_FOR_GAS - /** * Given some token amount, return the max that can be spent of it * https://github.com/Uniswap/interface/blob/main/src/utils/maxAmountSpend.ts @@ -64,9 +62,6 @@ export function maxAmountSpend( case ChainId.Bnb: minAmount = MIN_BNB_FOR_GAS break - case ChainId.Blast: - minAmount = MIN_BLAST_FOR_GAS - break default: return undefined } diff --git a/packages/wallet/src/utils/colors.tsx b/packages/wallet/src/utils/colors.tsx index 900e6222988..b86f62d79e8 100644 --- a/packages/wallet/src/utils/colors.tsx +++ b/packages/wallet/src/utils/colors.tsx @@ -1,11 +1,7 @@ -/* eslint-disable max-lines */ -import { useCallback, useEffect, useMemo, useState } from 'react' -import ImageColors from 'react-native-image-colors' -import { useIsDarkMode, useSporeColors } from 'ui/src' -import { ColorKeys, colors as GlobalColors, GlobalPalette, colorsLight } from 'ui/src/theme' +import { useMemo } from 'react' +import { useExtractedColors, useSporeColors } from 'ui/src' +import { colors as GlobalColors, GlobalPalette, colorsLight } from 'ui/src/theme' import { assert } from 'utilities/src/errors' -import { isSVGUri } from 'utilities/src/format/urls' -import { useAsyncData } from 'utilities/src/react/hooks' import { ChainId } from 'wallet/src/constants/chains' import { hex } from 'wcag-contrast' @@ -58,289 +54,6 @@ export function useNetworkColors(chainId: ChainId): { } } -export type ExtractedColors = { - primary?: string - secondary?: string - base?: string - detail?: string -} - -const specialCaseTokenColors: { [key: string]: string } = { - // old WBTC - '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', - // DAI - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x6B175474E89094C44Da98b954EedeAC495271d0F/logo.png': - '#FAB01B', - // UNI - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984/logo.png': - '#E6358C', - // BUSD - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4Fabb145d64652a948d72533023f6E7A623C7C53/logo.png': - '#EFBA09', - // AI-X - 'https://s2.coinmarketcap.com/static/img/coins/64x64/26984.png': '#29A1F1', - // 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', - // 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', - // USDC - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png': - '#0066D9', - // HEX - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png': - '#F93F8C', - // MONG - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1ce270557C1f68Cfb577b856766310Bf8B47FD9C/logo.png': - '#A96DFF', - // ARB - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1/logo.png': - '#29A1F1', - // PSYOP - 'https://s2.coinmarketcap.com/static/img/coins/64x64/25422.png': '#E88F00', - // MATIC - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0/logo.png': - '#A96DFF', - // TURBO - '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', - // SIMPSON - 'https://assets.coingecko.com/coins/images/30243/large/1111.png?1683692033': '#E88F00', - // MAKER - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2/logo.png': - '#50B197', - // 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', - // APE - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4d224452801ACEd8B2F0aebE155379bb5D594381/logo.png': - '#054AA9', - // GUSD - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd/logo.png': - '#00A4BD', - // OGN - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x8207c1FfC5B6804F6024322CcF34F29c3541Ae26/logo.png': - '#054AA9', - // RPL - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xD33526068D116cE69F19A9ee46F0bd304F21A51f/logo.png': - '#FF7B4F', -} - -const blackAndWhiteSpecialCase: Set = new Set([ - // QNT - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x4a220E6096B25EADb88358cb44068A3248254675/logo.png', - // Xen - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x06450dEe7FD2Fb8E39061434BAbCFC05599a6Fb8/logo.png', - // FWB - 'https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x35bD01FC9d6D5D81CA9E055Db88Dc49aa2c699A8/logo.png', -]) - -export function useExtractedColors( - imageUrl: Maybe, - fallback: ColorKeys = 'accent1', - cache = true -): { colors?: ExtractedColors; colorsLoading: boolean } { - const getImageColors = useCallback(async () => { - if (!imageUrl) { - return - } - - const imageColors = await ImageColors.getColors(imageUrl, { - key: imageUrl, - ...(fallback && { fallback }), - ...(cache && { cache }), - }) - - if (imageColors.platform === 'android') { - return { - primary: imageColors.dominant, - base: imageColors.average, - detail: imageColors.vibrant, - } - } - - if (imageColors.platform === 'ios') { - return { - primary: imageColors.primary, - secondary: imageColors.secondary, - base: imageColors.background, - detail: imageColors.detail, - } - } - }, [imageUrl, fallback, cache]) - - const { data: colors, isLoading: colorsLoading } = useAsyncData(getImageColors) - - return { colors, colorsLoading } -} - -function getSpecialCaseTokenColor(imageUrl: Maybe, isDarkMode: boolean): Nullable { - if (imageUrl && blackAndWhiteSpecialCase.has(imageUrl)) { - return isDarkMode ? '#FFFFFF' : '#000000' - } - - if (!imageUrl || !specialCaseTokenColors[imageUrl]) { - return null - } - - return specialCaseTokenColors[imageUrl] ?? null -} -/** - * Picks a contrast-passing color from a given token image URL and background color. - * The color extracting library will return a few options, and this function will - * try to pick the best of those given options. - * - * Usage: - * - * ```ts - * const { tokenColor, tokenColorLoading } = useExtractedTokenColor( - * tokenImageUrl, - * theme.colors.surface1, - * theme.colors.neutral3 - * ) - * ``` - * - * @param imageUrl The URL of the image to extract a color from - * @param tokenName The ticker of the asset (used to derive a color when no logo is available) - * @param backgroundColor The hex value of the background color to check contrast against - * @param defaultColor The color that will be returned while the extraction is still loading - * @returns The extracted color as a hex code string - */ -export function useExtractedTokenColor( - imageUrl: Maybe, - tokenName: Maybe, - backgroundColor: string, - defaultColor: string -): { tokenColor: Nullable; tokenColorLoading: boolean } { - const sporeColors = useSporeColors() - const { colors, colorsLoading } = useExtractedColors(imageUrl) - const [tokenColor, setTokenColor] = useState(defaultColor) - const [tokenColorLoading, setTokenColorLoading] = useState(true) - const isDarkMode = useIsDarkMode() - const logolessColorScheme = useLogolessColorScheme(tokenName ?? '') - - useEffect(() => { - if (!colorsLoading && !!colors) { - setTokenColor(pickContrastPassingColor(colors, backgroundColor)) - setTokenColorLoading(false) - } - }, [backgroundColor, colors, colorsLoading]) - - const specialCaseTokenColor = useMemo(() => { - return getSpecialCaseTokenColor(imageUrl, isDarkMode) - }, [imageUrl, isDarkMode]) - - if (specialCaseTokenColor) { - return { tokenColor: specialCaseTokenColor, tokenColorLoading: false } - } - - if (isSVGUri(imageUrl)) { - // Fall back to a more neutral color for SVG's since they fail extraction but we can render them elsewhere - return { tokenColor: sporeColors.neutral1?.val, tokenColorLoading: false } - } - - if (!imageUrl) { - const { foreground } = isDarkMode ? logolessColorScheme.dark : logolessColorScheme.light - return { tokenColor: foreground, tokenColorLoading: false } - } - - return { tokenColor, tokenColorLoading } -} - -enum LOGOLESS_COLORS { - PINK = 'PINK', - ORANGE = 'ORANGE', - YELLOW = 'YELLOW', - GREEN = 'GREEN', - TURQUOISE = 'TURQUOISE', - CYAN = 'CYAN', - BLUE = 'BLUE', - PURPLE = 'PURPLE', -} - -type ColorScheme = { - light: { foreground: string; background: string } - dark: { foreground: string; background: string } -} - -type LogolessColorSchemes = { - [key in LOGOLESS_COLORS]: ColorScheme -} - -const logolessColorSchemes: LogolessColorSchemes = { - // TODO (MOB-2417): update the colors in the global colors file to these and pull from there - [LOGOLESS_COLORS.PINK]: { - light: { foreground: '#FC74FE', background: '#FEF4FF' }, - dark: { foreground: '#FC74FE', background: '#361A37' }, - }, - [LOGOLESS_COLORS.ORANGE]: { - light: { foreground: '#FF7715', background: '#FFF2F1' }, - dark: { foreground: '#FF7715', background: '#2E0805' }, - }, - [LOGOLESS_COLORS.YELLOW]: { - light: { foreground: '#FFBF17', background: '#FFFCF2' }, - dark: { foreground: '#FFF612', background: '#1F1E02' }, - }, - [LOGOLESS_COLORS.GREEN]: { - light: { foreground: '#2FBA61', background: '#EEFBF1' }, - dark: { foreground: '#2FBA61', background: '#0F2C1A' }, - }, - [LOGOLESS_COLORS.TURQUOISE]: { - light: { foreground: '#00C3A0', background: '#F7FEEB' }, - dark: { foreground: '#5CFE9D', background: '#1A2A21' }, - }, - [LOGOLESS_COLORS.CYAN]: { - light: { foreground: '#2ABDFF', background: '#EBF8FF' }, - dark: { foreground: '#2ABDFF', background: '#15242B' }, - }, - [LOGOLESS_COLORS.BLUE]: { - light: { foreground: '#3271FF', background: '#EFF4FF' }, - dark: { foreground: '#3271FF', background: '#10143D' }, - }, - [LOGOLESS_COLORS.PURPLE]: { - light: { foreground: '#9E62FF', background: '#FAF5FF' }, - dark: { foreground: '#9E62FF', background: '#1A0040' }, - }, -} - -function getLogolessColorIndex(tokenName: string, numOptions: number): number { - const charCodes = Array.from(tokenName).map((char) => char.charCodeAt(0)) - const sum = charCodes.reduce((acc, curr) => acc + curr, 0) - return sum % numOptions -} - -/** - * Picks a color scheme for a token that doesn't have a logo. - * The color scheme is derived from the characters of the token name and will only change if the name changes - * @param tokenName The name of the token - * @returns a light and dark version of a color scheme with a foreground and background color - */ -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] - ] - }, [tokenName]) -} - /** * Picks a contrast-passing text color to put on top of a given background color. * The threshold right now is 3.0, which is the WCAG AA standard. @@ -357,56 +70,6 @@ export function getContrastPassingTextColor( return '$sporeBlack' } -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 - } - - const contrast = hex(color, backgroundColor) - return contrast >= contrastThreshold -} - -/** - * Picks a contrast-passing color from a given few that are returned from the color extraction library. - * The threshold right now is 1.95, which is a little bit less strict than when picking text to go on top - * of a color, because with the limitations of the color extraction library, a slightly lower threshold - * leads to better results right now. - * @param extractedColors An object of `background`, `primary`, `detail`, and `secondary` colors that - * the color extraction library returns for a given image URL - * @param backgroundHex The hex value of the background color to check the contrast of the resulting - * color against - * @returns a hex code that will pass a contrast check against the background - */ -function pickContrastPassingColor(extractedColors: ExtractedColors, backgroundHex: string): string { - const contrastThreshold = 1.95 - - const colorsInOrder = [ - extractedColors.base, - extractedColors.detail, - extractedColors.secondary, - extractedColors.primary, - ] as const - - // TODO(MOB-643): Define more robust color extraction logic. Some ideas: - // - compute all extracted colors and find the highest contrast one (that isn't #000000 or #FFFFFF) - // - bump color until it passes contrast: e.g. `import { lighten, desaturate } from 'polished'` - // - locally cache the result with the image logo URL as a key - // - move this logic to the backend - - for (const c of colorsInOrder) { - if (!!c && passesContrast(c, backgroundHex, contrastThreshold)) { - return c - } - } - - return colorsLight.accent1 -} - /** * @param uri image uri * @returns Extracts background color from image uri and finds closest theme colors. diff --git a/packages/wallet/src/utils/currencyId.ts b/packages/wallet/src/utils/currencyId.ts index 21d1d720b11..3793833a356 100644 --- a/packages/wallet/src/utils/currencyId.ts +++ b/packages/wallet/src/utils/currencyId.ts @@ -1,11 +1,10 @@ import { Currency } from '@uniswap/sdk-core' +import { CurrencyId } from 'uniswap/src/types/currency' import { getNativeAddress, getWrappedNativeAddress } from 'wallet/src/constants/addresses' import { ChainId, DEFAULT_NATIVE_ADDRESS } from 'wallet/src/constants/chains' import { toSupportedChainId } from 'wallet/src/features/chains/utils' import { areAddressesEqual } from './addresses' -export type CurrencyId = string - // swap router API special cases these strings to represent native currencies // all chains have "ETH" as native currency symbol except for polygon and bnb export enum SwapRouterNativeAssets { diff --git a/yarn.lock b/yarn.lock index b90f97b1985..52b21eee002 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13612,10 +13612,10 @@ __metadata: languageName: node linkType: hard -"@uniswap/default-token-list@npm:11.11.0, @uniswap/default-token-list@npm:^11.2.0": - version: 11.11.0 - resolution: "@uniswap/default-token-list@npm:11.11.0" - checksum: 10e5e528727e0cc075e526db01597533429f627a0a18a93a7b82692e361a6b7d822349c536a58f06363128abde0ecf55a734cf3f73e0af72f1e3c34d8ac96da4 +"@uniswap/default-token-list@npm:11.16.0, @uniswap/default-token-list@npm:^11.2.0": + version: 11.16.0 + resolution: "@uniswap/default-token-list@npm:11.16.0" + checksum: dd2debaf556ddb846debecb938b1550d59aef7dc4bc0957a4437fdfa2c21592a99133e59464bf1e76064571d364821bdd83633547558c5984843e1322f993aa7 languageName: node linkType: hard @@ -13747,25 +13747,25 @@ __metadata: "@types/xml2js": 0.4.14 "@uniswap/analytics": 1.7.0 "@uniswap/analytics-events": 2.32.0 - "@uniswap/default-token-list": 11.11.0 + "@uniswap/default-token-list": 11.16.0 "@uniswap/eslint-config": "workspace:^" "@uniswap/governance": 1.0.2 "@uniswap/liquidity-staker": 1.0.2 "@uniswap/merkle-distributor": 1.0.1 "@uniswap/permit2-sdk": 1.2.0 "@uniswap/redux-multicall": 1.1.8 - "@uniswap/router-sdk": 1.8.0 - "@uniswap/sdk-core": 4.1.2 + "@uniswap/router-sdk": 1.9.0 + "@uniswap/sdk-core": 4.2.0 "@uniswap/smart-order-router": 3.17.3 "@uniswap/token-lists": 1.0.0-beta.33 - "@uniswap/uniswapx-sdk": 1.4.1 - "@uniswap/universal-router-sdk": 1.7.1 + "@uniswap/uniswapx-sdk": 2.0.1-alpha.4 + "@uniswap/universal-router-sdk": 1.8.2 "@uniswap/v2-core": 1.0.1 "@uniswap/v2-periphery": 1.1.0-beta.0 - "@uniswap/v2-sdk": 4.1.0 + "@uniswap/v2-sdk": 4.3.0 "@uniswap/v3-core": 1.0.1 "@uniswap/v3-periphery": 1.4.4 - "@uniswap/v3-sdk": 3.10.2 + "@uniswap/v3-sdk": 3.11.0 "@vanilla-extract/css": 1.14.0 "@vanilla-extract/dynamic": 2.1.0 "@vanilla-extract/jest-transform": 1.1.1 @@ -13820,11 +13820,11 @@ __metadata: jest-fetch-mock: 3.0.3 jest-styled-components: 7.2.0 jotai: 1.3.7 - jpeg-js: 0.4.4 jsbi: 3.2.5 lightweight-charts: 4.1.1 lint-staged: ^14.0.0 localforage: 1.10.0 + madge: 6.1.0 make-plural: 7.0.0 mini-css-extract-plugin: ^2.7.6 ms: 2.1.3 @@ -13832,7 +13832,6 @@ __metadata: multihashes: 4.0.2 nock: 13.3.3 path-browserify: 1.0.1 - png-ts: 0.0.3 poisson-disk-sampling: 2.3.1 polished: 3.3.2 polyfill-object.fromentries: 1.0.1 @@ -13980,8 +13979,8 @@ __metadata: "@uniswap/analytics-events": 2.32.0 "@uniswap/eslint-config": "workspace:^" "@uniswap/ethers-rs-mobile": 0.0.5 - "@uniswap/sdk-core": 4.1.2 - "@uniswap/v3-sdk": 3.10.2 + "@uniswap/sdk-core": 4.2.0 + "@uniswap/v3-sdk": 3.11.0 "@walletconnect/core": 2.11.2 "@walletconnect/react-native-compat": 2.11.2 "@walletconnect/types": 2.11.2 @@ -14109,22 +14108,22 @@ __metadata: languageName: node linkType: hard -"@uniswap/router-sdk@npm:1.8.0, @uniswap/router-sdk@npm:^1.6.0, @uniswap/router-sdk@npm:^1.8.0": - version: 1.8.0 - resolution: "@uniswap/router-sdk@npm:1.8.0" +"@uniswap/router-sdk@npm:1.9.0, @uniswap/router-sdk@npm:^1.6.0, @uniswap/router-sdk@npm:^1.9.0": + version: 1.9.0 + resolution: "@uniswap/router-sdk@npm:1.9.0" dependencies: "@ethersproject/abi": ^5.5.0 - "@uniswap/sdk-core": ^4.0.7 + "@uniswap/sdk-core": ^4.2.0 "@uniswap/swap-router-contracts": ^1.1.0 - "@uniswap/v2-sdk": ^4.1.0 - "@uniswap/v3-sdk": ^3.10.1 - checksum: c885e4a2e42f62768a7fa1f7c5561d205b260975c728fb027537d5af4b08fea37435c6705d7bf2ae8242e25385afda876fce98de9afc689825ed60504a3a5550 + "@uniswap/v2-sdk": ^4.3.0 + "@uniswap/v3-sdk": ^3.11.0 + checksum: f2059c68deaee52968301b5f399b689240072f59019462b48fbd8baa7c15ed0a823f97b7460dbb0c87a7313d81044a1aee3267354981bc5072da307980739cd6 languageName: node linkType: hard -"@uniswap/sdk-core@npm:4.1.2": - version: 4.1.2 - resolution: "@uniswap/sdk-core@npm:4.1.2" +"@uniswap/sdk-core@npm:4.2.0": + version: 4.2.0 + resolution: "@uniswap/sdk-core@npm:4.2.0" dependencies: "@ethersproject/address": ^5.0.2 big.js: ^5.2.2 @@ -14132,7 +14131,7 @@ __metadata: jsbi: ^3.1.4 tiny-invariant: ^1.1.0 toformat: ^2.0.0 - checksum: 874bd4bae6c9df2d4b684bb233a1b2c4c558f03f97f0795a6a155c100da5220e3ba723895104174a153418ea4c3e760208313a24d94dbcdd17e6817f6c200a8e + checksum: fc38a3b9ab9b4984c4b2e011bf0883278563e327218a85482644af14a67452fb9d7d20e703ef5e3376e56a62e39df59db93d6161257460e9c9a1cf6a89dc3fae languageName: node linkType: hard @@ -14190,8 +14189,8 @@ __metadata: "@types/uuid": 9.0.1 "@uniswap/analytics": 1.7.0 "@uniswap/eslint-config": "workspace:^" - "@uniswap/universal-router-sdk": 1.7.1 - "@uniswap/v3-sdk": 3.10.2 + "@uniswap/universal-router-sdk": 1.8.2 + "@uniswap/v3-sdk": 3.11.0 clean-webpack-plugin: ^4.0.0 copy-webpack-plugin: ^11.0.0 dotenv-webpack: 8.0.1 @@ -14199,6 +14198,7 @@ __metadata: eslint: 8.44.0 eth-rpc-errors: 4.0.3 ethers: 5.7.2 + eventemitter3: 5.0.1 i18next: 23.10.0 jest: 29.7.0 jest-environment-jsdom: 29.5.0 @@ -14264,32 +14264,32 @@ __metadata: languageName: node linkType: hard -"@uniswap/uniswapx-sdk@npm:1.4.1": - version: 1.4.1 - resolution: "@uniswap/uniswapx-sdk@npm:1.4.1" +"@uniswap/uniswapx-sdk@npm:2.0.1-alpha.4": + version: 2.0.1-alpha.4 + resolution: "@uniswap/uniswapx-sdk@npm:2.0.1-alpha.4" dependencies: "@ethersproject/bytes": ^5.7.0 "@ethersproject/providers": ^5.7.0 "@uniswap/permit2-sdk": ^1.2.0 "@uniswap/sdk-core": ^4.0.3 ethers: ^5.7.0 - checksum: a8063730c5c588bf70118f231c34e1451237a19dabae2d462abdbaa650639b33318a4408490c80d9152542ccdc3a93a0e2eefaa7e386aeafef12f5f2c19e822f + checksum: 1f0a497e7277518cfcfb425f27e3b9cfd79bbad226bcffa7885f822e1c2a33e66c0412c7dd25c296cc966b881e63233636add2eba76751a30c7ba2a6cd9096cb languageName: node linkType: hard -"@uniswap/universal-router-sdk@npm:1.7.1, @uniswap/universal-router-sdk@npm:^1.5.4, @uniswap/universal-router-sdk@npm:^1.5.8": - version: 1.7.1 - resolution: "@uniswap/universal-router-sdk@npm:1.7.1" +"@uniswap/universal-router-sdk@npm:1.8.2, @uniswap/universal-router-sdk@npm:^1.5.4, @uniswap/universal-router-sdk@npm:^1.5.8": + version: 1.8.2 + resolution: "@uniswap/universal-router-sdk@npm:1.8.2" dependencies: "@uniswap/permit2-sdk": ^1.2.0 - "@uniswap/router-sdk": ^1.8.0 - "@uniswap/sdk-core": ^4.0.7 + "@uniswap/router-sdk": ^1.9.0 + "@uniswap/sdk-core": ^4.2.0 "@uniswap/universal-router": 1.6.0 - "@uniswap/v2-sdk": ^4.1.0 - "@uniswap/v3-sdk": ^3.10.1 + "@uniswap/v2-sdk": ^4.2.0 + "@uniswap/v3-sdk": ^3.11.0 bignumber.js: ^9.0.2 ethers: ^5.3.1 - checksum: 4b2036ac4f320c7b8aa107f5b4fad17c8e6bf3b56d22e0297085ad8742bd7277573250701d834fb5d79c5571f60b160f6895d22856cf616142b19a74469cd9ca + checksum: 87f7d9781ff22e71ecb38efb9cdf39408fed719c7fd6c301d33db0282b0f579b159fcd1b033bc3fb2aba92a551bafb33df2ae8aec7515524b8979c50b5d42b02 languageName: node linkType: hard @@ -14328,16 +14328,16 @@ __metadata: languageName: node linkType: hard -"@uniswap/v2-sdk@npm:4.1.0": - version: 4.1.0 - resolution: "@uniswap/v2-sdk@npm:4.1.0" +"@uniswap/v2-sdk@npm:4.3.0": + version: 4.3.0 + resolution: "@uniswap/v2-sdk@npm:4.3.0" dependencies: "@ethersproject/address": ^5.0.0 "@ethersproject/solidity": ^5.0.0 - "@uniswap/sdk-core": ^4.0.7 + "@uniswap/sdk-core": ^4.2.0 tiny-invariant: ^1.1.0 tiny-warning: ^1.0.3 - checksum: c2722fc800339fd0cd4c3aa9aa2b083e3f29ce6df15ddf0e6f671c378d50a9ec2d05035d80e9ecf6418aeb716a26c43e13f79b6e7c05931f324954ed4f44b0e7 + checksum: 1cc5a19486ec9832e674d642c2c715f8db9282ae4075df949a0667c7e1ba270c62c4bdd9d12a0698c44bfc7fc3e8352d2ccc97a800ab554e00938892405463ab languageName: node linkType: hard @@ -14368,19 +14368,19 @@ __metadata: languageName: node linkType: hard -"@uniswap/v3-sdk@npm:3.10.2, @uniswap/v3-sdk@npm:^3.10.0, @uniswap/v3-sdk@npm:^3.10.1": - version: 3.10.2 - resolution: "@uniswap/v3-sdk@npm:3.10.2" +"@uniswap/v3-sdk@npm:3.11.0, @uniswap/v3-sdk@npm:^3.10.0, @uniswap/v3-sdk@npm:^3.11.0": + version: 3.11.0 + resolution: "@uniswap/v3-sdk@npm:3.11.0" dependencies: "@ethersproject/abi": ^5.0.12 "@ethersproject/solidity": ^5.0.9 - "@uniswap/sdk-core": ^4.0.7 + "@uniswap/sdk-core": ^4.2.0 "@uniswap/swap-router-contracts": ^1.2.1 "@uniswap/v3-periphery": ^1.1.1 "@uniswap/v3-staker": 1.0.0 tiny-invariant: ^1.1.0 tiny-warning: ^1.0.3 - checksum: 187c34255fc082f8d424bd6e5faba825cd8dda01a9ae1d3a4377c4f11cec123562426afedff8c0513ad86f4458f37ee4ef8a186b86a087b8bd2736d74402cc92 + checksum: 35850a2f171ece5de27ab5066f983fdbc806b08b7a0db03c27b1fe4502176ab44136427f7b2a41c7476ec2b80398a9d74b5397f5d7bc35d174f888458e491a57 languageName: node linkType: hard @@ -24086,6 +24086,13 @@ __metadata: languageName: node linkType: hard +"eventemitter3@npm:5.0.1, eventemitter3@npm:^5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: 543d6c858ab699303c3c32e0f0f47fc64d360bf73c3daf0ac0b5079710e340d6fe9f15487f94e66c629f5f82cd1a8678d692f3dbb6f6fcd1190e1b97fcad36f8 + languageName: node + linkType: hard + "eventemitter3@npm:^3.1.0": version: 3.1.2 resolution: "eventemitter3@npm:3.1.2" @@ -24100,13 +24107,6 @@ __metadata: languageName: node linkType: hard -"eventemitter3@npm:^5.0.1": - version: 5.0.1 - resolution: "eventemitter3@npm:5.0.1" - checksum: 543d6c858ab699303c3c32e0f0f47fc64d360bf73c3daf0ac0b5079710e340d6fe9f15487f94e66c629f5f82cd1a8678d692f3dbb6f6fcd1190e1b97fcad36f8 - languageName: node - linkType: hard - "events@npm:3.3.0, events@npm:^3.2.0, events@npm:^3.3.0": version: 3.3.0 resolution: "events@npm:3.3.0" @@ -30517,10 +30517,10 @@ __metadata: languageName: node linkType: hard -"jpeg-js@npm:0.4.4": - version: 0.4.4 - resolution: "jpeg-js@npm:0.4.4" - checksum: bd7cb61aa8df40a9ee2c2106839c3df6054891e56cfc22c0ac581402e06c6295f962a4754b0b2ac50a401789131b1c6dc9df8d24400f1352168be1894833c590 +"jpeg-js@npm:0.4.2": + version: 0.4.2 + resolution: "jpeg-js@npm:0.4.2" + checksum: def900757c395e6376446aef5c13ed122cae1ab15308e84784d260c8d3c14d30c2092eaf32a4f57528d6e1f96a6b240a801e92122f00c9f3bb5d5c4d38df5bc0 languageName: node linkType: hard @@ -35466,7 +35466,7 @@ __metadata: languageName: node linkType: hard -"pako@npm:^1.0.5, pako@npm:^1.0.6, pako@npm:~1.0.5": +"pako@npm:^1.0.5, pako@npm:~1.0.5": version: 1.0.11 resolution: "pako@npm:1.0.11" checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16 @@ -36178,15 +36178,6 @@ __metadata: languageName: node linkType: hard -"png-ts@npm:0.0.3": - version: 0.0.3 - resolution: "png-ts@npm:0.0.3" - dependencies: - pako: ^1.0.6 - checksum: c1a87455975d419bd01ce1219707a506936ec78bdb60530ab559ffcb4e4da799dd636ae000a3ac0fe9c942c94b3fa1e6e7023d5b2a18e371264354cc87dc0307 - languageName: node - linkType: hard - "pngjs@npm:^3.0.0, pngjs@npm:^3.3.0, pngjs@npm:^3.3.3": version: 3.4.0 resolution: "pngjs@npm:3.4.0" @@ -44525,6 +44516,7 @@ __metadata: i18next: 23.10.0 react: 18.2.0 react-native: 0.71.13 + react-native-image-colors: 1.5.2 react-native-reanimated: 3.3.0 react-native-safe-area-context: 4.5.0 react-native-svg: 13.9.0 @@ -44532,6 +44524,7 @@ __metadata: typescript: 5.3.3 uppercamelcase: ^3.0.0 utilities: "workspace:^" + wcag-contrast: 3.0.0 languageName: unknown linkType: soft @@ -44834,6 +44827,7 @@ __metadata: "@graphql-codegen/typescript-resolvers": ^3.2.1 "@typechain/ethers-v5": 7.2.0 "@uniswap/eslint-config": "workspace:^" + "@uniswap/sdk-core": 4.2.0 apollo-link-rest: 0.9.0 depcheck: 1.4.7 eslint: 8.44.0 @@ -45378,7 +45372,7 @@ __metadata: "@uniswap/analytics": 1.7.0 "@uniswap/analytics-events": 2.32.0 "@uniswap/eslint-config": "workspace:^" - "@uniswap/sdk-core": 4.1.2 + "@uniswap/sdk-core": 4.2.0 aws-appsync-auth-link: 3.0.7 aws-appsync-subscription-link: 3.1.3 dayjs: 1.11.7 @@ -45896,11 +45890,11 @@ __metadata: "@uniswap/analytics-events": 2.32.0 "@uniswap/eslint-config": "workspace:^" "@uniswap/permit2-sdk": 1.2.0 - "@uniswap/router-sdk": 1.8.0 - "@uniswap/sdk-core": 4.1.2 - "@uniswap/universal-router-sdk": 1.7.1 - "@uniswap/v2-sdk": 4.1.0 - "@uniswap/v3-sdk": 3.10.2 + "@uniswap/router-sdk": 1.9.0 + "@uniswap/sdk-core": 4.2.0 + "@uniswap/universal-router-sdk": 1.8.2 + "@uniswap/v2-sdk": 4.3.0 + "@uniswap/v3-sdk": 3.11.0 apollo-link-rest: 0.9.0 axios: 1.6.5 dayjs: 1.11.7 @@ -45928,7 +45922,6 @@ __metadata: react-native-device-info: 10.0.2 react-native-fast-image: 8.6.3 react-native-gesture-handler: 2.15.0 - react-native-image-colors: 1.5.2 react-native-localize: 2.2.6 react-native-reanimated: 3.3.0 react-native-restart: 0.0.27

B+tfaMtB$8%b=Dh-~ZZd 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 literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/graphics/extension-promo-modal-light.png b/packages/ui/src/assets/graphics/extension-promo-modal-light.png new file mode 100644 index 0000000000000000000000000000000000000000..f79d14d1115de433590f32267a80831bed9395c2 GIT binary patch literal 319237 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