ci(release): publish latest release

parent c0bbcf59
IPFS hash of the deployment:
- CIDv0: `QmbEmY4ebbPrQirzx9PNZGKqyjK7pgUMVce2F2tMtJHbJ5`
- CIDv1: `bafybeif7uyvbcyhd5ib6xkikxoyvm4voodg4pgagjckny4m52o7qtclpsy`
- CIDv0: `QmRTQkA8KswGiGLBonZxttz4C9fJSnbD9QnKis37qVEJ2j`
- CIDv1: `bafybeibojzlcvbuydj5zkb562ipm3tcaa74sn4ub72dnwtse4diqma6gca`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
......@@ -10,51 +10,48 @@ You can also access the Uniswap Interface from an IPFS gateway.
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeif7uyvbcyhd5ib6xkikxoyvm4voodg4pgagjckny4m52o7qtclpsy.ipfs.dweb.link/
- https://bafybeif7uyvbcyhd5ib6xkikxoyvm4voodg4pgagjckny4m52o7qtclpsy.ipfs.cf-ipfs.com/
- [ipfs://QmbEmY4ebbPrQirzx9PNZGKqyjK7pgUMVce2F2tMtJHbJ5/](ipfs://QmbEmY4ebbPrQirzx9PNZGKqyjK7pgUMVce2F2tMtJHbJ5/)
- https://bafybeibojzlcvbuydj5zkb562ipm3tcaa74sn4ub72dnwtse4diqma6gca.ipfs.dweb.link/
- https://bafybeibojzlcvbuydj5zkb562ipm3tcaa74sn4ub72dnwtse4diqma6gca.ipfs.cf-ipfs.com/
- [ipfs://QmRTQkA8KswGiGLBonZxttz4C9fJSnbD9QnKis37qVEJ2j/](ipfs://QmRTQkA8KswGiGLBonZxttz4C9fJSnbD9QnKis37qVEJ2j/)
## 5.24.0 (2024-04-17)
## 5.25.0 (2024-04-22)
### Features
* **web:** [wagmi] Add viem to ethers adapters (#7237) 276faae
* **web:** deprecate token logo lookups (#6921) 0ade412
* **web:** deprecate+delete token safety lookups (#7132) b4642e9
* **web:** update signatures from subscription (#7389) f867870
* **web:** add check:bundle-size script and ci check for bundle size not growing (#7655) 0a4bce7
* **web:** Add feature flag for v2 data on Explore (#7705) 1679530
* **web:** combine all DoubleCurrencyLogo implementations (#7369) cda9384
* **web:** fix unicon v2 and remove jazzicons (#7586) 27caec6
* **web:** multiple routing preference (#7585) c6bfd6c
* **web:** multiple routing preferences styling (#7627) 58a55de
* **web:** remove all token-lists except avax fallbacks (#7422) 377fe4b
* **web:** remove remaining usage of token lists (#7446) 9895aa7
* **web:** subscription-based activity feed (#7266) 327da33
* **web:** update transactions from subscription (#7471) 49484c8
### Bug Fixes
* **web:** allow TrustWallet nodes in CSP (#7515) 0a5b28d
* **web:** change background color of LP warning banner (#7491) f543ab0
* **web:** change warning icon color; add learn more link (#7483) 352be3b
* **web:** checkbox color on open-limits drawer (#7410) 05dfaed
* **web:** dismiss chart tooltip when clicking outside of chart (#7285) 3d97a14
* **web:** ellipsis on unitag text in side drawer (#7386) 471661c
* **web:** fix cypress tests (#7347) e54ceb9
* **web:** fix icon cutoff (#7583) a01e106
* **web:** fix translations with JSX rendering as [object Object] (#7336) 67df76c
* **web:** fix uniswapx e2e tests (#7458) c6de35e
* **web:** fix x-chain token logos (#7374) 4367e3a
* **web:** functions pass-through (#7338) ace3062
* **web:** make sure SimpleToken hits the cache in all cases (#7488) 1be2954
* **web:** parse matic correctly from gql response (#7519) 800eae5
* **web:** remove assets repo fallback for all tokens (#7476) 8e2142c
* **web:** Send crashing on useENSAvatar while disconnected (#7595) 131c6a3
* **web:** set usePoolData errorPolicy to all (#7466) 55e3e8a
* **web:** SimpleTokenDetails fragment to be used in all queries (#7549) 678e624
* **web:** switch currency when input equals output (staging) 20277d2
* **web:** TokenBalanceProvider account change (#7432) 516b781
* **web:** use accent warning soft for outage banner icon wrapper (#7428) cda7ea9
* **web:** use cache-first policy for TopTokens query (#7484) 25d0950
### Code Refactoring
* **web:** isolate the subscription updater (#7387) 6caabbd
* **web:** split out parseRemote signature (#7388) 223e766
* **web:** use a single codepath for functions response transform (#7363) 60471a8
* **web:** add new chains to richlinkpreviews chainlogo (#7521) a21f010
* **web:** allow TrustWallet nodes in CSP (#7513) aed2bd4
* **web:** deflake some cypress tests (#7575) 4a7daef
* **web:** disable breaking tests and rename cypress passing test to be clearer in github branch protection rules (#7496) 0f88608
* **web:** Don't try to parse non-numbers (#7587) 5da785f
* **web:** fix token cacheing in explore tabs, navbar search (#7543) aaaaaa1
* **web:** fix token safety test on TDP (#7506) 4635506
* **web:** fix uk disclaimer e2e test (#7507) 1a8dbef
* **web:** fix uniswapx e2e tests (#7505) 880c131
* **web:** fix unitag e2e test (#7504) 8a946d5
* **web:** limits pending cancel state (#7654) 74a8767
* **web:** parse native MATIC correctly (#7516) a7d7957
* **web:** pass gas params to provider (#7464) 3ede664
* **web:** Properly compare natives for L2 PDP Tx Table (#7520) 4a1bb26
* **web:** remove 'review swap' title from success state (#7346) e16d7f3
* **web:** remove sentence-casing for rich link previews images (#7344) dff07a9
* **web:** Send crashing on useENSAvatar while disconnected (#7595) 56ba8b4
* **web:** switch currency when input equals output (#7570) 1f2fd97
* **web:** use cached results for common bases (#7649) ea1866d
* **web:** warn user if fee is greater than native balance (#7556) 100fce9
web/5.24.0
\ No newline at end of file
web/5.25.0
\ No newline at end of file
......@@ -11,7 +11,7 @@ import Apollo
public class Network {
public static let shared = Network()
private let UNISWAP_API_URL = Env.UNISWAP_API_BASE_URL + "/v1/graphql"
private let UNISWAP_API_URL = "https://ios.wallet.gateway.uniswap.org/v1/graphql"
public lazy var apollo: ApolloClient = {
let cache = InMemoryNormalizedCache()
......
......@@ -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.2.0",
"@uniswap/v3-sdk": "3.11.0",
"@uniswap/sdk-core": "4.2.1-beta.1",
"@uniswap/v3-sdk": "3.11.1-beta.2",
"@walletconnect/core": "2.11.2",
"@walletconnect/react-native-compat": "2.11.2",
"@walletconnect/utils": "2.11.2",
......@@ -163,14 +163,14 @@
"@babel/runtime": "7.18.9",
"@faker-js/faker": "7.6.0",
"@storybook/react": "7.0.2",
"@tamagui/babel-plugin": "1.94.3",
"@tamagui/babel-plugin": "1.94.5",
"@testing-library/react-hooks": "7.0.2",
"@testing-library/react-native": "11.5.0",
"@types/react-native": "0.71.3",
"@types/redux-mock-store": "1.0.6",
"@uniswap/eslint-config": "workspace:^",
"@walletconnect/types": "2.11.2",
"@welldone-software/why-did-you-render": "7.0.1",
"@welldone-software/why-did-you-render": "8.0.1",
"babel-loader": "8.2.3",
"babel-plugin-react-native-web": "0.17.5",
"babel-plugin-react-require": "4.0.0",
......
......@@ -3,7 +3,7 @@ import os
ENV_DEFAULTS_FILE = '../../.env.defaults'
ENV_DEFAULTS_LOCAL_FILE = '../../.env.defaults.local'
SWIFT_FILE_PATH = 'ios/WidgetsCore/Env.swift'
SWIFT_ENV_VARIABLES = ['UNISWAP_API_BASE_URL','UNISWAP_API_KEY']
SWIFT_ENV_VARIABLES = ['UNISWAP_API_KEY']
def to_swift_constant_line(key, value):
return f' static let {key.upper()} = "{value}"'
......
......@@ -49,10 +49,10 @@ import { StatsigProvider } from 'statsig-react-native'
import { flexStyles, useIsDarkMode } from 'ui/src'
import { config } from 'uniswap/src/config'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { DUMMY_STATSIG_SDK_KEY } from 'uniswap/src/features/statsig/constants'
import { WALLET_EXPERIMENTS } from 'uniswap/src/features/statsig/experiments'
import { WALLET_FEATURE_FLAG_NAMES } from 'uniswap/src/features/statsig/flags'
import { Statsig } from 'uniswap/src/features/statsig/sdk/statsig'
import { DUMMY_STATSIG_SDK_KEY } from 'uniswap/src/features/gating/constants'
import { WALLET_EXPERIMENTS } from 'uniswap/src/features/gating/experiments'
import { WALLET_FEATURE_FLAG_NAMES } from 'uniswap/src/features/gating/flags'
import { Statsig } from 'uniswap/src/features/gating/sdk/statsig'
import { UnitagUpdaterContextProvider } from 'uniswap/src/features/unitags/context'
import i18n from 'uniswap/src/i18n/i18n'
import { CurrencyId } from 'uniswap/src/types/currency'
......
......@@ -6,8 +6,8 @@ import { useAppStackNavigation } from 'src/app/navigation/types'
import { closeModal, openModal } from 'src/features/modals/modalSlice'
import { HomeScreenTabIndex } from 'src/screens/HomeScreenTabIndex'
import { Screens } from 'src/screens/Screens'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { logger } from 'utilities/src/logger/logger'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import {
......
......@@ -22,8 +22,6 @@ import {
useSporeColors,
} from 'ui/src'
import { spacing } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { isAndroid } from 'uniswap/src/utils/platform'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
import { ActionSheetModal, MenuItemProp } from 'wallet/src/components/modals/ActionSheetModal'
......@@ -76,7 +74,6 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme
const dispatch = useAppDispatch()
const hasImportedSeedPhrase = useNativeAccountExists()
const modalState = useAppSelector(selectModalState(ModalName.AccountSwitcher))
const unitagsFeatureFlagEnabled = useFeatureFlag(FeatureFlags.Unitags)
const onCompleteOnboarding = useCompleteOnboardingCallback({
entryPoint: OnboardingEntryPoint.Sidebar,
importType: hasImportedSeedPhrase ? ImportType.CreateAdditional : ImportType.CreateNew,
......@@ -132,25 +129,15 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme
dispatch(pendingAccountActions.trigger(PendingAccountActions.ActivateOneAndDelete))
dispatch(createAccountActions.trigger())
if (unitagsFeatureFlagEnabled) {
if (hasImportedSeedPhrase) {
setCreatedAdditionalAccount(true)
} else {
// create pending account and place into welcome flow
navigate(Screens.OnboardingStack, {
screen: OnboardingScreens.WelcomeWallet,
params: {
importType: ImportType.CreateNew,
entryPoint: OnboardingEntryPoint.Sidebar,
},
})
}
if (hasImportedSeedPhrase) {
setCreatedAdditionalAccount(true)
} else {
// create pending account and place into welcome flow
navigate(Screens.OnboardingStack, {
screen: OnboardingScreens.EditName,
screen: OnboardingScreens.WelcomeWallet,
params: {
importType: ImportType.CreateNew,
entryPoint: OnboardingEntryPoint.Sidebar,
importType: hasImportedSeedPhrase ? ImportType.CreateAdditional : ImportType.CreateNew,
},
})
}
......@@ -267,7 +254,7 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme
}
return options
}, [activeAccountAddress, dispatch, hasImportedSeedPhrase, onClose, t, unitagsFeatureFlagEnabled])
}, [activeAccountAddress, dispatch, hasImportedSeedPhrase, onClose, t])
const accountsWithoutActive = accounts.filter((a) => a.address !== activeAccountAddress)
......
......@@ -21,17 +21,17 @@ import {
Experiments,
WALLET_EXPERIMENTS,
getExperimentDefinition,
} from 'uniswap/src/features/statsig/experiments'
} from 'uniswap/src/features/gating/experiments'
import {
FeatureFlags,
WALLET_FEATURE_FLAG_NAMES,
getFeatureFlagName,
} from 'uniswap/src/features/statsig/flags'
} from 'uniswap/src/features/gating/flags'
import {
useExperimentValueWithExposureLoggingDisabled,
useFeatureFlagWithExposureLoggingDisabled,
} from 'uniswap/src/features/statsig/hooks'
import { Statsig } from 'uniswap/src/features/statsig/sdk/statsig'
} from 'uniswap/src/features/gating/hooks'
import { Statsig } from 'uniswap/src/features/gating/sdk/statsig'
import { Switch } from 'wallet/src/components/buttons/Switch'
import { TextInput } from 'wallet/src/components/input/TextInput'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
......
......@@ -5,8 +5,8 @@ import { selectModalState } from 'src/features/modals/selectModalState'
import { TransferFlow } from 'src/features/transactions/transfer/TransferFlow'
import { TransferFlow as TransferFlowRewrite } from 'src/features/transactions/transfer/transferRewrite/TransferFlow'
import { useSporeColors } from 'ui/src'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { ModalName } from 'wallet/src/telemetry/constants'
......
......@@ -41,7 +41,6 @@ import { BackupScreen } from 'src/screens/Onboarding/BackupScreen'
import { CloudBackupPasswordConfirmScreen } from 'src/screens/Onboarding/CloudBackupPasswordConfirmScreen'
import { CloudBackupPasswordCreateScreen } from 'src/screens/Onboarding/CloudBackupPasswordCreateScreen'
import { CloudBackupProcessingScreen } from 'src/screens/Onboarding/CloudBackupProcessingScreen'
import { EditNameScreen } from 'src/screens/Onboarding/EditNameScreen'
import { LandingScreen } from 'src/screens/Onboarding/LandingScreen'
import { ManualBackupScreen } from 'src/screens/Onboarding/ManualBackupScreen'
import { NotificationsSetupScreen } from 'src/screens/Onboarding/NotificationsSetupScreen'
......@@ -64,8 +63,8 @@ import { TokenDetailsScreen } from 'src/screens/TokenDetailsScreen'
import { WebViewScreen } from 'src/screens/WebViewScreen'
import { Icons, useDeviceInsets, useSporeColors } from 'ui/src'
import { spacing } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { OnboardingEntryPoint } from 'wallet/src/features/onboarding/types'
import { useActiveAccountWithThrow } from 'wallet/src/features/wallet/hooks'
import { selectFinishedOnboarding } from 'wallet/src/features/wallet/selectors'
......@@ -249,7 +248,6 @@ export function OnboardingStackNavigator(): JSX.Element {
name={UnitagScreens.ChooseProfilePicture}
options={{ ...TransitionPresets.ModalFadeTransition }}
/>
<OnboardingStack.Screen component={EditNameScreen} name={OnboardingScreens.EditName} />
<OnboardingStack.Screen component={BackupScreen} name={OnboardingScreens.Backup} />
<OnboardingStack.Screen
component={NotificationsSetupScreen}
......
......@@ -92,7 +92,6 @@ export type OnboardingStackParamList = {
[OnboardingScreens.BackupCloudProcessing]: CloudBackupFormParams & OnboardingStackBaseParams
[OnboardingScreens.Backup]: OnboardingStackBaseParams
[OnboardingScreens.Landing]: OnboardingStackBaseParams
[OnboardingScreens.EditName]: OnboardingStackBaseParams
[OnboardingScreens.Notifications]: OnboardingStackBaseParams
[OnboardingScreens.WelcomeWallet]: OnboardingStackBaseParams
[OnboardingScreens.Security]: OnboardingStackBaseParams
......
......@@ -10,7 +10,7 @@ import { logger } from 'utilities/src/logger/logger'
import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiatOnRamp/api'
import { importAccountSagaName } from 'wallet/src/features/wallet/import/importAccountSaga'
import { createStore } from 'wallet/src/state'
import { RootReducerNames } from 'wallet/src/state/reducer'
import { RootReducerNames, sharedPersistedStateWhitelist } from 'wallet/src/state/reducer'
import { MobileState, ReducerNames, mobileReducer } from './reducer'
import { mobileSaga } from './saga'
......@@ -54,21 +54,12 @@ const rtkQueryErrorLogger: Middleware = () => (next) => (action: PayloadAction<u
}
const whitelist: Array<ReducerNames | RootReducerNames> = [
'appearanceSettings',
'behaviorHistory',
...sharedPersistedStateWhitelist,
'biometricSettings',
'favorites',
'notifications',
'passwordLockout',
'searchHistory',
'telemetry',
'tokens',
'transactions',
'tweaks',
'wallet',
'cloudBackup',
'languageSettings',
'fiatCurrencySettings',
]
export const persistConfig = {
......
import React from 'react'
import { Flex, Separator, Text, Unicon, UniconV2, useSporeColors } from 'ui/src'
import Check from 'ui/src/assets/icons/check.svg'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { DisplayNameText } from 'wallet/src/components/accounts/DisplayNameText'
import { Account } from 'wallet/src/features/wallet/accounts/types'
import { useDisplayName } from 'wallet/src/features/wallet/hooks'
......
......@@ -25,8 +25,8 @@ import { Flex, HapticFeedback, Text, TouchableArea, useIsDarkMode, useSporeColor
import Scan from 'ui/src/assets/icons/receive.svg'
import ScanQRIcon from 'ui/src/assets/icons/scan.svg'
import { iconSizes } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { logger } from 'utilities/src/logger/logger'
import { WalletQRCode } from 'wallet/src/components/QRCodeScanner/WalletQRCode'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
......
......@@ -6,8 +6,8 @@ import {
UNISWAP_URL_SCHEME_WALLETCONNECT_AS_PARAM,
UNISWAP_WALLETCONNECT_URL,
} from 'src/features/deepLinking/constants'
import { DynamicConfigs } from 'uniswap/src/features/statsig/configs'
import { useDynamicConfig } from 'uniswap/src/features/statsig/hooks'
import { DynamicConfigs } from 'uniswap/src/features/gating/configs'
import { useDynamicConfig } from 'uniswap/src/features/gating/hooks'
import { logger } from 'utilities/src/logger/logger'
import { RPCType } from 'wallet/src/constants/chains'
import { AssetType } from 'wallet/src/entities/assets'
......
......@@ -5,8 +5,8 @@ import { openModal } from 'src/features/modals/modalSlice'
import { setUserProperty } from 'src/features/telemetry'
import { UserPropertyName } from 'src/features/telemetry/constants'
import { Screens } from 'src/screens/Screens'
import { isDevBuild } from 'src/utils/version'
import { Flex, HapticFeedback, Icons, ImpactFeedbackStyle, Text, TouchableArea } from 'ui/src'
import { isDevEnv } from 'uniswap/src/utils/env'
import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon'
import { AnimatedUnitagDisplayName } from 'wallet/src/components/accounts/AnimatedUnitagDisplayName'
import { pushNotification } from 'wallet/src/features/notifications/slice'
......@@ -87,7 +87,7 @@ export function AccountHeader(): JSX.Element {
hitSlop={20}
testID={ElementName.Manage}
onLongPress={async (): Promise<void> => {
if (isDevBuild()) {
if (isDevEnv()) {
await HapticFeedback.selection()
dispatch(openModal({ name: ModalName.Experiments }))
}
......
......@@ -3,8 +3,8 @@ 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 unitagHooks from 'uniswap/src/features/unitags/hooks'
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,
......
import React, { useMemo } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { FlatList } from 'react-native'
import { FadeIn, FadeOut } from 'react-native-reanimated'
......@@ -10,14 +10,8 @@ import { SectionHeaderText } from 'src/components/explore/search/SearchSectionHe
import { AnimatedFlex, Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import ClockIcon from 'ui/src/assets/icons/clock.svg'
import { iconSizes } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { clearSearchHistory } from 'wallet/src/features/search/searchHistorySlice'
import {
SearchResult,
SearchResultType,
WalletSearchResult,
} from 'wallet/src/features/search/SearchResult'
import { SearchResultType, WalletSearchResult } from 'wallet/src/features/search/SearchResult'
import { selectSearchHistory } from 'wallet/src/features/search/selectSearchHistory'
const TrendUpIcon = <Icons.TrendUp color="$neutral2" size="$icon.24" />
......@@ -39,28 +33,11 @@ export function SearchEmptySection(): JSX.Element {
const { t } = useTranslation()
const dispatch = useAppDispatch()
const searchHistory = useAppSelector(selectSearchHistory)
const unitagFeatureFlagEnabled = useFeatureFlag(FeatureFlags.Unitags)
const onPressClearSearchHistory = (): void => {
dispatch(clearSearchHistory())
}
const modifiedHistory: SearchResult[] = useMemo(
() =>
searchHistory.map((historyItem: SearchResult) => {
if (!unitagFeatureFlagEnabled && historyItem.type === SearchResultType.Unitag) {
return {
type: SearchResultType.WalletByAddress,
address: historyItem.address,
searchId: historyItem.searchId,
}
} else {
return historyItem
}
}),
[searchHistory, unitagFeatureFlagEnabled]
)
// Show search history (if applicable), trending tokens, and wallets
return (
<AnimatedFlex entering={FadeIn} exiting={FadeOut} gap="$spacing12" pb="$spacing36">
......@@ -85,7 +62,7 @@ export function SearchEmptySection(): JSX.Element {
</TouchableArea>
</Flex>
}
data={modifiedHistory}
data={searchHistory}
renderItem={(props): JSX.Element | null =>
renderSearchItem({ ...props, searchContext: { isHistory: true } })
}
......
import { useMemo } from 'react'
import { useUnitagByAddress, useUnitagByName } from 'uniswap/src/features/unitags/hooks'
import { ChainId } from 'wallet/src/constants/chains'
import { useENS } from 'wallet/src/features/ens/useENS'
import { SearchResultType, WalletSearchResult } from 'wallet/src/features/search/SearchResult'
import { useIsSmartContractAddress } from 'wallet/src/features/transactions/transfer/hooks/useIsSmartContractAddress'
import { useUnitagByAddress, useUnitagByName } from 'wallet/src/features/unitags/hooks'
import { getValidAddress } from 'wallet/src/utils/addresses'
// eslint-disable-next-line complexity
......
......@@ -6,7 +6,7 @@ import { Flex, Text, TouchableArea, useIsDarkMode } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { concatStrings } from 'utilities/src/primitives/string'
import { FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types'
import { getServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils'
import { getOptionalServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils'
import { ImageUri } from 'wallet/src/features/images/ImageUri'
function LogoLoader(): JSX.Element {
......@@ -24,7 +24,7 @@ export function FORQuoteItem({
}): JSX.Element | null {
const { t } = useTranslation()
const isDarkMode = useIsDarkMode()
const logoUrl = getServiceProviderLogo(serviceProvider?.logos, isDarkMode)
const logoUrl = getOptionalServiceProviderLogo(serviceProvider?.logos, isDarkMode)
if (!serviceProvider) {
return null
......
......@@ -5,8 +5,8 @@ import { SeedPhraseDisplay } from 'src/components/mnemonic/SeedPhraseDisplay'
import { APP_STORE_LINK } from 'src/constants/urls'
import { UpgradeStatus } from 'src/features/forceUpgrade/types'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import { DynamicConfigs } from 'uniswap/src/features/statsig/configs'
import { useDynamicConfig } from 'uniswap/src/features/statsig/hooks'
import { DynamicConfigs } from 'uniswap/src/features/gating/configs'
import { useDynamicConfig } from 'uniswap/src/features/gating/hooks'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { WarningModal } from 'wallet/src/components/modals/WarningModal/WarningModal'
import { WarningSeverity } from 'wallet/src/features/transactions/WarningModal/types'
......
......@@ -2,13 +2,13 @@ import { FlashList } from '@shopify/flash-list'
import React, { forwardRef, memo, useCallback, useMemo } from 'react'
import { RefreshControl } from 'react-native'
import { useAppStackNavigation } from 'src/app/navigation/types'
import { NftView } from 'src/components/NFT/NftView'
import { useAdaptiveFooter } from 'src/components/home/hooks'
import { TAB_BAR_HEIGHT, TabProps } from 'src/components/layout/TabHelpers'
import { Screens } from 'src/screens/Screens'
import { Flex, useDeviceInsets, useSporeColors } from 'ui/src'
import { GQLQueries } from 'uniswap/src/data/graphql/uniswap-data-api/queries'
import { isAndroid } from 'uniswap/src/utils/platform'
import { NftView } from 'wallet/src/components/nfts/NftView'
import { NftsList } from 'wallet/src/components/nfts/NftsList'
import { NFTItem } from 'wallet/src/features/nfts/types'
......
......@@ -6,8 +6,8 @@ import { openModal } from 'src/features/modals/modalSlice'
import { Flex, Icons, Text, TouchableArea } from 'ui/src'
import PaperStackIcon from 'ui/src/assets/icons/paper-stack.svg'
import { iconSizes, colors as rawColors } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { AccountType } from 'wallet/src/features/wallet/accounts/types'
import { useActiveAccount } from 'wallet/src/features/wallet/hooks'
......
import { NftView } from 'src/components/NFT/NftView'
import { useDeviceInsets, useSporeColors } from 'ui/src'
import { spacing } from 'ui/src/theme'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { NftView } from 'wallet/src/components/nfts/NftView'
import { NftsList } from 'wallet/src/components/nfts/NftsList'
import { NFTItem } from 'wallet/src/features/nfts/types'
import { ModalName } from 'wallet/src/telemetry/constants'
......
......@@ -7,9 +7,6 @@ import { initAndPersistCache } from 'src/data/cache'
import { sendMobileAnalyticsEvent } from 'src/features/telemetry'
import { MobileEventName } from 'src/features/telemetry/constants'
import { selectCustomEndpoint } from 'src/features/tweaks/selectors'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { isNonJestDev } from 'utilities/src/environment'
import { logger } from 'utilities/src/logger/logger'
import { useAsyncData } from 'utilities/src/react/hooks'
......@@ -72,7 +69,6 @@ if (isNonJestDev) {
export const usePersistedApolloClient = (): ApolloClient<NormalizedCacheObject> | undefined => {
const [client, setClient] = useState<ApolloClient<NormalizedCacheObject>>()
const customEndpoint = useAppSelector(selectCustomEndpoint)
const cloudflareGatewayEnabled = useFeatureFlag(FeatureFlags.GatewayDNSUpdateMobile)
const apolloLink = customEndpoint
? getCustomGraphqlHttpLink(customEndpoint)
......@@ -90,9 +86,7 @@ export const usePersistedApolloClient = (): ApolloClient<NormalizedCacheObject>
)
}
const restLink = cloudflareGatewayEnabled
? getRestLink(uniswapUrls.apiBaseUrlCloudflare)
: getRestLink()
const restLink = getRestLink()
const newClient = new ApolloClient({
assumeImmutableResults: true,
......
import appsFlyer from 'react-native-appsflyer'
import { isBetaBuild, isDevBuild } from 'src/utils/version'
import { config } from 'uniswap/src/config'
import { isBetaEnv, isDevEnv } from 'uniswap/src/utils/env'
import { logger } from 'utilities/src/logger/logger'
export function initAppsFlyer(): void {
appsFlyer.initSdk(
{
devKey: config.appsflyerApiKey,
isDebug: isDevBuild() || isBetaBuild(),
isDebug: isDevEnv() || isBetaEnv(),
appId: config.appsflyerAppId,
onInstallConversionDataListener: false,
onDeepLinkListener: false,
......
......@@ -15,7 +15,7 @@ import { sendMobileAnalyticsEvent } from 'src/features/telemetry'
import { MobileEventName } from 'src/features/telemetry/constants'
import { waitForWcWeb3WalletIsReady } from 'src/features/walletConnect/saga'
import { Screens } from 'src/screens/Screens'
import { UNISWAP_APP_HOSTNAME } from 'uniswap/src/constants/urls'
import { UNISWAP_WEB_URL } from 'uniswap/src/constants/urls'
import { setAccountAsActive } from 'wallet/src/features/wallet/slice'
import { ModalName } from 'wallet/src/telemetry/constants'
import {
......@@ -164,8 +164,8 @@ describe(handleDeepLink, () => {
it('Handles Share NFT Item Universal Link', async () => {
const path = `nfts/asset/${SAMPLE_SEED_ADDRESS_1}/123`
const pathUrl = `https://${UNISWAP_APP_HOSTNAME}/${path}`
const hashedUrl = `https://${UNISWAP_APP_HOSTNAME}/#/${path}`
const pathUrl = `${UNISWAP_WEB_URL}/${path}`
const hashedUrl = `${UNISWAP_WEB_URL}/#/${path}`
const expectedModal: OpenModalParams = {
name: ModalName.Explore,
initialState: {
......@@ -207,8 +207,8 @@ describe(handleDeepLink, () => {
it('Handles Share NFT Collection Universal Link', async () => {
const path = `nfts/collection/${SAMPLE_SEED_ADDRESS_1}`
const pathUrl = `https://${UNISWAP_APP_HOSTNAME}/${path}`
const hashedUrl = `https://${UNISWAP_APP_HOSTNAME}/#/${path}`
const pathUrl = `${UNISWAP_WEB_URL}/${path}`
const hashedUrl = `${UNISWAP_WEB_URL}/#/${path}`
const expectedModal: OpenModalParams = {
name: ModalName.Explore,
initialState: {
......@@ -248,8 +248,8 @@ describe(handleDeepLink, () => {
it('Handles Share Token Item Universal Link', async () => {
const path = `tokens/ethereum/${SAMPLE_SEED_ADDRESS_1}`
const pathUrl = `https://${UNISWAP_APP_HOSTNAME}/${path}`
const hashedUrl = `https://${UNISWAP_APP_HOSTNAME}/#/${path}`
const pathUrl = `${UNISWAP_WEB_URL}/${path}`
const hashedUrl = `${UNISWAP_WEB_URL}/#/${path}`
const expectedModal: OpenModalParams = {
name: ModalName.Explore,
initialState: {
......@@ -289,7 +289,7 @@ describe(handleDeepLink, () => {
it('Handles Share currently active Account Address Universal Link', () => {
const hash = `#/address/${account.address}`
const url = `https://${UNISWAP_APP_HOSTNAME}/${hash}`
const url = `${UNISWAP_WEB_URL}/${hash}`
return expectSaga(handleDeepLink, {
payload: {
url,
......@@ -305,7 +305,7 @@ describe(handleDeepLink, () => {
it('Handles Share already added Account Address Universal Link', () => {
const hash = `#/address/${SAMPLE_SEED_ADDRESS_2}`
const url = `https://${UNISWAP_APP_HOSTNAME}/${hash}`
const url = `${UNISWAP_WEB_URL}/${hash}`
return expectSaga(handleDeepLink, {
payload: {
url,
......@@ -330,8 +330,8 @@ describe(handleDeepLink, () => {
it('Handles Share external Account Address Universal Link', async () => {
const path = `address/${SAMPLE_SEED_ADDRESS_2}`
const pathUrl = `https://${UNISWAP_APP_HOSTNAME}/${path}`
const hashedUrl = `https://${UNISWAP_APP_HOSTNAME}/#/${path}`
const pathUrl = `${UNISWAP_WEB_URL}/${path}`
const hashedUrl = `${UNISWAP_WEB_URL}/#/${path}`
const expectedModal: OpenModalParams = {
name: ModalName.Explore,
initialState: {
......
......@@ -26,8 +26,8 @@ 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 } from 'uniswap/src/constants/urls'
import { FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/statsig/flags'
import { UNISWAP_WEB_HOSTNAME } from 'uniswap/src/constants/urls'
import { FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/gating/flags'
import i18n from 'uniswap/src/i18n/i18n'
import { logger } from 'utilities/src/logger/logger'
import { selectExtensionOnboardingState } from 'wallet/src/features/behaviorHistory/selectors'
......@@ -217,7 +217,7 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) {
if (!activeAccount) {
// For app.uniswap.org links it should open a browser with the link
// instead of handling it inside the app
if (url.hostname === UNISWAP_APP_HOSTNAME) {
if (url.hostname === UNISWAP_WEB_HOSTNAME) {
yield* call(openUri, action.payload.url, /* openExternalBrowser */ true)
}
// Skip handling any other deep links
......@@ -315,8 +315,8 @@ export function* handleDeepLink(action: ReturnType<typeof openDeepLink>) {
}
}
if (url.hostname === UNISWAP_APP_HOSTNAME) {
const urlParts = url.href.split(`${UNISWAP_APP_HOSTNAME}/`)
if (url.hostname === UNISWAP_WEB_HOSTNAME) {
const urlParts = url.href.split(`${UNISWAP_WEB_HOSTNAME}/`)
const urlPath = urlParts.length >= 1 ? (urlParts[1] as string) : ''
yield* call(handleUniswapAppDeepLink, urlPath, action.payload.url, LinkSource.Share)
return
......
......@@ -8,11 +8,11 @@ import { disableOnPress } from 'src/utils/disableOnPress'
import { Flex, HapticFeedback, TouchableArea } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks'
import { logger } from 'utilities/src/logger/logger'
import { CHAIN_INFO, ChainId } from 'wallet/src/constants/chains'
import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType, CopyNotificationType } from 'wallet/src/features/notifications/types'
import { useUnitagByAddress } from 'wallet/src/features/unitags/hooks'
import { sendWalletAnalyticsEvent } from 'wallet/src/telemetry'
import { ShareableEntity, WalletEventName } from 'wallet/src/telemetry/constants'
import { setClipboard } from 'wallet/src/utils/clipboard'
......
......@@ -27,8 +27,8 @@ import {
} from 'ui/src'
import { ENS_LOGO } from 'ui/src/assets'
import { iconSizes, imageSizes } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
import { useENSDescription, useENSName, useENSTwitterUsername } from 'wallet/src/features/ens/api'
import { selectWatchedAddressSet } from 'wallet/src/features/favorites/selectors'
......
import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types'
import { FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types'
export interface ExchangeTransferModalState {
serviceProvider: FORTransferInstitution
serviceProvider: FORServiceProvider
}
import { BottomSheetFlatList } from '@gorhom/bottom-sheet'
import React, { useCallback } from 'react'
import { ListRenderItemInfo } from 'react-native'
import { getCountry } from 'react-native-localize'
import { FlatList, ListRenderItemInfo } from 'react-native'
import { FadeIn, FadeOut } from 'react-native-reanimated'
import { useAppDispatch } from 'src/app/hooks'
import { openModal } from 'src/features/modals/modalSlice'
import { AnimatedFlex, Flex, ImpactFeedbackStyle, Loader, Text, TouchableArea } from 'ui/src'
import { AnimatedFlex, Flex, ImpactFeedbackStyle, Text, TouchableArea, useIsDarkMode } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { useFiatOnRampAggregatorTransferInstitutionsQuery } from 'wallet/src/features/fiatOnRamp/api'
import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types'
import { FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types'
import { getServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
import { ModalName } from 'wallet/src/telemetry/constants'
function key(item: FORTransferInstitution): string {
return item.id as string
function key(item: FORServiceProvider): string {
return item.serviceProvider
}
const CEX_ICON_SIZE = iconSizes.icon36
const CEX_ICON_BORDER_RADIUS = 12
function CEXItemWrapper({
institution,
onSelectTransferInstitution,
serviceProvider,
onSelectServiceProvider,
}: {
institution: FORTransferInstitution
onSelectTransferInstitution: (transferInstitution: FORTransferInstitution) => void
serviceProvider: FORServiceProvider
onSelectServiceProvider: (serviceProvider: FORServiceProvider) => void
}): JSX.Element | null {
const onPress = (): void => onSelectTransferInstitution(institution)
const onPress = (): void => onSelectServiceProvider(serviceProvider)
const isDarkMode = useIsDarkMode()
const logoUrl = getServiceProviderLogo(serviceProvider.logos, isDarkMode)
return (
<TouchableArea hapticFeedback hapticStyle={ImpactFeedbackStyle.Light} onPress={onPress}>
......@@ -46,11 +47,11 @@ function CEXItemWrapper({
borderRadius={CEX_ICON_BORDER_RADIUS}
height={CEX_ICON_SIZE}
resizeMode="cover"
uri={institution.icon}
uri={logoUrl}
width={CEX_ICON_SIZE}
/>
<Text flexShrink={1} variant="body2">
{institution.name}
{serviceProvider.name}
</Text>
</Flex>
</Flex>
......@@ -58,18 +59,21 @@ function CEXItemWrapper({
)
}
export function TransferInstitutionSelector({ onClose }: { onClose: () => void }): JSX.Element {
export function ServiceProviderSelector({
onClose,
serviceProviders,
}: {
onClose: () => void
serviceProviders: FORServiceProvider[]
}): JSX.Element {
const dispatch = useAppDispatch()
const { data, isLoading } = useFiatOnRampAggregatorTransferInstitutionsQuery({
countryCode: getCountry(),
})
const onSelectTransferInstitution = useCallback(
(transferInstitution: FORTransferInstitution) => {
const onSelectServiceProvider = useCallback(
(serviceProvider: FORServiceProvider) => {
dispatch(
openModal({
name: ModalName.ExchangeTransferModal,
initialState: { serviceProvider: transferInstitution },
initialState: { serviceProvider },
})
)
onClose()
......@@ -78,30 +82,26 @@ export function TransferInstitutionSelector({ onClose }: { onClose: () => void }
)
const renderItem = useCallback(
({ item: institution }: ListRenderItemInfo<FORTransferInstitution>) => (
({ item: serviceProvider }: ListRenderItemInfo<FORServiceProvider>) => (
<CEXItemWrapper
institution={institution}
onSelectTransferInstitution={onSelectTransferInstitution}
serviceProvider={serviceProvider}
onSelectServiceProvider={onSelectServiceProvider}
/>
),
[onSelectTransferInstitution]
[onSelectServiceProvider]
)
return (
<Flex grow>
<AnimatedFlex grow entering={FadeIn} exiting={FadeOut}>
{isLoading ? (
<Loader.TransferInstitution iconSize={CEX_ICON_SIZE} itemsCount={5} />
) : (
<BottomSheetFlatList
ItemSeparatorComponent={renderItemSeparator}
bounces={true}
data={data?.institutions || []}
keyExtractor={key}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
/>
)}
<FlatList
ItemSeparatorComponent={renderItemSeparator}
bounces={true}
data={serviceProviders}
keyExtractor={key}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
/>
</AnimatedFlex>
</Flex>
)
......
......@@ -48,6 +48,16 @@ const PREDEFINED_AMOUNTS = [100, 300, 1000]
type OnChangeAmount = (amount: string) => void
function OnRampError({ errorText, color }: { errorText: string; color: ColorTokens }): JSX.Element {
return (
<Flex centered>
<Text color={color} variant="body3">
{errorText}
</Text>
</Flex>
)
}
interface Props {
showNativeKeyboard: boolean
onInputPanelLayout: (event: LayoutChangeEvent) => void
......@@ -67,6 +77,7 @@ interface Props {
onTokenSelectorPress: () => void
predefinedAmountsSupported: boolean
appFiatCurrencySupported: boolean
notAvailableInThisRegion?: boolean
fiatCurrencyInfo: FiatCurrencyInfo
}
......@@ -89,6 +100,7 @@ export function FiatOnRampAmountSection({
onTokenSelectorPress,
predefinedAmountsSupported,
appFiatCurrencySupported,
notAvailableInThisRegion,
fiatCurrencyInfo,
}: Props): JSX.Element {
const { t } = useTranslation()
......@@ -186,7 +198,8 @@ export function FiatOnRampAmountSection({
{currency.currencyInfo && (
<SelectTokenButton
amount={quoteAmount}
disabled={!quoteCurrencyAmountReady}
amountReady={quoteCurrencyAmountReady}
disabled={notAvailableInThisRegion}
loading={selectTokenLoading}
selectedCurrencyInfo={currency.currencyInfo}
onPress={onTokenSelectorPress}
......@@ -199,18 +212,17 @@ export function FiatOnRampAmountSection({
key={amount}
amount={amount}
currentAmount={value}
disabled={notAvailableInThisRegion}
fiatCurrencyInfo={fiatCurrencyInfo}
onPress={onChoosePredifendAmount}
/>
))}
</Flex>
) : null}
{!appFiatCurrencySupported ? (
<Flex centered>
<Text color="$neutral3" variant="body3">
{t('fiatOnRamp.error.usd')}
</Text>
</Flex>
{notAvailableInThisRegion ? (
<OnRampError color="$neutral2" errorText={t('fiatOnRamp.error.unavailable')} />
) : !appFiatCurrencySupported ? (
<OnRampError color="$neutral3" errorText={t('fiatOnRamp.error.usd')} />
) : null}
</Flex>
</Flex>
......@@ -221,6 +233,7 @@ interface SelectTokenButtonProps {
onPress: () => void
selectedCurrencyInfo: CurrencyInfo
amount: number
amountReady?: boolean
disabled?: boolean
loading?: boolean
}
......@@ -229,6 +242,7 @@ function SelectTokenButton({
selectedCurrencyInfo,
onPress,
amount,
amountReady,
disabled,
loading,
}: SelectTokenButtonProps): JSX.Element {
......@@ -236,12 +250,13 @@ function SelectTokenButton({
amount.toString(),
selectedCurrencyInfo.currency
)
const textColor = disabled || loading ? '$neutral3' : '$neutral2'
const textColor = !amountReady || disabled || loading ? '$neutral3' : '$neutral2'
return (
<TouchableArea
hapticFeedback
borderRadius="$roundedFull"
disabled={disabled}
testID={ElementName.TokenSelectorToggle}
onPress={onPress}>
<Flex centered row flexDirection="row" gap="$none" p="$spacing4">
......@@ -272,11 +287,13 @@ function PredefinedAmount({
onPress,
currentAmount,
fiatCurrencyInfo,
disabled,
}: {
amount: number
currentAmount: string
onPress: (amount: string) => void
fiatCurrencyInfo: FiatCurrencyInfo
disabled?: boolean
}): JSX.Element {
const colors = useSporeColors()
const { addFiatSymbolToNumber } = useLocalizationContext()
......@@ -290,14 +307,15 @@ function PredefinedAmount({
return (
<TouchableOpacity
disabled={disabled}
onPress={async (): Promise<void> => {
await HapticFeedback.impact()
onPress(amount.toString())
}}>
<Pill
backgroundColor={highlighted ? '$surface2' : '$surface1'}
customBorderColor={colors.surface3.val}
foregroundColor={colors[highlighted ? 'neutral1' : 'neutral2'].val}
backgroundColor={!disabled && highlighted ? '$surface2' : '$surface1'}
customBorderColor={disabled ? colors.surface2.val : colors.surface3.val}
foregroundColor={colors[disabled ? 'neutral3' : highlighted ? 'neutral1' : 'neutral2'].val}
label={formattedAmount}
px="$spacing16"
textVariant="buttonLabel3"
......
......@@ -2,6 +2,10 @@ import React from 'react'
import { useTranslation } from 'react-i18next'
import { Image, ImageBackground, StyleSheet } from 'react-native'
import { FadeIn, FadeOut } from 'react-native-reanimated'
import {
SERVICE_PROVIDER_ICON_BORDER_RADIUS,
ServiceProviderLogoStyles,
} from 'src/features/fiatOnRamp/constants'
import { AnimatedFlex, Flex, Text, useDeviceInsets, useIsDarkMode } from 'ui/src'
import {
FOR_CONNECTING_BACKGROUND_DARK,
......@@ -10,9 +14,6 @@ import {
} from 'ui/src/assets'
import { iconSizes } from 'ui/src/theme'
export const SERVICE_PROVIDER_ICON_SIZE = 90
export const SERVICE_PROVIDER_ICON_BORDER_RADIUS = 20
export function FiatOnRampConnectingView({
amount,
quoteCurrencyCode,
......@@ -75,7 +76,7 @@ const styles = StyleSheet.create({
uniswapLogoWrapper: {
backgroundColor: '#FFEFF8', // #FFD8EF with 40% opacity on a white background
borderRadius: SERVICE_PROVIDER_ICON_BORDER_RADIUS,
height: SERVICE_PROVIDER_ICON_SIZE,
width: SERVICE_PROVIDER_ICON_SIZE,
height: ServiceProviderLogoStyles.icon.height,
width: ServiceProviderLogoStyles.icon.width,
},
})
......@@ -5,10 +5,8 @@ import { FadeIn, FadeOut, FadeOutDown } from 'react-native-reanimated'
import { useAppDispatch, useShouldShowNativeKeyboard } from 'src/app/hooks'
import { FiatOnRampCtaButton } from 'src/components/fiatOnRamp/CtaButton'
import { FiatOnRampAmountSection } from 'src/features/fiatOnRamp/FiatOnRampAmountSection'
import {
FiatOnRampConnectingView,
SERVICE_PROVIDER_ICON_SIZE,
} from 'src/features/fiatOnRamp/FiatOnRampConnecting'
import { FiatOnRampConnectingView } from 'src/features/fiatOnRamp/FiatOnRampConnecting'
import { ServiceProviderLogoStyles } from 'src/features/fiatOnRamp/constants'
import { useMoonpayFiatOnRamp, useMoonpaySupportedTokens } from 'src/features/fiatOnRamp/hooks'
import { FiatOnRampCurrency } from 'src/features/fiatOnRamp/types'
import { closeModal } from 'src/features/modals/modalSlice'
......@@ -292,11 +290,14 @@ function FiatOnRampContent({ onClose }: { onClose: () => void }): JSX.Element {
<Flex
alignItems="center"
borderRadius="$rounded20"
height={SERVICE_PROVIDER_ICON_SIZE}
height={ServiceProviderLogoStyles.icon.height}
justifyContent="center"
style={styles.moonpayLogoWrapper}
width={SERVICE_PROVIDER_ICON_SIZE}>
<MoonpayLogo height={SERVICE_PROVIDER_ICON_SIZE} width={SERVICE_PROVIDER_ICON_SIZE} />
width={ServiceProviderLogoStyles.icon.width}>
<MoonpayLogo
height={ServiceProviderLogoStyles.icon.height}
width={ServiceProviderLogoStyles.icon.width}
/>
</Flex>
}
serviceProviderName="MoonPay"
......
......@@ -15,7 +15,7 @@ import {
useFiatOnRampAggregatorCryptoQuoteQuery,
useFiatOnRampAggregatorSupportedFiatCurrenciesQuery,
} from 'wallet/src/features/fiatOnRamp/api'
import { FORQuote } from 'wallet/src/features/fiatOnRamp/types'
import { FORQuote, FORSupportedFiatCurrency } from 'wallet/src/features/fiatOnRamp/types'
import {
isFiatOnRampApiError,
isInvalidRequestAmountTooHigh,
......@@ -27,6 +27,7 @@ import { useActiveAccountAddress } from 'wallet/src/features/wallet/hooks'
export function useMeldFiatCurrencySupportInfo(countryCode: string): {
appFiatCurrencySupportedInMeld: boolean
meldSupportedFiatCurrency: FiatCurrencyInfo
supportedFiatCurrencies: FORSupportedFiatCurrency[] | undefined
} {
// Not all the currencies are supported by Meld, so we need to fallback to USD if the currency is not supported
const appFiatCurrencyInfo = useAppFiatCurrencyInfo()
......@@ -49,6 +50,7 @@ export function useMeldFiatCurrencySupportInfo(countryCode: string): {
return {
appFiatCurrencySupportedInMeld: appFiatCurrencySupported,
meldSupportedFiatCurrency,
supportedFiatCurrencies: supportedFiatCurrencies?.fiatCurrencies,
}
}
......
import { StyleSheet } from 'react-native'
export const FOR_MODAL_SNAP_POINTS = ['70%', '100%']
export const SERVICE_PROVIDER_ICON_SIZE = 90
export const SERVICE_PROVIDER_ICON_BORDER_RADIUS = 20
export const ServiceProviderLogoStyles = StyleSheet.create({
icon: {
height: SERVICE_PROVIDER_ICON_SIZE,
width: SERVICE_PROVIDER_ICON_SIZE,
},
})
......@@ -8,7 +8,6 @@ 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 {
......@@ -194,9 +193,7 @@ export function useMoonpayFiatOnRamp({
amount: baseCurrencyAmount,
currencyCode: quoteCurrencyCode,
baseCurrencyCode,
redirectUrl: `${
isAndroid ? uniswapUrls.appUrl : uniswapUrls.appBaseUrl
}/?screen=transaction&fiatOnRamp=true&userAddress=${activeAccountAddress}`,
redirectUrl: `${uniswapUrls.redirectUrlBase}/?screen=transaction&fiatOnRamp=true&userAddress=${activeAccountAddress}`,
}
: skipToken
)
......
import type { ReactNativeFirebase } from '@react-native-firebase/app'
import '@react-native-firebase/auth'
import firestore, { FirebaseFirestoreTypes } from '@react-native-firebase/firestore'
import { isBetaBuild, isDevBuild } from 'src/utils/version'
import { isBetaEnv, isDevEnv } from 'uniswap/src/utils/env'
const ADDRESS_DATA_COLLECTION = 'address_data'
const DEV_ADDRESS_DATA_COLLECTION = 'dev_address_data'
......@@ -39,10 +39,10 @@ export const getFirestoreMetadataRef = (
.doc('data')
export function getAddressDataCollectionFromBundleId(): string {
if (isDevBuild()) {
if (isDevEnv()) {
return DEV_ADDRESS_DATA_COLLECTION
}
if (isBetaBuild()) {
if (isBetaEnv()) {
return BETA_ADDRESS_DATA_COLLECTION
}
return ADDRESS_DATA_COLLECTION
......
......@@ -2,9 +2,10 @@ import { ExploreModalState } from 'src/app/modals/ExploreModalState'
import { RemoveWalletModalState } from 'src/components/RemoveWallet/RemoveWalletModalState'
import { ExtensionWaitlistModalState } from 'src/features/scantastic/ExtensionWaitlistModalState'
import { ScantasticModalState } from 'src/features/scantastic/ScantasticModalState'
import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState'
import { Screens } from 'src/screens/Screens'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types'
import { FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types'
import { TransactionState } from 'wallet/src/features/transactions/transactionState/types'
import { ModalName } from 'wallet/src/telemetry/constants'
......@@ -16,14 +17,14 @@ export interface AppModalState<T> {
export interface ModalsState {
[ModalName.AccountSwitcher]: AppModalState<undefined>
[ModalName.ExchangeTransferModal]: AppModalState<{
serviceProvider: FORTransferInstitution
serviceProvider: FORServiceProvider
}>
[ModalName.Experiments]: AppModalState<undefined>
[ModalName.Explore]: AppModalState<ExploreModalState>
[ModalName.FiatCurrencySelector]: AppModalState<undefined>
[ModalName.FiatOnRamp]: AppModalState<undefined>
[ModalName.FiatOnRampAggregator]: AppModalState<undefined>
[ModalName.ReceiveCryptoModal]: AppModalState<undefined>
[ModalName.ReceiveCryptoModal]: AppModalState<ReceiveCryptoModalState>
[ModalName.LanguageSelector]: AppModalState<undefined>
[ModalName.RemoveWallet]: AppModalState<RemoveWalletModalState>
[ModalName.RestoreWallet]: AppModalState<undefined>
......
......@@ -4,6 +4,7 @@ import { RemoveWalletModalState } from 'src/components/RemoveWallet/RemoveWallet
import { ExchangeTransferModalState } from 'src/features/fiatOnRamp/ExchangeTransferModalState'
import { ExtensionWaitlistModalState } from 'src/features/scantastic/ExtensionWaitlistModalState'
import { ScantasticModalState } from 'src/features/scantastic/ScantasticModalState'
import { ReceiveCryptoModalState } from 'src/screens/ReceiveCryptoModalState'
import { Screens } from 'src/screens/Screens'
import { getKeys } from 'utilities/src/primitives/objects'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
......@@ -47,7 +48,7 @@ type FiatOnRampAggregatorModalParams = {
type ReceiveCryptoModalParams = {
name: typeof ModalName.ReceiveCryptoModal
initialState?: undefined
initialState: ReceiveCryptoModalState
}
type LanguageSelectorModalParams = {
......@@ -123,7 +124,7 @@ export const initialModalsState: ModalsState = {
},
[ModalName.ReceiveCryptoModal]: {
isOpen: false,
initialState: undefined,
initialState: [],
},
[ModalName.WalletConnectScan]: {
isOpen: false,
......
......@@ -4,8 +4,8 @@ import { OnboardingStackBaseParams, useOnboardingStackNavigation } from 'src/app
import { sendMobileAnalyticsEvent } from 'src/features/telemetry'
import { MobileEventName } from 'src/features/telemetry/constants'
import { Screens } from 'src/screens/Screens'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { useTrace } from 'utilities/src/telemetry/trace/TraceContext'
import {
setHasSkippedUnitagPrompt,
......@@ -43,7 +43,6 @@ export function useCompleteOnboardingCallback({
const parentTrace = useTrace()
const navigation = useOnboardingStackNavigation()
const unitagsFeatureFlagEnabled = useFeatureFlag(FeatureFlags.Unitags)
const claimUnitag = useClaimUnitag()
const uniconsV2Enabled = useFeatureFlag(FeatureFlags.UniconsV2)
......@@ -90,10 +89,8 @@ export function useCompleteOnboardingCallback({
// Remove pending flag from all new accounts.
dispatch(pendingAccountActions.trigger(PendingAccountActions.Activate))
// Dismiss unitags prompt if:
// - the feature was enabled
// - the onboarding method prompts for unitags (create new)
if (unitagsFeatureFlagEnabled && importType === ImportType.CreateNew) {
// Dismiss unitags prompt if the onboarding method prompts for unitags (create new)
if (importType === ImportType.CreateNew) {
dispatch(setHasSkippedUnitagPrompt(true))
}
......
......@@ -121,7 +121,7 @@ export function ScantasticModal(): JSX.Element | null {
try {
// submit encrypted blob
const response = await fetch(`${uniswapUrls.apiBaseExtensionUrl}/scantastic/blob`, {
const response = await fetch(`${uniswapUrls.scantasticApiUrl}/blob`, {
method: 'POST',
headers: {
Accept: 'application/json',
......@@ -176,16 +176,13 @@ export function ScantasticModal(): JSX.Element | null {
return
}
try {
const response = await fetch(
`${uniswapUrls.apiBaseExtensionUrl}/scantastic/otp-state/${uuid}`,
{
method: 'POST',
headers: {
Accept: 'application/json',
Origin: 'https://uniswap.org',
},
}
)
const response = await fetch(`${uniswapUrls.scantasticApiUrl}/otp-state/${uuid}`, {
method: 'POST',
headers: {
Accept: 'application/json',
Origin: 'https://uniswap.org',
},
})
if (!response.ok) {
throw new Error(`Failed to check OTP state: ${await response.text()}`)
}
......
......@@ -16,7 +16,7 @@ export function* telemetrySaga() {
new ApplicationTransport(
uniswapUrls.amplitudeProxyUrl,
OriginApplication.MOBILE,
uniswapUrls.apiBaseUrl,
uniswapUrls.apiOrigin,
DeviceInfo.getBundleId()
),
allowAnalytics
......
......@@ -28,9 +28,10 @@ import {
} 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/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { useUnitagUpdater } from 'uniswap/src/features/unitags/context'
import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks'
import { ProfileMetadata } from 'uniswap/src/features/unitags/types'
import { isIOS } from 'uniswap/src/utils/platform'
import { logger } from 'utilities/src/logger/logger'
......@@ -43,10 +44,7 @@ import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType } from 'wallet/src/features/notifications/types'
import { updateUnitagMetadata } from 'wallet/src/features/unitags/api'
import { tryUploadAvatar } from 'wallet/src/features/unitags/avatars'
import {
useAvatarUploadCredsWithRefresh,
useUnitagByAddress,
} from 'wallet/src/features/unitags/hooks'
import { useAvatarUploadCredsWithRefresh } from 'wallet/src/features/unitags/hooks'
import { useWalletSigners } from 'wallet/src/features/wallet/context'
import { useAccount } from 'wallet/src/features/wallet/hooks'
import { DisplayNameType } from 'wallet/src/features/wallet/types'
......
......@@ -2,8 +2,8 @@ import { useCallback, useEffect, useState } from 'react'
import { useAppSelector } from 'src/app/hooks'
import { openModal } from 'src/features/modals/modalSlice'
import { selectModalState } from 'src/features/modals/selectModalState'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { logger } from 'utilities/src/logger/logger'
import { Keyring } from 'wallet/src/features/wallet/Keyring/Keyring'
import { useNativeAccountExists } from 'wallet/src/features/wallet/hooks'
......
......@@ -3,20 +3,18 @@ import { useTranslation } from 'react-i18next'
import { getCountry } from 'react-native-localize'
import { useAppDispatch } from 'src/app/hooks'
import { Screen } from 'src/components/layout/Screen'
import {
FiatOnRampConnectingView,
SERVICE_PROVIDER_ICON_BORDER_RADIUS,
SERVICE_PROVIDER_ICON_SIZE,
} from 'src/features/fiatOnRamp/FiatOnRampConnecting'
import { FiatOnRampConnectingView } from 'src/features/fiatOnRamp/FiatOnRampConnecting'
import { ServiceProviderLogoStyles } from 'src/features/fiatOnRamp/constants'
import { useFiatOnRampTransactionCreator } from 'src/features/fiatOnRamp/hooks'
import { Flex, useIsDarkMode } from 'ui/src'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { isAndroid } from 'uniswap/src/utils/platform'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useTimeout } from 'utilities/src/time/timing'
import { ChainId } from 'wallet/src/constants/chains'
import { useFiatOnRampAggregatorTransferWidgetQuery } from 'wallet/src/features/fiatOnRamp/api'
import { FORTransferInstitution } from 'wallet/src/features/fiatOnRamp/types'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
import { FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types'
import { getServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils'
import { ImageUri } from 'wallet/src/features/images/ImageUri'
import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType } from 'wallet/src/features/notifications/types'
import { useActiveAccountAddressWithThrow } from 'wallet/src/features/wallet/hooks'
......@@ -27,14 +25,11 @@ import { openUri } from 'wallet/src/utils/linking'
// Design decision
const CONNECTING_TIMEOUT = 2 * ONE_SECOND_MS
const DEFAULT_TRANSFER_AMOUNT = 1
const DEFAULT_TRANSFER_CURRENCY = 'ETH'
export function ExchangeTransferConnecting({
serviceProvider,
onClose,
}: {
serviceProvider: FORTransferInstitution
serviceProvider: FORServiceProvider
onClose: () => void
}): JSX.Element {
const { t } = useTranslation()
......@@ -43,8 +38,8 @@ export function ExchangeTransferConnecting({
const [timeoutElapsed, setTimeoutElapsed] = useState(false)
const initialTypeInfo = useMemo(
() => ({ institutionLogoUrl: serviceProvider.icon }),
[serviceProvider.icon]
() => ({ serviceProviderLogo: serviceProvider.logos }),
[serviceProvider.logos]
)
const { externalTransactionId, dispatchAddTransaction } = useFiatOnRampTransactionCreator(
......@@ -72,15 +67,11 @@ export function ExchangeTransferConnecting({
isLoading: widgetLoading,
error: widgetError,
} = useFiatOnRampAggregatorTransferWidgetQuery({
sourceAmount: DEFAULT_TRANSFER_AMOUNT,
sourceCurrencyCode: DEFAULT_TRANSFER_CURRENCY,
countryCode: getCountry(),
institutionId: serviceProvider.id,
serviceProvider: serviceProvider.serviceProvider,
walletAddress: activeAccountAddress,
externalSessionId: externalTransactionId,
redirectURL: `${
isAndroid ? uniswapUrls.appUrl : uniswapUrls.appBaseUrl
}/?screen=transaction&fiatOnRamp=true&userAddress=${activeAccountAddress}`,
redirectUrl: `${uniswapUrls.redirectUrlBase}/?screen=transaction&fiatOnRamp=true&userAddress=${activeAccountAddress}`,
})
useEffect(() => {
......@@ -113,16 +104,20 @@ export function ExchangeTransferConnecting({
serviceProvider?.name,
])
const isDarkMode = useIsDarkMode()
const logoUrl = getServiceProviderLogo(serviceProvider.logos, isDarkMode)
return (
<Screen>
<FiatOnRampConnectingView
serviceProviderLogo={
<RemoteImage
borderRadius={SERVICE_PROVIDER_ICON_BORDER_RADIUS}
height={SERVICE_PROVIDER_ICON_SIZE}
uri={serviceProvider.icon}
width={SERVICE_PROVIDER_ICON_SIZE}
/>
<Flex
alignItems="center"
height={ServiceProviderLogoStyles.icon.height}
justifyContent="center"
width={ServiceProviderLogoStyles.icon.width}>
<ImageUri imageStyle={ServiceProviderLogoStyles.icon} uri={logoUrl} />
</Flex>
}
serviceProviderName={serviceProvider.name}
/>
......
......@@ -2,15 +2,12 @@ import { NativeStackScreenProps } from '@react-navigation/native-stack'
import { skipToken } from '@reduxjs/toolkit/query/react'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet } from 'react-native'
import { useAppDispatch } from 'src/app/hooks'
import { FiatOnRampStackParamList } from 'src/app/navigation/types'
import { Screen } from 'src/components/layout/Screen'
import {
FiatOnRampConnectingView,
SERVICE_PROVIDER_ICON_SIZE,
} from 'src/features/fiatOnRamp/FiatOnRampConnecting'
import { FiatOnRampConnectingView } from 'src/features/fiatOnRamp/FiatOnRampConnecting'
import { useFiatOnRampContext } from 'src/features/fiatOnRamp/FiatOnRampContext'
import { ServiceProviderLogoStyles } from 'src/features/fiatOnRamp/constants'
import { useFiatOnRampTransactionCreator } from 'src/features/fiatOnRamp/hooks'
import { getServiceProviderForQuote } from 'src/features/fiatOnRamp/utils'
import { closeModal } from 'src/features/modals/modalSlice'
......@@ -18,12 +15,11 @@ import { FiatOnRampScreens } from 'src/screens/Screens'
import { Flex, Text, useIsDarkMode } from 'ui/src'
import { spacing } from 'ui/src/theme'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { isAndroid } from 'uniswap/src/utils/platform'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useTimeout } from 'utilities/src/time/timing'
import { ChainId } from 'wallet/src/constants/chains'
import { useFiatOnRampAggregatorWidgetQuery } from 'wallet/src/features/fiatOnRamp/api'
import { getServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils'
import { getOptionalServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils'
import { ImageUri } from 'wallet/src/features/images/ImageUri'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
import { pushNotification } from 'wallet/src/features/notifications/slice'
......@@ -93,9 +89,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element |
sourceCurrencyCode: baseCurrencyInfo.code,
walletAddress: activeAccountAddress,
externalSessionId: externalTransactionId,
redirectUrl: `${
isAndroid ? uniswapUrls.appUrl : uniswapUrls.appBaseUrl
}/?screen=transaction&fiatOnRamp=true&userAddress=${activeAccountAddress}`,
redirectUrl: `${uniswapUrls.redirectUrlBase}/?screen=transaction&fiatOnRamp=true&userAddress=${activeAccountAddress}`,
}
: skipToken
)
......@@ -154,7 +148,7 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element |
])
const isDarkMode = useIsDarkMode()
const logoUrl = getServiceProviderLogo(serviceProvider?.logos, isDarkMode)
const logoUrl = getOptionalServiceProviderLogo(serviceProvider?.logos, isDarkMode)
return (
<Screen>
......@@ -170,9 +164,9 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element |
serviceProviderLogo={
<Flex
alignItems="center"
height={SERVICE_PROVIDER_ICON_SIZE}
height={ServiceProviderLogoStyles.icon.height}
justifyContent="center"
width={SERVICE_PROVIDER_ICON_SIZE}>
width={ServiceProviderLogoStyles.icon.width}>
<ImageUri imageStyle={ServiceProviderLogoStyles.icon} uri={logoUrl} />
</Flex>
}
......@@ -192,10 +186,3 @@ export function FiatOnRampConnectingScreen({ navigation }: Props): JSX.Element |
</Screen>
)
}
const ServiceProviderLogoStyles = StyleSheet.create({
icon: {
height: SERVICE_PROVIDER_ICON_SIZE,
width: SERVICE_PROVIDER_ICON_SIZE,
},
})
......@@ -33,7 +33,11 @@ import {
useFiatOnRampAggregatorTransactionsQuery,
} from 'wallet/src/features/fiatOnRamp/api'
import { FORQuote, FORServiceProvider, FORTransaction } from 'wallet/src/features/fiatOnRamp/types'
import { getServiceProviderLogo } from 'wallet/src/features/fiatOnRamp/utils'
import {
getServiceProviderLogo,
isFiatOnRampApiError,
isNoQuotesError,
} from 'wallet/src/features/fiatOnRamp/utils'
import { pushNotification } from 'wallet/src/features/notifications/slice'
import { AppNotificationType } from 'wallet/src/features/notifications/types'
import { sendWalletAnalyticsEvent } from 'wallet/src/telemetry'
......@@ -116,7 +120,7 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
const { showNativeKeyboard, onDecimalPadLayout, isLayoutPending, onInputPanelLayout } =
useShouldShowNativeKeyboard()
const { appFiatCurrencySupportedInMeld, meldSupportedFiatCurrency } =
const { appFiatCurrencySupportedInMeld, meldSupportedFiatCurrency, supportedFiatCurrencies } =
useMeldFiatCurrencySupportInfo(countryCode)
const debouncedAmount = useDebounce(amount, DEFAULT_DELAY * 2)
......@@ -164,11 +168,6 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
limit: 1,
})
const { errorText, errorColor } = useParseFiatOnRampError(
quotesError || serviceProvidersError,
meldSupportedFiatCurrency.code
)
const prevQuotes = usePrevious(quotes)
useEffect(() => {
if (quotes && (!selectedQuote || prevQuotes !== quotes)) {
......@@ -285,6 +284,16 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
meldSupportedFiatCurrency.code.toLowerCase()
)
const notAvailableInThisRegion =
supportedFiatCurrencies?.length === 0 ||
(isFiatOnRampApiError(quotesError) && isNoQuotesError(quotesError)) ||
quotes?.length === 0
const { errorText, errorColor } = useParseFiatOnRampError(
!notAvailableInThisRegion && (quotesError || serviceProvidersError),
meldSupportedFiatCurrency.code
)
return (
<Screen edges={['top']}>
<HandleBar backgroundColor="none" />
......@@ -312,6 +321,7 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
errorText={errorText}
fiatCurrencyInfo={meldSupportedFiatCurrency}
inputRef={inputRef}
notAvailableInThisRegion={notAvailableInThisRegion}
predefinedAmountsSupported={predefinedAmountsSupported}
quoteAmount={selectedQuote?.destinationAmount ?? 0}
quoteCurrencyAmountReady={Boolean(amount && selectedQuote)}
......@@ -341,6 +351,7 @@ export function FiatOnRampScreen({ navigation }: Props): JSX.Element {
{!showNativeKeyboard && (
<DecimalPadLegacy
hasCurrencyPrefix
disabled={notAvailableInThisRegion}
resetSelection={resetSelection}
selection={selection}
setValue={onChangeValue('textInput')}
......
......@@ -67,8 +67,8 @@ import BuyIcon from 'ui/src/assets/icons/buy.svg'
import ScanIcon from 'ui/src/assets/icons/scan-home.svg'
import SendIcon from 'ui/src/assets/icons/send-action.svg'
import { iconSizes, spacing } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { useInterval, useTimeout } from 'utilities/src/time/timing'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
......@@ -76,6 +76,7 @@ import {
selectHasSkippedUnitagPrompt,
selectHasViewedUniconV2IntroModal,
} from 'wallet/src/features/behaviorHistory/selectors'
import { useCexTransferProviders } from 'wallet/src/features/fiatOnRamp/api'
import { useSelectAddressHasNotifications } from 'wallet/src/features/notifications/hooks'
import { setNotificationStatus } from 'wallet/src/features/notifications/slice'
import { PortfolioBalance } from 'wallet/src/features/portfolio/PortfolioBalance'
......@@ -332,6 +333,7 @@ export function HomeScreen(props?: AppStackScreenProp<Screens.Home>): JSX.Elemen
const forAggregatorEnabled = useFeatureFlag(FeatureFlags.ForAggregator)
const cexTransferEnabled = useFeatureFlag(FeatureFlags.CexTransfers)
const cexTransferProviders = useCexTransferProviders(cexTransferEnabled)
const onPressBuy = useCallback(
() =>
......@@ -351,14 +353,16 @@ export function HomeScreen(props?: AppStackScreenProp<Screens.Home>): JSX.Elemen
}, [dispatch])
const onPressSend = useCallback(() => dispatch(openModal({ name: ModalName.Send })), [dispatch])
const onPressReceive = useCallback(() => {
if (cexTransferEnabled) {
dispatch(openModal({ name: ModalName.ReceiveCryptoModal }))
if (cexTransferProviders.length > 0) {
dispatch(
openModal({ name: ModalName.ReceiveCryptoModal, initialState: cexTransferProviders })
)
} else {
dispatch(
openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })
)
}
}, [dispatch, cexTransferEnabled])
}, [dispatch, cexTransferProviders])
const onPressViewOnlyLabel = useCallback(
() => dispatch(openModal({ name: ModalName.ViewOnlyExplainer })),
[dispatch]
......
......@@ -11,8 +11,8 @@ import { OnboardingScreens } from 'src/screens/Screens'
import { useAddBackButton } from 'src/utils/useAddBackButton'
import { Flex, Icons, Text, TouchableArea, Unicon, UniconV2, useIsDarkMode } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName'
import {
FORMAT_DATE_TIME_SHORT,
......
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { ActivityIndicator, TextInput as NativeTextInput, StyleSheet } from 'react-native'
import { useAppDispatch } from 'src/app/hooks'
import { OnboardingStackParamList } from 'src/app/navigation/types'
import Trace from 'src/components/Trace/Trace'
import { SafeKeyboardOnboardingScreen } from 'src/features/onboarding/SafeKeyboardOnboardingScreen'
import { OnboardingScreens } from 'src/screens/Screens'
import { useAddBackButton } from 'src/utils/useAddBackButton'
import { AnimatePresence, Button, Flex, Icons, Text } from 'ui/src'
import { fonts } from 'ui/src/theme'
import { isAndroid } from 'uniswap/src/utils/platform'
import { TextInput } from 'wallet/src/components/input/TextInput'
import { NICKNAME_MAX_LENGTH } from 'wallet/src/constants/accounts'
import { ImportType } from 'wallet/src/features/onboarding/types'
import {
EditAccountAction,
editAccountActions,
} from 'wallet/src/features/wallet/accounts/editAccountSaga'
import { AccountType } from 'wallet/src/features/wallet/accounts/types'
import {
PendingAccountActions,
pendingAccountActions,
} from 'wallet/src/features/wallet/create/pendingAccountsSaga'
import { usePendingAccounts } from 'wallet/src/features/wallet/hooks'
import { ElementName } from 'wallet/src/telemetry/constants'
import { shortenAddress } from 'wallet/src/utils/addresses'
type Props = NativeStackScreenProps<OnboardingStackParamList, OnboardingScreens.EditName>
export function EditNameScreen({ navigation, route: { params } }: Props): JSX.Element {
const dispatch = useAppDispatch()
const { t } = useTranslation()
// Reference pending accounts to avoid any lag in saga import.
const pendingAccount = Object.values(usePendingAccounts())?.[0]
useEffect(() => {
// Sets the default wallet nickname based on derivation index once the pendingAccount is set.
if (pendingAccount && pendingAccount.type !== AccountType.Readonly) {
setNewAccountName(
pendingAccount.name ||
t('onboarding.wallet.defaultName', { number: pendingAccount.derivationIndex + 1 }) ||
''
)
}
}, [pendingAccount, t])
const [newAccountName, setNewAccountName] = useState<string>('')
useAddBackButton(navigation)
useEffect(() => {
const beforeRemoveListener = (): void => {
dispatch(pendingAccountActions.trigger(PendingAccountActions.Delete))
}
navigation.addListener('beforeRemove', beforeRemoveListener)
return () => navigation.removeListener('beforeRemove', beforeRemoveListener)
}, [dispatch, navigation])
const onPressNext = (): void => {
navigation.navigate({
name:
params?.importType === ImportType.CreateNew
? OnboardingScreens.WelcomeWallet
: OnboardingScreens.Notifications,
merge: true,
params,
})
if (pendingAccount) {
dispatch(
editAccountActions.trigger({
type: EditAccountAction.Rename,
address: pendingAccount?.address,
newName: newAccountName.trim(),
})
)
}
}
return (
<SafeKeyboardOnboardingScreen
subtitle={t('onboarding.editName.subtitle')}
title={t('onboarding.editName.title')}>
{pendingAccount ? (
<CustomizationSection
accountName={newAccountName}
address={pendingAccount?.address}
setAccountName={setNewAccountName}
/>
) : (
<ActivityIndicator />
)}
<Flex justifyContent="flex-end">
<Trace logPress element={ElementName.Continue}>
<Button testID={ElementName.Continue} onPress={onPressNext}>
{t('onboarding.editName.button.create')}
</Button>
</Trace>
</Flex>
</SafeKeyboardOnboardingScreen>
)
}
function CustomizationSection({
address,
accountName,
setAccountName,
}: {
address: Address
accountName: string
setAccountName: Dispatch<SetStateAction<string>>
}): JSX.Element {
const { t } = useTranslation()
const textInputRef = useRef<NativeTextInput>(null)
// we default it to `true` to avoid flickering of a pencil icon,
// because CustomizationSection has `autoFocus=true`
const [focused, setFocused] = useState(true)
const focusInputWithKeyboard = (): void => {
textInputRef.current?.focus()
}
const walletAddress = shortenAddress(address)
return (
<Flex
centered
$short={{
gap: '$none',
}}
gap="$spacing24">
<Flex centered gap="$spacing24" height={200} px="$spacing16" width="100%">
<Flex
borderColor="$surface3"
borderRadius="$rounded16"
borderWidth={1}
py="$spacing12"
width="100%">
<Flex centered row>
<TextInput
ref={textInputRef}
autoFocus
fontSize={fonts.heading3.fontSize}
maxFontSizeMultiplier={fonts.heading3.maxFontSizeMultiplier}
maxLength={NICKNAME_MAX_LENGTH}
placeholder={t('onboarding.editName.label')}
placeholderTextColor="$neutral3"
style={isAndroid ? styles.noHorizontalPadding : {}}
testID={ElementName.WalletNameInput}
textAlign="center"
value={accountName}
onBlur={(): void => {
setFocused(false)
setAccountName(accountName.trim())
}}
onChangeText={setAccountName}
onFocus={(): void => setFocused(true)}
/>
<AnimatePresence>
{!focused && (
<Button
fadeIn
fadeOut
animation="lazy"
icon={<Icons.Pencil color="$neutral2" />}
theme="secondary"
onPress={focusInputWithKeyboard}
/>
)}
</AnimatePresence>
</Flex>
</Flex>
<Flex centered gap="$spacing4">
<Text color="$neutral3" variant="body3">
<Trans
components={{ highlight: <Text color="$neutral3" variant="buttonLabel3" /> }}
i18nKey="onboarding.editName.walletAddress"
values={{ walletAddress }}
/>
</Text>
</Flex>
</Flex>
</Flex>
)
}
const styles = StyleSheet.create({
noHorizontalPadding: {
paddingHorizontal: 0,
},
})
......@@ -10,10 +10,8 @@ import { openModal } from 'src/features/modals/modalSlice'
import { TermsOfService } from 'src/screens/Onboarding/TermsOfService'
import { OnboardingScreens, UnitagScreens } from 'src/screens/Screens'
import { hideSplashScreen } from 'src/utils/splashScreen'
import { isDevBuild } from 'src/utils/version'
import { Button, Flex, HapticFeedback, Text, TouchableArea, useIsDarkMode } from 'ui/src'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { isDevEnv } from 'uniswap/src/utils/env'
import { useTimeout } from 'utilities/src/time/timing'
import { ImportType, OnboardingEntryPoint } from 'wallet/src/features/onboarding/types'
import { useCanAddressClaimUnitag } from 'wallet/src/features/unitags/hooks'
......@@ -31,33 +29,24 @@ export function LandingScreen({ navigation }: Props): JSX.Element {
const { t } = useTranslation()
const isDarkMode = useIsDarkMode()
const unitagsFeatureFlagEnabled = useFeatureFlag(FeatureFlags.Unitags)
const { canClaimUnitag } = useCanAddressClaimUnitag()
const onPressCreateWallet = useCallback((): void => {
dispatch(pendingAccountActions.trigger(PendingAccountActions.Delete))
dispatch(createAccountActions.trigger())
if (unitagsFeatureFlagEnabled) {
if (canClaimUnitag) {
navigation.navigate(UnitagScreens.ClaimUnitag, {
entryPoint: OnboardingScreens.Landing,
})
} else {
// If can't claim, go direct to welcome screen
navigation.navigate(OnboardingScreens.WelcomeWallet, {
importType: ImportType.CreateNew,
entryPoint: OnboardingEntryPoint.FreshInstallOrReplace,
})
}
if (canClaimUnitag) {
navigation.navigate(UnitagScreens.ClaimUnitag, {
entryPoint: OnboardingScreens.Landing,
})
} else {
// use edit nickname screen still before launch of unitags
navigation.navigate(OnboardingScreens.EditName, {
// If can't claim, go direct to welcome screen
navigation.navigate(OnboardingScreens.WelcomeWallet, {
importType: ImportType.CreateNew,
entryPoint: OnboardingEntryPoint.FreshInstallOrReplace,
})
}
}, [canClaimUnitag, dispatch, navigation, unitagsFeatureFlagEnabled])
}, [canClaimUnitag, dispatch, navigation])
const onPressImportWallet = (): void => {
navigation.navigate(OnboardingScreens.ImportMethod, {
......@@ -101,7 +90,7 @@ export function LandingScreen({ navigation }: Props): JSX.Element {
hitSlop={16}
testID={ElementName.ImportAccount}
onLongPress={async (): Promise<void> => {
if (isDevBuild()) {
if (isDevEnv()) {
await HapticFeedback.selection()
dispatch(openModal({ name: ModalName.Experiments }))
}
......
......@@ -51,8 +51,8 @@ import { ONBOARDING_QR_ETCHING_VIDEO_DARK, ONBOARDING_QR_ETCHING_VIDEO_LIGHT } f
import LockIcon from 'ui/src/assets/icons/lock.svg'
import { AnimatedFlex, flexStyles } from 'ui/src/components/layout'
import { fonts, iconSizes, opacify, spacing } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { QRCodeDisplay } from 'wallet/src/components/QRCodeScanner/QRCode'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
import { Arrow } from 'wallet/src/components/icons/Arrow'
......
import { useTranslation } from 'react-i18next'
import { TransferInstitutionSelector } from 'src/features/fiatOnRamp/FiatOnRampTransferInstitutionSelector'
import { FOR_MODAL_SNAP_POINTS } from 'src/features/fiatOnRamp/constants'
import { useAppSelector } from 'src/app/hooks'
import { ServiceProviderSelector } from 'src/features/fiatOnRamp/ExchangeTransferServiceProviderSelector'
import { closeModal, openModal } from 'src/features/modals/modalSlice'
import { selectModalState } from 'src/features/modals/selectModalState'
import {
Flex,
HapticFeedback,
......@@ -106,6 +107,7 @@ export function ReceiveCryptoModal(): JSX.Element {
const colors = useSporeColors()
const dispatch = useAppDispatch()
const { t } = useTranslation()
const { initialState } = useAppSelector(selectModalState(ModalName.ReceiveCryptoModal))
const onClose = (): void => {
dispatch(closeModal({ name: ModalName.ReceiveCryptoModal }))
......@@ -114,14 +116,12 @@ export function ReceiveCryptoModal(): JSX.Element {
return (
<BottomSheetModal
extendOnKeyboardVisible
fullScreen
hideKeyboardOnDismiss
hideKeyboardOnSwipeDown
backgroundColor={colors.surface1.get()}
name={ModalName.ReceiveCryptoModal}
snapPoints={FOR_MODAL_SNAP_POINTS}
onClose={onClose}>
<Flex grow gap="$spacing12" px="$spacing16">
<Flex grow gap="$spacing12" mb="$spacing16" px="$spacing16">
<Flex gap="$spacing4" p="$spacing8">
<Text color="$neutral1" mt="$spacing2" textAlign="center" variant="subheading1">
{t('home.upsell.receive.title')}
......@@ -138,7 +138,7 @@ export function ReceiveCryptoModal(): JSX.Element {
</Text>
<Separator />
</Flex>
<TransferInstitutionSelector onClose={onClose} />
<ServiceProviderSelector serviceProviders={initialState || []} onClose={onClose} />
</Flex>
</BottomSheetModal>
)
......
import { FORServiceProvider } from 'wallet/src/features/fiatOnRamp/types'
export type ReceiveCryptoModalState = FORServiceProvider[]
......@@ -34,7 +34,6 @@ export enum OnboardingScreens {
BackupCloudProcessing = 'OnboardingBackupCloudProcessing',
BackupManual = 'OnboardingBackupManual',
Landing = 'OnboardingLanding',
EditName = 'EditName',
Notifications = 'OnboardingNotifications',
WelcomeWallet = 'WelcomeWallet',
Security = 'OnboardingSecurity',
......
......@@ -47,8 +47,8 @@ import MessageQuestion from 'ui/src/assets/icons/message-question.svg'
import UniswapIcon from 'ui/src/assets/icons/uniswap-logo.svg'
import { iconSizes, spacing } from 'ui/src/theme'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName'
import { isAndroid } from 'uniswap/src/utils/platform'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
......
......@@ -34,13 +34,11 @@ import NotificationIcon from 'ui/src/assets/icons/bell.svg'
import GlobalIcon from 'ui/src/assets/icons/global.svg'
import TextEditIcon from 'ui/src/assets/icons/textEdit.svg'
import { iconSizes, spacing } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
import { Switch } from 'wallet/src/components/buttons/Switch'
import { ChainId } from 'wallet/src/constants/chains'
import { useENS } from 'wallet/src/features/ens/useENS'
import { useUnitagByAddress } from 'wallet/src/features/unitags/hooks'
import {
EditAccountAction,
editAccountActions,
......@@ -74,9 +72,8 @@ export function SettingsWallet({
const [notificationSwitchEnabled, setNotificationSwitchEnabled] = useState<boolean>(
notificationsEnabledOnFirebase
)
const unitagsFeatureFlagEnabled = useFeatureFlag(FeatureFlags.Unitags)
const showEditProfile = unitagsFeatureFlagEnabled && !readonly
const showEditProfile = !readonly
useEffect(() => {
// If the user deletes the account while on this screen, go back
......
......@@ -14,8 +14,6 @@ import { Screen } from 'src/components/layout/Screen'
import { UnitagBanner } from 'src/components/unitags/UnitagBanner'
import { Button, Flex, Icons, Text } from 'ui/src'
import { fonts } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { useFeatureFlag } from 'uniswap/src/features/statsig/hooks'
import { isIOS } from 'uniswap/src/utils/platform'
import { TextInput } from 'wallet/src/components/input/TextInput'
import { NICKNAME_MAX_LENGTH } from 'wallet/src/constants/accounts'
......@@ -42,12 +40,8 @@ export function SettingsWalletEdit({
const displayName = useDisplayName(address)
const [nickname, setNickname] = useState(displayName?.name)
const [showEditButton, setShowEditButton] = useState(true)
const unitagsFeatureFlagEnabled = useFeatureFlag(FeatureFlags.Unitags)
const { canClaimUnitag } = useCanAddressClaimUnitag(address)
const showUnitagBanner =
unitagsFeatureFlagEnabled &&
activeAccount?.type === AccountType.SignerMnemonic &&
canClaimUnitag
const showUnitagBanner = activeAccount?.type === AccountType.SignerMnemonic && canClaimUnitag
const accountNameIsEditable =
displayName?.type === DisplayNameType.Local || displayName?.type === DisplayNameType.Address
......
import DeviceInfo from 'react-native-device-info'
import { isBetaEnv, isDevEnv } from 'uniswap/src/utils/env'
import { StatsigEnvironmentTier } from 'wallet/src/version'
/**
......@@ -24,44 +25,37 @@ export enum BuildVariant {
}
export function getBuildVariant(): BuildVariant {
if (isDevBuild()) {
if (isDevEnv()) {
return BuildVariant.Development
} else if (isBetaBuild()) {
} else if (isBetaEnv()) {
return BuildVariant.Beta
} else {
return BuildVariant.Production
}
}
export function isDevBuild(): boolean {
return DeviceInfo.getBundleId().endsWith('.dev')
}
export function isBetaBuild(): boolean {
return DeviceInfo.getBundleId().endsWith('.beta')
}
export function getStatsigEnvironmentTier(): StatsigEnvironmentTier {
if (isDevBuild()) {
if (isDevEnv()) {
return StatsigEnvironmentTier.DEV
}
if (isBetaBuild()) {
if (isBetaEnv()) {
return StatsigEnvironmentTier.BETA
}
return StatsigEnvironmentTier.PROD
}
export function getSentryEnvironment(): SentryEnvironment {
if (isDevBuild()) {
if (isDevEnv()) {
return SentryEnvironment.DEV
}
if (isBetaBuild()) {
if (isBetaEnv()) {
return SentryEnvironment.BETA
}
return SentryEnvironment.PROD
}
export function getSentryTracesSamplingRate(): number {
if (isDevBuild() || isBetaBuild()) {
if (isDevEnv() || isBetaEnv()) {
return 1
}
return 0.2
......
......@@ -13,6 +13,8 @@ ignores: [
"process",
"madge",
# Dependencies that depcheck thinks are missing but are actually present or never used
## package.json scripts
"esbuild-register",
## GraphQL
"@graphql-codegen/*",
"get-graphql-schema",
......
......@@ -17,6 +17,5 @@ REACT_APP_TEMP_API_URL="https://temp.gateway.uniswap.org/v1"
REACT_APP_UNISWAP_API_URL="https://interface.gateway.uniswap.org/v2"
REACT_APP_UNISWAP_BASE_API_URL="https://interface.gateway.uniswap.org"
REACT_APP_UNISWAP_GATEWAY_DNS="https://interface.gateway.uniswap.org/v2"
REACT_APP_UNITAGS_API_URL="https://gateway.uniswap.org/v2/unitags"
REACT_APP_WALLET_CONNECT_PROJECT_ID="c6c9bacd35afa3eb9e6cccf6d8464395"
REACT_APP_IS_UNISWAP_INTERFACE=true
......@@ -46,6 +46,11 @@ module.exports = {
importNames: ['usePortfolioBalancesQuery', 'usePortfolioBalancesWebLazyQuery'],
message: 'Import cached/subscription-based balance hooks from `TokenBalancesProvider.tsx` instead.',
},
{
name: 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks',
importNames: ['useActivityWebQuery'],
message: 'Import cached/subscription-based activity hooks from `AssetActivityProvider` instead.',
},
{
name: '@uniswap/smart-order-router',
message: 'Only import types, unless you are in the client-side SOR, to preserve lazy-loading.',
......
......@@ -13,12 +13,6 @@ yarn
yarn web start
```
## Unsupported tokens
Check out `useUnsupportedTokenList()` in [src/state/lists/hooks.ts](./src/state/lists/hooks.ts) for blocking tokens in your instance of the interface.
You can block an entire list of tokens by passing in a tokenlist like [here](./src/constants/lists.ts)
## Accessing Uniswap V2
The Uniswap Interface supports swapping, adding liquidity, removing liquidity and migrating liquidity for Uniswap protocol V2.
......
......@@ -8,6 +8,7 @@ const path = require('path')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
const { IgnorePlugin, ProvidePlugin } = require('webpack')
const { RetryChunkLoadPlugin } = require('webpack-retry-chunk-load-plugin')
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')
const commitHash = execSync('git rev-parse HEAD').toString().trim()
const isProduction = process.env.NODE_ENV === 'production'
......@@ -92,6 +93,15 @@ module.exports = {
}),
],
configure: (webpackConfig) => {
if (isProduction || process.env.UNISWAP_ANALYZE_BUNDLE_SIZE) {
// do bundle analysis
webpackConfig.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'json',
})
)
}
// Configure webpack plugins:
webpackConfig.plugins = webpackConfig.plugins
.map((plugin) => {
......
......@@ -2,6 +2,7 @@ import { getTestSelector } from '../utils'
describe('Buy Crypto Modal', () => {
it('should open and close', () => {
cy.intercept('https://api.moonpay.com/v4/ip_address?apiKey=*', { fixture: 'moonpay/ip_address_valid.json' })
cy.visit('/')
// Open the fiat onramp modal
......@@ -14,6 +15,7 @@ describe('Buy Crypto Modal', () => {
})
it('should open and close, mobile viewport', () => {
cy.intercept('https://api.moonpay.com/v4/ip_address?apiKey=*', { fixture: 'moonpay/ip_address_valid.json' })
cy.viewport('iphone-6')
cy.visit('/')
......@@ -25,4 +27,13 @@ describe('Buy Crypto Modal', () => {
cy.get('body').click(10, 10)
cy.get(getTestSelector('fiat-onramp-modal')).should('not.exist')
})
it("should not open if the user's region is not supported", () => {
cy.intercept('https://api.moonpay.com/v4/ip_address?apiKey=*', { fixture: 'moonpay/ip_address_invalid.json' })
cy.visit('/')
// Try to open the fiat onramp modal
cy.get(getTestSelector('buy-fiat-button')).click()
cy.get(getTestSelector('fiat-onramp-modal')).should('not.exist')
})
})
......@@ -72,7 +72,7 @@ describe('Landing Page', () => {
})
it('renders uk compliance banner in uk', () => {
cy.intercept('https://interface.gateway.uniswap.org/v1/amplitude-proxy', (req) => {
cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v1\/amplitude-proxy/, (req) => {
const requestBody = JSON.stringify(req.body)
const byteSize = new Blob([requestBody]).size
req.alias = 'amplitude'
......@@ -89,7 +89,8 @@ describe('Landing Page', () => {
)
})
cy.visit('/swap')
cy.contains('UK disclaimer')
cy.contains('Read more').click()
cy.contains('Disclaimer for UK residents')
})
it('shows a nav button to download the app when feature is enabled', () => {
......
......@@ -64,17 +64,16 @@ describe('Swap errors', () => {
})
it('slippage failure', () => {
cy.visit(`/swap?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
cy.hardhat({ automine: false }).then(async (hardhat) => {
await hardhat.fund(hardhat.wallet, CurrencyAmount.fromRawAmount(USDC_MAINNET, 500e6))
await hardhat.mine()
await Promise.all([
hardhat.approval.setTokenAllowanceForPermit2({ owner: hardhat.wallet, token: USDC_MAINNET }),
hardhat.approval.setPermit2Allowance({ owner: hardhat.wallet, token: USDC_MAINNET }),
])
await hardhat.approval.setTokenAllowanceForPermit2({ owner: hardhat.wallet, token: USDC_MAINNET })
await hardhat.approval.setPermit2Allowance({ owner: hardhat.wallet, token: USDC_MAINNET })
await hardhat.mine()
})
cy.visit(`/swap?inputCurrency=${USDC_MAINNET.address}&outputCurrency=${DAI.address}`)
getBalance(DAI).then((initialBalance) => {
// Gas estimation fails for this transaction (that would normally fail), so we stub it.
cy.hardhat().then((hardhat) => {
......
import { CurrencyAmount } from '@uniswap/sdk-core'
import { URADutchOrderQuoteResponse } from 'state/routing/types'
import { URAQuoteResponse } from 'state/routing/types'
import { USDC_MAINNET } from '../../../src/constants/tokens'
import { getBalance, getTestSelector } from '../../utils'
......@@ -120,7 +120,7 @@ describe('Swap with fees', () => {
it('displays UniswapX fee in UI', () => {
cy.visit('/swap')
cy.fixture('uniswapx/feeQuote.json').then((fixture: URADutchOrderQuoteResponse) => {
cy.fixture('uniswapx/feeQuote.json').then((fixture: URAQuoteResponse) => {
// Intercept the trade quote
cy.intercept({ url: 'https://interface.gateway.uniswap.org/v2/quote' }, (req) => {
// Avoid intercepting stablecoin pricing fetches
......
......@@ -54,9 +54,12 @@ describe('Swap', () => {
it('swaps ETH for USDC', () => {
cy.interceptGraphqlOperation('Activity', 'mini-portfolio/empty_activity.json')
cy.interceptQuoteRequest('swap_eth_usdc_classic.json')
cy.visit('/swap')
cy.hardhat({ automine: false })
getBalance(USDC_MAINNET).then((initialBalance) => {
cy.get(`#swap-currency-input .token-symbol-container`).should('contain.text', 'ETH')
// Select USDC
cy.get('#swap-currency-output .open-currency-select-button').click()
cy.get(getTestSelector('token-search-input')).type(USDC_MAINNET.address)
......
......@@ -383,13 +383,17 @@ describe('UniswapX activity history', () => {
cy.get('#swap-currency-input .token-amount-input').type('300')
const gqlSpy = cy.spy().as('gqlSpy')
cy.intercept(/graphql/, (req) => {
// Spy on request frequency
req.on('response', gqlSpy)
// Reply with a fixture to speed up test
req.reply({
fixture: 'mini-portfolio/tokens.json',
})
cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v1\/graphql/, (req) => {
if (req.body.operationName === 'PortfolioBalancesWeb') {
// Spy on request frequency
req.on('response', gqlSpy)
// Reply with a fixture to speed up test
req.reply({
fixture: 'mini-portfolio/tokens.json',
})
} else {
req.continue()
}
})
// Expect balances to fetch upon opening mini portfolio
......@@ -400,13 +404,10 @@ describe('UniswapX activity history', () => {
cy.get('#swap-button').click()
cy.contains('Confirm swap').click()
// Expect balances to refetch after approval
cy.get('@gqlSpy').should('have.been.calledTwice')
// Return filled order status from uniswapx api
cy.intercept(OrderStatusEndpoint, { fixture: 'uniswapx/filledStatusResponse.json' })
// Expect balances to refetch after swap
cy.get('@gqlSpy').should('have.been.calledThrice')
cy.get('@gqlSpy').should('have.been.calledTwice')
})
})
import { ChainId, WETH9 } from '@uniswap/sdk-core'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { shortenAddress } from 'utilities/src/addresses'
import { ARB, UNI } from '../../src/constants/tokens'
import { getTestSelector } from '../utils'
......@@ -51,8 +52,12 @@ describe('Token details', () => {
})
it('token with warning and low trading volume should have all information populated', () => {
cy.interceptGraphqlOperation('SimpleToken', 'simple_token_warning.json')
// Null token created for this test, 0 trading volume and has warning modal
cy.visit('/explore/tokens/ethereum/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
cy.visit('/explore/tokens/ethereum/0x1eFBB78C8b917f67986BcE54cE575069c0143681', {
featureFlags: [{ flag: FeatureFlags.GqlTokenLists, value: true }],
})
// Should have missing price view when price unavailable (expected for this token)
cy.get('[data-cy="chart-error-view"]').should('exist')
......
import { FeatureFlags } from 'uniswap/src/features/statsig/flags'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { getTestSelector } from '../utils'
describe('Wallet Dropdown', () => {
......
{
"alpha2": "US",
"alpha3": "USA",
"country": "United States of America",
"ipAddress": "142.154.213.230",
"isAllowed": true,
"isBuyAllowed": false,
"isNftAllowed": true,
"isSellAllowed": false,
"isBalanceLedgerWithdrawAllowed": true,
"isLowLimitEnabled": false,
"state": "NY"
}
{
"alpha2": "US",
"alpha3": "USA",
"country": "United States of America",
"ipAddress": "142.154.213.230",
"isAllowed": true,
"isBuyAllowed": true,
"isNftAllowed": true,
"isSellAllowed": true,
"isBalanceLedgerWithdrawAllowed": true,
"isLowLimitEnabled": true,
"state": "NY"
}
{
"data": {
"token": {
"id": "VG9rZW46RVRIRVJFVU1fbnVsbA==",
"address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"chain": "ETHEREUM",
"symbol": "USDC",
"name": "USD Coin",
"decimals": 6,
"standard": "ERC20",
"project": {
"id": "VG9rZW5Qcm9qZWN0OkVUSEVSRVVNX251bGxfRXRoZXJldW0=",
"name": "Circle: USDC",
"logo": {
"id": "SW1hZ2U6aHR0cHM6Ly90b2tlbi1pY29ucy5zMy5hbWF6b25hd3MuY29tL2V0aC5wbmc=",
"url": "https://etherscan.io/token/images/centre-usdc_28.png",
"__typename": "Image"
},
"safetyLevel": "VERIFIED",
"logoUrl": "https://etherscan.io/token/images/centre-usdc_28.png",
"isSpam": false,
"__typename": "TokenProject"
},
"__typename": "Token"
}
}
}
{
"data": {
"token": {
"id": "test-0x1eFBB78C8b917f67986BcE54cE575069c0143681",
"address": "0x1eFBB78C8b917f67986BcE54cE575069c0143681",
"chain": "ETHEREUM",
"symbol": "test",
"name": "test token",
"decimals": 18,
"project": {
"id": "VG9rZW5Qcm9qZWN0OkVUSEVSRVVNXzB4NmIxNzU0NzRlODkwOTRjNDRkYTk4Yjk1NGVlZGVhYzQ5NTI3MWQwZl9EYWk=",
"name": "Dai",
"logo": {
"id": "SW1hZ2U6aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1VuaXN3YXAvYXNzZXRzL21hc3Rlci9ibG9ja2NoYWlucy9ldGhlcmV1bS9hc3NldHMvMHg2QjE3NTQ3NEU4OTA5NEM0NERhOThiOTU0RWVkZUFDNDk1MjcxZDBGL2xvZ28ucG5n",
"url": "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1eFBB78C8b917f67986BcE54cE575069c0143681/logo.png",
"__typename": "Image"
},
"safetyLevel": "STRONG_WARNING",
"logoUrl": "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x1eFBB78C8b917f67986BcE54cE575069c0143681/logo.png",
"isSpam": false,
"__typename": "TokenProject"
},
"__typename": "Token"
}
}
}
{
"routing": "CLASSIC",
"quote": {
"methodParameters": {
"calldata": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000661ebbb400000000000000000000000000000000000000000000000000000000000000050b0905040c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000012c5b11105b570000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000f4c0400000000000000000000000000000000000000000000000000012c5b11105b5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000027213e28d7fda5c57fe9e5dd923818dbccf71c4700000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000",
"value": "0x012c5b11105b57",
"to": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD"
},
"amount": "1000000",
"amountDecimals": "1",
"quote": "327782153215338",
"quoteDecimals": "0.000327782153215338",
"quoteGasAdjusted": "3754905339323778",
"quoteGasAdjustedDecimals": "0.003754905339323778",
"quoteGasAndPortionAdjusted": "3754905339323778",
"quoteGasAndPortionAdjustedDecimals": "0.003754905339323778",
"gasUseEstimateQuote": "3427123186108440",
"gasUseEstimateQuoteDecimals": "0.00342712318610844",
"gasUseEstimate": "264285",
"gasUseEstimateUSD": "10.481019768575804317",
"simulationStatus": "SUCCESS",
"simulationError": false,
"gasPriceWei": "12967528184",
"route": [
[
{
"type": "v2-pool",
"address": "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc",
"tokenIn": {
"chainId": 1,
"decimals": "18",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"symbol": "WETH"
},
"tokenOut": {
"chainId": 1,
"decimals": "6",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC",
"buyFeeBps": "0",
"sellFeeBps": "0"
},
"reserve0": {
"token": {
"chainId": 1,
"decimals": "6",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC",
"buyFeeBps": "0",
"sellFeeBps": "0"
},
"quotient": "43318082931464"
},
"reserve1": {
"token": {
"chainId": 1,
"decimals": "18",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"symbol": "WETH"
},
"quotient": "14156297485330702299324"
},
"amountIn": "328601608598376",
"amountOut": "1002500"
}
]
],
"routeString": "[V2] 100.00% = WETH -- [0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc] --> USDC",
"quoteId": "471c9a76-3b5d-4bc2-9f6e-11b2bb199d57",
"portionBips": 25,
"portionRecipient": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47",
"portionAmount": "2500",
"portionAmountDecimals": "0.0025",
"requestId": "60fd2230-ef27-49fa-aef7-9a44fe8216e2",
"tradeType": "EXACT_OUTPUT",
"slippage": 0.5
},
"requestId": "60fd2230-ef27-49fa-aef7-9a44fe8216e2",
"allQuotes": [
null,
{
"routing": "CLASSIC",
"quote": {
"methodParameters": {
"calldata": "0x3593564c000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000661ebbb400000000000000000000000000000000000000000000000000000000000000050b0905040c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002a000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000012c5b11105b570000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000f4c0400000000000000000000000000000000000000000000000000012c5b11105b5700000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000027213e28d7fda5c57fe9e5dd923818dbccf71c4700000000000000000000000000000000000000000000000000000000000009c40000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb9226600000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000040000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000000000000000000000000000000000000000000000000000",
"value": "0x012c5b11105b57",
"to": "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD"
},
"blockNumber": "19669619",
"amount": "1000000",
"amountDecimals": "1",
"quote": "327782153215338",
"quoteDecimals": "0.000327782153215338",
"quoteGasAdjusted": "3754905339323778",
"quoteGasAdjustedDecimals": "0.003754905339323778",
"quoteGasAndPortionAdjusted": "3754905339323778",
"quoteGasAndPortionAdjustedDecimals": "0.003754905339323778",
"gasUseEstimateQuote": "3427123186108440",
"gasUseEstimateQuoteDecimals": "0.00342712318610844",
"gasUseEstimate": "264285",
"gasUseEstimateUSD": "10.481019768575804317",
"simulationStatus": "SUCCESS",
"simulationError": false,
"gasPriceWei": "12967528184",
"route": [
[
{
"type": "v2-pool",
"address": "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc",
"tokenIn": {
"chainId": 1,
"decimals": "18",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"symbol": "WETH"
},
"tokenOut": {
"chainId": 1,
"decimals": "6",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC",
"buyFeeBps": "0",
"sellFeeBps": "0"
},
"reserve0": {
"token": {
"chainId": 1,
"decimals": "6",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC",
"buyFeeBps": "0",
"sellFeeBps": "0"
},
"quotient": "43318082931464"
},
"reserve1": {
"token": {
"chainId": 1,
"decimals": "18",
"address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"symbol": "WETH"
},
"quotient": "14156297485330702299324"
},
"amountIn": "328601608598376",
"amountOut": "1002500"
}
]
],
"routeString": "[V2] 100.00% = WETH -- [0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc] --> USDC",
"quoteId": "471c9a76-3b5d-4bc2-9f6e-11b2bb199d57",
"portionBips": 25,
"portionRecipient": "0x27213E28D7fDA5c57Fe9e5dD923818DBCcf71c47",
"portionAmount": "2500",
"portionAmountDecimals": "0.0025",
"requestId": "60fd2230-ef27-49fa-aef7-9a44fe8216e2",
"tradeType": "EXACT_OUTPUT",
"slippage": 0.5
}
}
]
}
......@@ -2,7 +2,7 @@ import 'cypress-hardhat/lib/browser'
import { Eip1193Bridge } from '@ethersproject/experimental/lib/eip1193-bridge'
import { FeatureFlagClient, FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/statsig/flags'
import { FeatureFlagClient, FeatureFlags, getFeatureFlagName } from 'uniswap/src/features/gating/flags'
import { UserState, initialState } from '../../src/state/user/reducer'
import { CONNECTED_WALLET_USER_STATE, setInitialUserState } from '../utils/user-state'
......@@ -27,6 +27,11 @@ declare global {
* @param {string} fixturePath - The path to the fixture to respond with.
*/
interceptGraphqlOperation(operationName: string, fixturePath: string): Chainable<Subject>
/**
* Intercepts a quote request and responds with the given fixture.
* @param {string} fixturePath - The path to the fixture to respond with.
*/
interceptQuoteRequest(fixturePath: string): Chainable<Subject>
}
interface VisitOptions {
featureFlags?: Array<{ flag: FeatureFlags; value: boolean }>
......@@ -123,6 +128,7 @@ Cypress.Commands.add('waitForAmplitudeEvent', (eventName, requiredProperties) =>
Cypress.Commands.add('interceptGraphqlOperation', (operationName, fixturePath) => {
return cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v1\/graphql/, (req) => {
req.headers['origin'] = 'https://app.uniswap.org'
if (req.body.operationName === operationName) {
req.reply({ fixture: fixturePath })
} else {
......@@ -130,3 +136,10 @@ Cypress.Commands.add('interceptGraphqlOperation', (operationName, fixturePath) =
}
})
})
Cypress.Commands.add('interceptQuoteRequest', (fixturePath) => {
return cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v2\/quote/, (req) => {
req.headers['origin'] = 'https://app.uniswap.org'
req.reply({ fixture: fixturePath })
})
})
// @ts-ignore
import TokenListJSON from '@uniswap/default-token-list'
import { CyHttpMessages } from 'cypress/types/net-stubbing'
beforeEach(() => {
......@@ -41,11 +40,6 @@ beforeEach(() => {
// Mock statsig to allow us to mock flags.
cy.intercept(/statsig/, { statusCode: 409 })
// Mock our own token list responses to avoid the latency of IPFS.
cy.intercept('https://gateway.ipfs.io/ipns/tokens.uniswap.org', TokenListJSON)
.intercept('https://gateway.ipfs.io/ipns/extendedtokens.uniswap.org', { statusCode: 404 })
.intercept('https://gateway.ipfs.io/ipns/unsupportedtokens.uniswap.org', { statusCode: 404 })
// Reset hardhat between tests to ensure isolation.
// This resets the fork, as well as options like automine.
cy.hardhat().then((hardhat) => hardhat.reset())
......
......@@ -31,12 +31,6 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
const networkLogo = getNetworkLogoUrl(networkName.toUpperCase(), origin)
// Capitalize name such that each word starts with a capital letter
let words = data.name.split(' ')
words = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
let name = words.join(' ')
name = name.trim()
return new ImageResponse(
(
<div
......@@ -77,7 +71,6 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
position: 'absolute',
right: '2px',
bottom: '0px',
borderRadius: '100%',
}}
/>
)}
......@@ -112,7 +105,6 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
position: 'absolute',
right: '2px',
bottom: '0px',
borderRadius: '100%',
}}
/>
)}
......@@ -127,7 +119,7 @@ export const onRequest: PagesFunction = async ({ params, request }) => {
marginTop: '24px',
}}
>
{name}
{data.name}
</div>
<div
style={{
......
......@@ -10,6 +10,14 @@ export default function getNetworkLogoUrl(network: string, origin: string) {
return origin + '/images/logos/Optimism_Logo.png'
case Chain.Celo:
return origin + '/images/logos/Celo_Logo.png'
case Chain.Base:
return origin + '/images/logos/Base_Logo.png'
case Chain.Bnb:
return origin + '/images/logos/BNB_Logo.png'
case Chain.Avalanche:
return origin + '/images/logos/Avax_Logo.png'
case Chain.Blast:
return origin + '/images/logos/Blast_Logo.png'
default:
return ''
}
......
......@@ -6,7 +6,7 @@
"scripts": {
"ajv": "node scripts/compile-ajv-validators.js",
"check:deps:usage": "depcheck",
"check:circular": "concurrently \"../../scripts/check-circular-imports.sh ./src/pages/App.tsx 8\" \"../../scripts/check-circular-imports.sh ./src/setupTests.ts 0\"",
"check:circular": "concurrently \"../../scripts/check-circular-imports.sh ./src/pages/App.tsx 7\" \"../../scripts/check-circular-imports.sh ./src/setupTests.ts 0\"",
"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",
......@@ -30,6 +30,7 @@
"typecheck:cloud": "tsc -p functions/tsconfig.json",
"typecheck:cypress": "tsc -p cypress/tsconfig.json",
"test": "craco test",
"test:bundle": "node -r esbuild-register ./src/test-utils/bundle-size-test.ts",
"snapshots": "craco test -u",
"test:cloud": "yarn jest functions --config=functions/jest.config.json",
"cypress:open": "cypress open --browser chrome --e2e",
......@@ -91,10 +92,12 @@
"@swc/plugin-styled-components": "1.5.97",
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "13.4.0",
"@testing-library/react-hooks": "7.0.2",
"@testing-library/user-event": "14.5.1",
"@typechain/ethers-v5": "7.2.0",
"@types/array.prototype.flat": "1.2.5",
"@types/array.prototype.flatmap": "1.2.6",
"@types/chrome": "0.0.254",
"@types/d3": "7.4.3",
"@types/jest": "29.5.0",
"@types/ms": "0.7.31",
......@@ -126,6 +129,7 @@
"cypress-hardhat": "2.5.0",
"dotenv": "16.0.3",
"dotenv-cli": "^7.0.0",
"esbuild-register": "^3.5.0",
"eslint": "8.44.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-rulesdir": "0.2.2",
......@@ -156,6 +160,7 @@
"tsafe": "1.6.4",
"typescript": "5.3.3",
"webpack": "5.90.0",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-retry-chunk-load-plugin": "3.1.1",
"wrangler": "3.15.0",
"yarn-deduplicate": "6.0.0"
......@@ -171,7 +176,6 @@
"@graphql-codegen/typescript-resolvers": "^3.2.1",
"@juggle/resize-observer": "3.4.0",
"@looksrare/sdk": "0.10.4",
"@metamask/jazzicon": "2.0.0",
"@opensea/seaport-js": "1.3.0",
"@popperjs/core": "2.11.8",
"@reach/dialog": "0.10.5",
......@@ -182,8 +186,8 @@
"@sentry/core": "7.80.0",
"@sentry/react": "7.80.0",
"@sentry/types": "7.80.0",
"@tamagui/core": "1.94.3",
"@tamagui/react-native-svg": "1.94.3",
"@tamagui/core": "1.94.5",
"@tamagui/react-native-svg": "1.94.5",
"@tanstack/react-query": "5.28.14",
"@tanstack/react-table": "8.10.7",
"@types/poisson-disk-sampling": "2.2.4",
......@@ -194,20 +198,20 @@
"@uniswap/governance": "1.0.2",
"@uniswap/liquidity-staker": "1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/permit2-sdk": "1.2.0",
"@uniswap/permit2-sdk": "1.2.1-beta.1",
"@uniswap/redux-multicall": "1.1.8",
"@uniswap/router-sdk": "1.9.0",
"@uniswap/sdk-core": "4.2.0",
"@uniswap/router-sdk": "1.9.1-beta.2",
"@uniswap/sdk-core": "4.2.1-beta.1",
"@uniswap/smart-order-router": "3.17.3",
"@uniswap/token-lists": "1.0.0-beta.33",
"@uniswap/uniswapx-sdk": "2.0.1-alpha.4",
"@uniswap/universal-router-sdk": "1.8.2",
"@uniswap/uniswapx-sdk": "2.0.3-alpha.1",
"@uniswap/universal-router-sdk": "2.0.4-beta.3",
"@uniswap/v2-core": "1.0.1",
"@uniswap/v2-periphery": "1.1.0-beta.0",
"@uniswap/v2-sdk": "4.3.0",
"@uniswap/v2-sdk": "4.3.1-beta.2",
"@uniswap/v3-core": "1.0.1",
"@uniswap/v3-periphery": "1.4.4",
"@uniswap/v3-sdk": "3.11.0",
"@uniswap/v3-sdk": "3.11.1-beta.2",
"@vanilla-extract/css": "1.14.0",
"@vanilla-extract/dynamic": "2.1.0",
"@vanilla-extract/sprinkles": "1.6.1",
......@@ -286,6 +290,7 @@
"statsig-react": "1.32.0",
"styled-components": "5.3.11",
"tiny-invariant": "1.3.1",
"ui": "workspace:^",
"uniswap": "workspace:^",
"use-resize-observer": "9.1.0",
"utilities": "workspace:^",
......
......@@ -85,7 +85,6 @@
"https://*.base.org/",
"https://*.walletconnect.com",
"https://ethereum-optimism.github.io/",
"https://*.twnodes.com",
"https://forno.celo.org/",
"https://*.gemini.com",
"https://gateway.ipfs.io/",
......
apps/web/public/images/logos/Celo_Logo.png

4.45 KB | W: | H:

apps/web/public/images/logos/Celo_Logo.png

705 Bytes | W: | H:

apps/web/public/images/logos/Celo_Logo.png
apps/web/public/images/logos/Celo_Logo.png
apps/web/public/images/logos/Celo_Logo.png
apps/web/public/images/logos/Celo_Logo.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -24,7 +24,7 @@ import { useAppDispatch } from 'state/hooks'
import { setRecentConnectionDisconnected } from 'state/user/reducer'
import styled from 'styled-components'
import { ThemedText } from 'theme/components'
import { useUnitagByAddressWithoutFlag } from 'uniswap/src/features/unitags/hooksWithoutFlags'
import { useUnitagByAddress } from 'uniswap/src/features/unitags/hooks'
import { isPathBlocked } from 'utils/blockedPaths'
import { NumberType, useFormatter } from 'utils/formatNumbers'
import { useCloseModal, useFiatOnrampAvailability, useOpenModal, useToggleModal } from '../../state/application/hooks'
......@@ -156,14 +156,14 @@ export default function AuthenticatedHeader({ account, openSettings }: { account
error || (!fiatOnrampAvailable && fiatOnrampAvailabilityChecked) || fiatOnrampAvailabilityLoading
)
const { data: portfolioBalances } = useTokenBalancesQuery({ skip: !accountDrawerOpen })
const { data: portfolioBalances } = useTokenBalancesQuery({ cacheOnly: !accountDrawerOpen })
const portfolio = portfolioBalances?.portfolios?.[0]
const totalBalance = portfolio?.tokensTotalDenominatedValue?.value
const absoluteChange = portfolio?.tokensTotalDenominatedValueChange?.absolute?.value
const percentChange = portfolio?.tokensTotalDenominatedValueChange?.percentage?.value
const [showDisconnectConfirm, setShowDisconnectConfirm] = useState(false)
const { unitag } = useUnitagByAddressWithoutFlag(account, Boolean(account))
const { unitag } = useUnitagByAddress(account)
const amount = unclaimedAmount?.toFixed(0, { groupSeparator: ',' } ?? '-')
return (
......
import { ChainId, Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import {
CancelLimitsDialog,
CancellationState,
......@@ -26,15 +25,10 @@ import { Divider, ThemedText } from 'theme/components'
import { UniswapXOrderStatus } from 'types/uniswapx'
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
import { PERMIT2_ADDRESS } from '@uniswap/permit2-sdk'
import { sendAnalyticsEvent } from 'analytics'
import { cancelMultipleUniswapXOrders } from 'components/AccountDrawer/MiniPortfolio/Activity/utils'
import { useCancelMultipleOrdersCallback } from 'components/AccountDrawer/MiniPortfolio/Activity/utils'
import AlertTriangleFilled from 'components/Icons/AlertTriangleFilled'
import { LimitDisclaimer } from 'components/swap/LimitDisclaimer'
import { ContractTransaction } from 'ethers/lib/ethers'
import { useContract } from 'hooks/useContract'
import PERMIT2_ABI from 'uniswap/src/abis/permit2.json'
import { Permit2 } from 'uniswap/src/abis/types/Permit2'
import { PortfolioLogo } from '../PortfolioLogo'
import { OffchainOrderLineItem, OffchainOrderLineItemProps, OffchainOrderLineItemType } from './OffchainOrderLineItem'
......@@ -150,6 +144,8 @@ function getOrderTitle(order: UniswapXOrderDetails): ReactNode {
return isLimit ? <Trans>Limit pending</Trans> : <Trans>Order pending</Trans>
case UniswapXOrderStatus.EXPIRED:
return isLimit ? <Trans>Limit expired</Trans> : <Trans>Order expired</Trans>
case UniswapXOrderStatus.PENDING_CANCELLATION:
return <Trans>Pending cancellation</Trans>
case UniswapXOrderStatus.INSUFFICIENT_FUNDS:
case UniswapXOrderStatus.CANCELLED:
return isLimit ? <Trans>Limit cancelled</Trans> : <Trans>Order cancelled</Trans>
......@@ -160,20 +156,6 @@ function getOrderTitle(order: UniswapXOrderDetails): ReactNode {
}
}
function useCancelOrder(order?: UniswapXOrderDetails): () => Promise<ContractTransaction[] | undefined> {
const { provider } = useWeb3React()
const permit2 = useContract<Permit2>(PERMIT2_ADDRESS, PERMIT2_ABI, true)
return useCallback(async () => {
if (!order) return undefined
return await cancelMultipleUniswapXOrders({
orders: [{ encodedOrder: order.encodedOrder as string, type: order.type as SignatureType }],
chainId: order.chainId,
provider,
permit2,
})
}, [order, permit2, provider])
}
export function OrderContent({
order,
logos,
......@@ -335,7 +317,9 @@ export function OffchainActivityModal() {
setSelectedOrder((order) => order && { ...order, modalOpen: false })
}, [setSelectedOrder])
const cancelOrder = useCancelOrder(syncedSelectedOrder)
const cancelOrder = useCancelMultipleOrdersCallback(
useMemo(() => [syncedSelectedOrder].filter(Boolean) as Array<UniswapXOrderDetails>, [syncedSelectedOrder])
)
return (
<>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment