ci(release): publish latest release

parent fc68138e
IPFS hash of the deployment:
- CIDv0: `QmQcU3yDYBfnzcB31iYwTcW41JwYaQabvdaaM28WCXf8Ws`
- CIDv1: `bafybeibbyso4k2bvdabn3u3s3ubzaxmszuwtjlhppj5zjjfeex2g4rhoga`
- CIDv0: `QmZCHeoJuPsFdadwYDZ1gxfLvYaaRxv5pGxTRcmSGXqSgP`
- CIDv1: `bafybeifbjqm3qbn3nu6diizot5gr24pw2dai5r6l5iafo5y4t5nufursoa`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
......@@ -10,15 +10,39 @@ You can also access the Uniswap Interface from an IPFS gateway.
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeibbyso4k2bvdabn3u3s3ubzaxmszuwtjlhppj5zjjfeex2g4rhoga.ipfs.dweb.link/
- https://bafybeibbyso4k2bvdabn3u3s3ubzaxmszuwtjlhppj5zjjfeex2g4rhoga.ipfs.cf-ipfs.com/
- [ipfs://QmQcU3yDYBfnzcB31iYwTcW41JwYaQabvdaaM28WCXf8Ws/](ipfs://QmQcU3yDYBfnzcB31iYwTcW41JwYaQabvdaaM28WCXf8Ws/)
- https://bafybeifbjqm3qbn3nu6diizot5gr24pw2dai5r6l5iafo5y4t5nufursoa.ipfs.dweb.link/
- https://bafybeifbjqm3qbn3nu6diizot5gr24pw2dai5r6l5iafo5y4t5nufursoa.ipfs.cf-ipfs.com/
- [ipfs://QmZCHeoJuPsFdadwYDZ1gxfLvYaaRxv5pGxTRcmSGXqSgP/](ipfs://QmZCHeoJuPsFdadwYDZ1gxfLvYaaRxv5pGxTRcmSGXqSgP/)
### 5.25.7 (2024-05-09)
## 5.26.0 (2024-05-09)
### Features
* **web:** add dynamic metatag updating w/ react-helmet-async (#7732) 4afd49c
* **web:** add multichain ux flag (#7796) 870f18f
* **web:** automate sitemap generation during prod deploy (#7846) 6010986
* **web:** migrate user state back to localStorage (#7452) b8ff545
### Bug Fixes
* **web:** hotfix warning modified extensions (#8042) ac4781d
* **web:** add "Safe" RPC URLs to CSP (#7844) 7880af0
* **web:** align price impact in details and warning (#7734) 65303d4
* **web:** convert useQuery to use QueryOptions (#7729) da4104c
* **web:** disable flaky tests in merge queue runs (#7771) c5fc13b
* **web:** fix token lookups from network for backend-unsupported chains (#7894) 2bcd438
* **web:** fix tokens lookups for avax (#7893) f4deeb6
* **web:** Flip logic for Sells/Buys (#7880) cffd9ec
* **web:** hotfix warning modified extensions (#8073) deeeb4f
* **web:** infinite loop when connecting to unsupported chain (#7891) 4df2b6a
* **web:** pool page stuck in rerender loop (#7892) 45541af
* **web:** reduce gap in AdvancedSwapDetails component (#7733) 278f948
* **web:** refactor chain URL param handling (#7807) a33bc3d
* **web:** remove limits+send feature flags & fix broken /limit page refresh (#7870) 6c5eaa9
* **web:** serialize polling tx receipts (#7908) 3c44924
* **web:** support swapping spam tokens (#7793) 020a1e4
* **web:** update the CTAs on pools page to link to explore/pools page (#7808) cb625d8
* **web:** width of account bottom sheet on mobile (#7886) 03ebbd0
web/5.25.7
\ No newline at end of file
web/5.26.0
\ No newline at end of file
......@@ -131,17 +131,17 @@ android {
dev {
isDefault(true)
applicationIdSuffix ".dev"
versionName "1.26"
versionName "1.27"
dimension "variant"
}
beta {
applicationIdSuffix ".beta"
versionName "1.26"
versionName "1.27"
dimension "variant"
}
prod {
dimension "variant"
versionName "1.26"
versionName "1.27"
}
}
......
......@@ -185,6 +185,7 @@
9FEC9B8B2A858CF1003CD019 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FEC9B8A2A858CF1003CD019 /* AppDelegate.m */; };
A32F9FBD272343C9002CFCDB /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A32F9FBC272343C8002CFCDB /* GoogleService-Info.plist */; };
A3F0A5B1272B1DFA00895B25 /* KeychainSwiftDistrib.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3F0A5B0272B1DFA00895B25 /* KeychainSwiftDistrib.swift */; };
AC0EE0982BD826E700BCCF07 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = AC0EE0972BD826E700BCCF07 /* PrivacyInfo.xcprivacy */; };
AEE498F72A85AD86000DDF8E /* Basel-Book.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AEE498F52A85AD86000DDF8E /* Basel-Book.ttf */; };
AEE498F82A85AD86000DDF8E /* Basel-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AEE498F62A85AD86000DDF8E /* Basel-Medium.ttf */; };
B8649A8D70B018E583494128 /* libPods-WidgetsCoreTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F9B12416ED2E83444B0936B2 /* libPods-WidgetsCoreTests.a */; };
......@@ -489,6 +490,7 @@
A721D9DBF345F037F45E92BE /* Pods-WidgetsCore.beta.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WidgetsCore.beta.xcconfig"; path = "Target Support Files/Pods-WidgetsCore/Pods-WidgetsCore.beta.xcconfig"; sourceTree = "<group>"; };
A82B0304EEE753C6DEB3943E /* libPods-OneSignalNotificationServiceExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-OneSignalNotificationServiceExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A87B4C3727F6D91B4DAABF14 /* Pods-Uniswap.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap.debug.xcconfig"; path = "Target Support Files/Pods-Uniswap/Pods-Uniswap.debug.xcconfig"; sourceTree = "<group>"; };
AC0EE0972BD826E700BCCF07 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = Uniswap/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
AEE498F52A85AD86000DDF8E /* Basel-Book.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Basel-Book.ttf"; path = "../src/assets/fonts/Basel-Book.ttf"; sourceTree = "<group>"; };
AEE498F62A85AD86000DDF8E /* Basel-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "Basel-Medium.ttf"; path = "../src/assets/fonts/Basel-Medium.ttf"; sourceTree = "<group>"; };
B398F0A4DA17159EA0E2D6DF /* Pods-Uniswap.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Uniswap.dev.xcconfig"; path = "Target Support Files/Pods-Uniswap/Pods-Uniswap.dev.xcconfig"; sourceTree = "<group>"; };
......@@ -880,6 +882,7 @@
13B07FAE1A68108700A75B9A /* Uniswap */ = {
isa = PBXGroup;
children = (
AC0EE0972BD826E700BCCF07 /* PrivacyInfo.xcprivacy */,
8EA8AB3F2AB7ED76004E7EF3 /* Icons */,
8E566D9F2AA1095000D4AA76 /* Components */,
07B067692A7D6EC8001DD9B9 /* Widget */,
......@@ -1433,6 +1436,7 @@
77CF6065C8A24FE48204A2C1 /* SplashScreen.storyboard in Resources */,
681301B42A3726EE00A5BF43 /* pending_swap.riv in Resources */,
681301B22A3726EE00A5BF43 /* pending_send.riv in Resources */,
AC0EE0982BD826E700BCCF07 /* PrivacyInfo.xcprivacy in Resources */,
AEE498F72A85AD86000DDF8E /* Basel-Book.ttf in Resources */,
0767E02F2A61BBDC0042ADA2 /* Basel-Regular.otf in Resources */,
0767E0352A61BBDC0042ADA2 /* Basel-Bold.otf in Resources */,
......@@ -2450,7 +2454,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
......@@ -2496,7 +2500,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets;
......@@ -2542,7 +2546,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.widgets;
......@@ -2588,7 +2592,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets;
......@@ -2630,7 +2634,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
......@@ -2673,7 +2677,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension;
......@@ -2716,7 +2720,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.WidgetIntentExtension;
......@@ -2759,7 +2763,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension;
......@@ -2795,7 +2799,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -2833,7 +2837,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3003,7 +3007,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
......@@ -3047,7 +3051,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension;
......@@ -3143,7 +3147,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3214,7 +3218,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension;
......@@ -3310,7 +3314,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3381,7 +3385,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.26;
MARKETING_VERSION = 1.27;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.dev.OneSignalNotificationServiceExtension;
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
<string>C56D.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
<string>0A2A.1</string>
</array>
</dict>
</array>
</dict>
</plist>
......@@ -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.1-beta.1",
"@uniswap/v3-sdk": "3.11.1-beta.2",
"@uniswap/sdk-core": "4.2.1",
"@uniswap/v3-sdk": "3.11.1",
"@walletconnect/core": "2.11.2",
"@walletconnect/react-native-compat": "2.11.2",
"@walletconnect/utils": "2.11.2",
......
......@@ -169,12 +169,10 @@ function App(): JSX.Element | null {
function SentryTags({ children }: PropsWithChildren): JSX.Element {
useEffect(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [_, flagKey] of WALLET_FEATURE_FLAG_NAMES.entries()) {
Sentry.setTag(`featureFlag.${flagKey}`, Statsig.checkGateWithExposureLoggingDisabled(flagKey))
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [_, experimentDef] of WALLET_EXPERIMENTS.entries()) {
Sentry.setTag(
`experiment.${experimentDef.name}`,
......
......@@ -75,7 +75,7 @@ function useHandleShareNft(): (args: ShareNftArgs) => Promise<void> {
function useHandleShareToken(): (args: ShareTokenArgs) => Promise<void> {
return useCallback(async ({ currencyId }: ShareTokenArgs): Promise<void> => {
const url = getTokenUrl(currencyId)
const url = getTokenUrl(currencyId, true)
if (!url) {
logger.error(new Error('Failed to get token URL'), {
......
......@@ -14,13 +14,13 @@ import { useSagaStatus } from 'src/utils/useSagaStatus'
import {
Button,
Flex,
Icons,
Text,
TouchableArea,
useDeviceDimensions,
useDeviceInsets,
useSporeColors,
} from 'ui/src'
import { Plus } from 'ui/src/components/icons'
import { spacing } from 'ui/src/theme'
import { isAndroid } from 'uniswap/src/utils/platform'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
......@@ -298,7 +298,7 @@ export function AccountSwitcher({ onClose }: { onClose: () => void }): JSX.Eleme
<TouchableArea hapticFeedback mt="$spacing16" onPress={onPressAddWallet}>
<Flex row alignItems="center" gap="$spacing16" ml="$spacing24">
<Flex borderColor="$surface3" borderRadius="$roundedFull" borderWidth={1} p="$spacing8">
<Icons.Plus color="$neutral2" size="$icon.12" strokeWidth={2} />
<Plus color="$neutral2" size="$icon.12" strokeWidth={2} />
</Flex>
<Text color="$neutral2" variant="buttonLabel3">
{t('account.wallet.button.add')}
......
......@@ -6,33 +6,9 @@ import { useAppDispatch, useAppSelector } from 'src/app/hooks'
import { closeModal } from 'src/features/modals/modalSlice'
import { selectCustomEndpoint } from 'src/features/tweaks/selectors'
import { setCustomEndpoint } from 'src/features/tweaks/slice'
import {
Accordion,
Button,
Flex,
Icons,
Separator,
Text,
useDeviceInsets,
useSporeColors,
} from 'ui/src'
import { Accordion, Button, Flex, Text, useDeviceInsets } from 'ui/src'
import { spacing } from 'ui/src/theme'
import {
Experiments,
WALLET_EXPERIMENTS,
getExperimentDefinition,
} from 'uniswap/src/features/gating/experiments'
import {
FeatureFlags,
WALLET_FEATURE_FLAG_NAMES,
getFeatureFlagName,
} from 'uniswap/src/features/gating/flags'
import {
useExperimentValueWithExposureLoggingDisabled,
useFeatureFlagWithExposureLoggingDisabled,
} from 'uniswap/src/features/gating/hooks'
import { Statsig } from 'uniswap/src/features/gating/sdk/statsig'
import { Switch } from 'wallet/src/components/buttons/Switch'
import { AccordionHeader, GatingOverrides } from 'wallet/src/components/gating/GatingOverrides'
import { TextInput } from 'wallet/src/components/input/TextInput'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { ModalName } from 'wallet/src/telemetry/constants'
......@@ -65,16 +41,6 @@ export function ExperimentsModal(): JSX.Element {
}
}
const featureFlagRows: JSX.Element[] = []
for (const [flag, flagName] of WALLET_FEATURE_FLAG_NAMES.entries()) {
featureFlagRows.push(<FeatureFlagRow key={flagName} flag={flag} />)
}
const experimentRows: JSX.Element[] = []
for (const [experiment, experimentDef] of WALLET_EXPERIMENTS.entries()) {
experimentRows.push(<ExperimentRow key={experimentDef.name} experiment={experiment} />)
}
return (
<BottomSheetModal
fullScreen
......@@ -132,119 +98,9 @@ export function ExperimentsModal(): JSX.Element {
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="feature-flags">
<AccordionHeader title="⛳️ Feature Flags" />
<Accordion.Content>
<Text variant="body2">
Overridden feature flags are reset when the app is restarted
</Text>
<Flex gap="$spacing12" mt="$spacing12">
{featureFlagRows}
</Flex>
</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="experiments">
<AccordionHeader title="🔬 Experiments" />
<Accordion.Content>
<Text variant="body2">
Overridden experiments are reset when the app is restarted
</Text>
<Flex gap="$spacing24" mt="$spacing12">
{experimentRows}
</Flex>
</Accordion.Content>
</Accordion.Item>
<GatingOverrides />
</Accordion>
</ScrollView>
</BottomSheetModal>
)
}
function AccordionHeader({ title }: { title: React.ReactNode }): JSX.Element {
return (
<Accordion.Header mt="$spacing12">
<Accordion.Trigger>
{({ open }: { open: boolean }): JSX.Element => (
<>
<Flex row justifyContent="space-between" width="100%">
<Text variant="subheading1">{title}</Text>
<Icons.RotatableChevron direction={open ? 'up' : 'down'} />
</Flex>
</>
)}
</Accordion.Trigger>
</Accordion.Header>
)
}
function FeatureFlagRow({ flag }: { flag: FeatureFlags }): JSX.Element {
const status = useFeatureFlagWithExposureLoggingDisabled(flag)
const name = getFeatureFlagName(flag)
return (
<Flex row alignItems="center" gap="$spacing16" justifyContent="space-between">
<Text variant="body1">{name}</Text>
<Switch
value={status}
onValueChange={(newValue: boolean): void => {
Statsig.overrideGate(name, newValue)
}}
/>
</Flex>
)
}
function ExperimentRow({ experiment }: { experiment: Experiments }): JSX.Element {
const experimentDef = getExperimentDefinition(experiment)
return (
<>
<Separator />
<Flex>
<Text variant="body1">{experimentDef.name}</Text>
<Flex gap="$spacing4">
<Flex
key={experimentDef.name}
row
alignItems="center"
gap="$spacing16"
justifyContent="space-between"
paddingStart="$spacing16">
<Text variant="body2" />
<ExperimentValueSwitch experiment={experiment} />
</Flex>
</Flex>
</Flex>
</>
)
}
function ExperimentValueSwitch({ experiment }: { experiment: Experiments }): JSX.Element {
const colors = useSporeColors()
const experimentDef = getExperimentDefinition(experiment)
const currentValue = useExperimentValueWithExposureLoggingDisabled(experiment)
return (
<Flex gap="$spacing8">
{experimentDef.values.map((value) => (
<Flex
key={value}
gap="$spacing4"
onPressOut={() => {
Statsig.overrideConfig(experimentDef.name, {
[experimentDef.key]: value,
})
}}>
<Text color={value === currentValue ? colors.accent1.val : colors.neutral1.val}>
{value}
</Text>
</Flex>
))}
</Flex>
)
}
......@@ -5,7 +5,6 @@ import { useAppDispatch } from 'src/app/hooks'
import {
Button,
Flex,
Icons,
Text,
Unicon,
UniconV2,
......@@ -13,6 +12,7 @@ import {
useIsDarkMode,
useUniconColors,
} from 'ui/src'
import { RightArrow } from 'ui/src/components/icons'
import { spacing } from 'ui/src/theme'
import { UniconGradient } from 'wallet/src/components/accounts/AccountIcon'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
......@@ -47,7 +47,7 @@ export function UniconsV2Modal({ address }: UniconsV2ModalProps): JSX.Element {
<Unicon address={address} size={UNICON_SIZE - UNICON_PADDING} />
<UniconGradient color={uniconColor} size={UNICON_SIZE} />
</Flex>
<Icons.RightArrow color="$neutral3" size="$icon.16" />
<RightArrow color="$neutral3" size="$icon.16" />
<UniconV2 address={address} size={UNICON_SIZE} />
</Flex>
<Flex alignItems="center" gap="$spacing12">
......
......@@ -21,7 +21,6 @@ import {
Flex,
FlexProps,
HapticFeedback,
Icons,
LinearGradient,
Text,
TouchableArea,
......@@ -29,6 +28,7 @@ import {
useIsDarkMode,
useSporeColors,
} from 'ui/src'
import { Search } from 'ui/src/components/icons'
import { borderRadii, fonts } from 'ui/src/theme'
import { isAndroid, isIOS } from 'uniswap/src/utils/platform'
import { useHighestBalanceNativeCurrencyId } from 'wallet/src/features/dataApi/balances'
......@@ -250,7 +250,7 @@ function ExploreTabBarButton({ activeScale = 0.98 }: ExploreTabBarButtonProps):
shadowOffset={SWAP_BUTTON_SHADOW_OFFSET}
shadowOpacity={isDarkMode ? 0.6 : 0.4}
shadowRadius={borderRadii.rounded20}>
<Icons.Search color="$neutral2" size="$icon.24" />
<Search color="$neutral2" size="$icon.24" />
<Text
allowFontScaling={false}
color="$neutral2"
......
......@@ -61,7 +61,8 @@ import { SettingsWalletEdit } from 'src/screens/SettingsWalletEdit'
import { SettingsWalletManageConnection } from 'src/screens/SettingsWalletManageConnection'
import { TokenDetailsScreen } from 'src/screens/TokenDetailsScreen'
import { WebViewScreen } from 'src/screens/WebViewScreen'
import { Icons, useDeviceInsets, useSporeColors } from 'ui/src'
import { useDeviceInsets, useSporeColors } from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons'
import { spacing } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
......@@ -211,7 +212,7 @@ export function FiatOnRampStackNavigator(): JSX.Element {
}
const renderHeaderBackImage = (): JSX.Element => (
<Icons.RotatableChevron color="$neutral2" height={28} width={28} />
<RotatableChevron color="$neutral2" height={28} width={28} />
)
export function OnboardingStackNavigator(): JSX.Element {
......
......@@ -2,7 +2,8 @@ import React from 'react'
import { useAnimatedStyle } from 'react-native-reanimated'
import { useLineChartDatetime } from 'react-native-wagmi-charts'
import { AnimatedText } from 'src/components/text/AnimatedText'
import { Flex, Icons, useSporeColors } from 'ui/src'
import { Flex, useSporeColors } from 'ui/src'
import { AnimatedCaretChange } from 'ui/src/components/icons'
import { isAndroid } from 'uniswap/src/utils/platform'
import { FiatCurrency } from 'wallet/src/features/fiatCurrency/constants'
import { useAppFiatCurrency, useAppFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks'
......@@ -65,7 +66,7 @@ export function RelativeChangeText({ loading }: { loading: boolean }): JSX.Eleme
alignItems={isAndroid ? 'center' : 'flex-end'}
gap="$spacing2"
mt={isAndroid ? '$none' : '$spacing2'}>
<Icons.AnimatedCaretChange
<AnimatedCaretChange
size="$icon.16"
strokeWidth={2}
style={[
......
......@@ -7,16 +7,9 @@ import { Alert, LayoutChangeEvent, LayoutRectangle, StyleSheet } from 'react-nat
import { launchImageLibrary } from 'react-native-image-picker'
import { FadeIn, FadeOut } from 'react-native-reanimated'
import { Defs, LinearGradient, Path, Rect, Stop, Svg } from 'react-native-svg'
import {
AnimatedFlex,
Button,
Flex,
Icons,
Text,
useDeviceDimensions,
useSporeColors,
} from 'ui/src'
import { AnimatedFlex, Button, Flex, Text, useDeviceDimensions, useSporeColors } from 'ui/src'
import CameraScan from 'ui/src/assets/icons/camera-scan.svg'
import { Global, Photo } from 'ui/src/components/icons'
import { iconSizes, spacing } from 'ui/src/theme'
import { Sentry } from 'utilities/src/logger/Sentry'
import { DevelopmentOnly } from 'wallet/src/components/DevelopmentOnly/DevelopmentOnly'
......@@ -264,14 +257,14 @@ export function QRCodeScanner(props: QRCodeScannerProps | WCScannerProps): JSX.E
{isReadingImageFile ? (
<SpinningLoader size={iconSizes.icon28} />
) : (
<Icons.Photo color="$neutral1" size={iconSizes.icon28} />
<Photo color="$neutral1" size={iconSizes.icon28} />
)}
</Flex>
{isWalletConnectModal && props.numConnections > 0 && (
<Button
fontFamily="$body"
icon={<Icons.Global color="$neutral2" />}
icon={<Global color="$neutral2" />}
theme="secondary"
onPress={props.onPressConnections}>
{t('qrScanner.button.connections', { count: props.numConnections })}
......
......@@ -9,7 +9,8 @@ import {
} from 'src/app/navigation/types'
import { openModal } from 'src/features/modals/modalSlice'
import { Screens } from 'src/screens/Screens'
import { Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { Switch } from 'wallet/src/components/buttons/Switch'
import { Arrow } from 'wallet/src/components/icons/Arrow'
......@@ -123,7 +124,7 @@ export function SettingsRow({
</Text>
</Flex>
) : null}
<Icons.RotatableChevron
<RotatableChevron
color="$neutral3"
direction="end"
height={iconSizes.icon24}
......
......@@ -2,7 +2,14 @@ import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Animated, { FadeIn, FadeOut } from 'react-native-reanimated'
import { LongText } from 'src/components/text/LongText'
import { Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import {
ChartBar,
ChartPie,
Language as LanguageIcon,
TrendDown,
TrendUp,
} from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { TokenDetailsScreenQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { NumberType } from 'utilities/src/format/types'
......@@ -58,45 +65,35 @@ export function TokenDetailsMarketData({
<Flex gap="$spacing8">
<StatsRow
label={t('token.stats.marketCap')}
statsIcon={
<Icons.ChartPie color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />
}>
statsIcon={<ChartPie color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />}>
<Text loading={isLoading} variant="body2">
{convertFiatAmountFormatted(marketCap, NumberType.FiatTokenStats)}
</Text>
</StatsRow>
<StatsRow
label={t('token.stats.fullyDilutedValuation')}
statsIcon={
<Icons.ChartPie color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />
}>
statsIcon={<ChartPie color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />}>
<Text loading={isLoading} variant="body2">
{convertFiatAmountFormatted(fullyDilutedValuation, NumberType.FiatTokenStats)}
</Text>
</StatsRow>
<StatsRow
label={t('token.stats.volume')}
statsIcon={
<Icons.ChartBar color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />
}>
statsIcon={<ChartBar color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />}>
<Text loading={isLoading} variant="body2">
{convertFiatAmountFormatted(volume, NumberType.FiatTokenStats)}
</Text>
</StatsRow>
<StatsRow
label={t('token.stats.priceHighYear')}
statsIcon={
<Icons.TrendUp color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />
}>
statsIcon={<TrendUp color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />}>
<Text loading={isLoading} variant="body2">
{convertFiatAmountFormatted(priceHight52W, NumberType.FiatTokenDetails)}
</Text>
</StatsRow>
<StatsRow
label={t('token.stats.priceLowYear')}
statsIcon={
<Icons.TrendDown color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />
}>
statsIcon={<TrendDown color={tokenColor ?? defaultTokenColor} size={iconSizes.icon16} />}>
<Text loading={isLoading} variant="body2">
{convertFiatAmountFormatted(priceLow52W, NumberType.FiatTokenDetails)}
</Text>
......@@ -171,7 +168,7 @@ export function TokenDetailsStats({
{showTranslation ? (
<Flex row alignItems="center" gap="$spacing12" width="100%">
<Flex fill row alignItems="center" gap="$spacing12">
<Icons.Language color="$neutral2" size="$icon.20" />
<LanguageIcon color="$neutral2" size="$icon.20" />
<Text color="$neutral2" variant="body3">
{currentLanguageInfo.displayName}
</Text>
......@@ -183,7 +180,7 @@ export function TokenDetailsStats({
) : (
<Animated.View entering={FadeIn.duration(100)} exiting={FadeOut.duration(100)}>
<Flex row alignItems="center" gap="$spacing12">
<Icons.Language color="$neutral2" size="$icon.20" />
<LanguageIcon color="$neutral2" size="$icon.20" />
<Text color="$neutral2" variant="body3">
{t('token.stats.translation.translate', {
language: currentLanguageInfo.displayName,
......
......@@ -11,7 +11,8 @@ import {
WalletConnectSession,
removePendingSession,
} from 'src/features/walletConnect/walletConnectSlice'
import { AnimatedFlex, Flex, Icons, Text, TouchableArea, useDeviceDimensions } from 'ui/src'
import { AnimatedFlex, Flex, Text, TouchableArea, useDeviceDimensions } from 'ui/src'
import { Edit, Scan } from 'ui/src/components/icons'
import { spacing } from 'ui/src/theme'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { ModalName } from 'wallet/src/telemetry/constants'
......@@ -60,14 +61,14 @@ export function ConnectedDappsList({ backButton, sessions }: ConnectedDappsProps
setIsEditing(!isEditing)
}}>
{isEditing ? (
<Icons.Edit color="$accent1" size="$icon.20" />
<Edit color="$accent1" size="$icon.20" />
) : (
<Icons.Edit color="$neutral2" size="$icon.20" />
<Edit color="$neutral2" size="$icon.20" />
)}
</TouchableArea>
) : (
<TouchableArea onPress={onPressScan}>
<Icons.Scan color="$neutral2" size="$icon.20" />
<Scan color="$neutral2" size="$icon.20" />
</TouchableArea>
)}
</Flex>
......
......@@ -20,7 +20,8 @@ import {
addSession,
removePendingSession,
} from 'src/features/walletConnect/walletConnectSlice'
import { Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Check, X } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { ONE_SECOND_MS } from 'utilities/src/time/time'
import { AccountDetails } from 'wallet/src/components/accounts/AccountDetails'
......@@ -76,7 +77,7 @@ const SitePermissions = (): JSX.Element => {
</Flex>
<Flex gap="$spacing8" pt="$spacing12">
<Flex centered row gap="$spacing4">
<Icons.Check color="$statusSuccess" size={iconSizes.icon16} />
<Check color="$statusSuccess" size={iconSizes.icon16} />
<Text
$short={{ variant: infoTextSize }}
allowFontScaling={false}
......@@ -87,7 +88,7 @@ const SitePermissions = (): JSX.Element => {
</Text>
</Flex>
<Flex centered row gap="$spacing4">
<Icons.Check color="$statusSuccess" size={iconSizes.icon16} />
<Check color="$statusSuccess" size={iconSizes.icon16} />
<Text
$short={{ variant: infoTextSize }}
allowFontScaling={false}
......@@ -98,7 +99,7 @@ const SitePermissions = (): JSX.Element => {
</Text>
</Flex>
<Flex centered row gap="$spacing4">
<Icons.X color="$statusCritical" size={iconSizes.icon16} />
<X color="$statusCritical" size={iconSizes.icon16} />
<Text
$short={{ variant: infoTextSize }}
allowFontScaling={false}
......
......@@ -186,7 +186,21 @@ export function WalletConnectModal({
chainId: parsedUwulinkRequest.chainId,
}
if (parsedUwulinkRequest.method === UwULinkMethod.Erc20Send) {
if (parsedUwulinkRequest.method === EthMethod.PersonalSign) {
dispatch(
addRequest({
account: activeAccount.address,
request: {
...newRequest,
type: EthMethod.PersonalSign,
message: parsedUwulinkRequest.message,
// rawMessage should be the hex version of `message`, but our wallet will only use
// `message` if it exists. so this is mostly to appease Typescript
rawMessage: parsedUwulinkRequest.message,
},
})
)
} else if (parsedUwulinkRequest.method === UwULinkMethod.Erc20Send) {
const preparedTransaction = await toTokenTransferRequest(
parsedUwulinkRequest,
activeAccount,
......
......@@ -18,6 +18,7 @@ import { getTokenTransferRequest } from 'wallet/src/features/transactions/transf
import { TransferCurrencyParams } from 'wallet/src/features/transactions/transfer/types'
import { Account } from 'wallet/src/features/wallet/accounts/types'
import {
EthMethod,
EthTransaction,
UwULinkErc20SendRequest,
UwULinkMethod,
......@@ -188,6 +189,10 @@ export function isAllowedUwuLinkRequest(
return Boolean(findAllowedTokenRecipient(request, allowlist))
}
if (request.method === EthMethod.PersonalSign) {
return true
}
// generic transactions
const { to, value } = request.value
const belowMaximumValue =
......
......@@ -5,7 +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 { Flex, HapticFeedback, Icons, ImpactFeedbackStyle, Text, TouchableArea } from 'ui/src'
import { Flex, HapticFeedback, ImpactFeedbackStyle, Text, TouchableArea } from 'ui/src'
import { CopyAlt, Settings } from 'ui/src/components/icons'
import { isDevEnv } from 'uniswap/src/utils/env'
import { AccountIcon } from 'wallet/src/components/accounts/AccountIcon'
import { AnimatedUnitagDisplayName } from 'wallet/src/components/accounts/AnimatedUnitagDisplayName'
......@@ -106,7 +107,7 @@ export function AccountHeader(): JSX.Element {
hitSlop={20}
testID="account-header/settings-button"
onPress={onPressSettings}>
<Icons.Settings color="$neutral2" opacity={0.8} size="$icon.24" />
<Settings color="$neutral2" opacity={0.8} size="$icon.24" />
</TouchableArea>
</Flex>
{walletHasName ? (
......@@ -138,7 +139,7 @@ export function AccountHeader(): JSX.Element {
variant="subheading2">
{sanitizeAddressText(shortenAddress(activeAddress))}
</Text>
<Icons.CopyAlt color="$neutral1" size="$icon.16" />
<CopyAlt color="$neutral1" size="$icon.16" />
</Flex>
</TouchableArea>
)}
......
import React from 'react'
import { ColorTokens, IconProps, Icons, TouchableArea, TouchableAreaProps } from 'ui/src'
import { ColorTokens, IconProps, TouchableArea, TouchableAreaProps } from 'ui/src'
import { X } from 'ui/src/components/icons'
type Props = {
onPress: () => void
......@@ -11,7 +12,7 @@ type Props = {
export function CloseButton({ onPress, size, strokeWidth, color, ...rest }: Props): JSX.Element {
return (
<TouchableArea onPress={onPress} {...rest} testID="buttons/close-button">
<Icons.X color={color} size={size ?? '$icon.20'} strokeWidth={strokeWidth ?? 2} />
<X color={color} size={size ?? '$icon.20'} strokeWidth={strokeWidth ?? 2} />
</TouchableArea>
)
}
import { default as React } from 'react'
import { useTranslation } from 'react-i18next'
import { Flex, Icons, Text, TouchableArea } from 'ui/src'
import { Flex, Text, TouchableArea } from 'ui/src'
import { TripleDots } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
export function FavoriteHeaderRow({
......@@ -32,7 +33,7 @@ export function FavoriteHeaderRow({
hitSlop={16}
testID="favorite-header-row/favorite-button"
onPress={onPress}>
<Icons.TripleDots
<TripleDots
color="$neutral2"
size={iconSizes.icon20}
strokeLinecap="round"
......
......@@ -9,7 +9,8 @@ import {
import { sendMobileAnalyticsEvent } from 'src/features/telemetry'
import { MobileEventName } from 'src/features/telemetry/constants'
import { disableOnPress } from 'src/utils/disableOnPress'
import { Flex, Icons, Text, TouchableArea, useIsDarkMode } from 'ui/src'
import { Flex, Text, TouchableArea, useIsDarkMode } from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { TokenSortableField } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { logger } from 'utilities/src/logger/logger'
......@@ -86,7 +87,7 @@ function _SortButton({ orderBy }: FilterGroupProps): JSX.Element {
<Text ellipse color="$neutral2" flexShrink={1} numberOfLines={1} variant="buttonLabel3">
{getTokensOrderBySelectedLabel(orderBy, t)}
</Text>
<Icons.RotatableChevron
<RotatableChevron
color="$neutral2"
direction="down"
height={iconSizes.icon20}
......
......@@ -7,14 +7,15 @@ import { SearchPopularNFTCollections } from 'src/components/explore/search/Searc
import { SearchPopularTokens } from 'src/components/explore/search/SearchPopularTokens'
import { renderSearchItem } from 'src/components/explore/search/SearchResultsSection'
import { SectionHeaderText } from 'src/components/explore/search/SearchSectionHeader'
import { AnimatedFlex, Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import { AnimatedFlex, Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import ClockIcon from 'ui/src/assets/icons/clock.svg'
import { TrendUp } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { clearSearchHistory } from 'wallet/src/features/search/searchHistorySlice'
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" />
const TrendUpIcon = <TrendUp color="$neutral2" size="$icon.24" />
export const SUGGESTED_WALLETS: WalletSearchResult[] = [
{
......
......@@ -2,7 +2,8 @@ import React from 'react'
import { useTranslation } from 'react-i18next'
import { FadeIn, FadeOut } from 'react-native-reanimated'
import { SectionHeaderText } from 'src/components/explore/search/SearchSectionHeader'
import { AnimatedFlex, Flex, Icons, Loader } from 'ui/src'
import { AnimatedFlex, Flex, Loader } from 'ui/src'
import { Coin, Gallery, Person } from 'ui/src/components/icons'
export const SearchResultsLoader = (): JSX.Element => {
const { t } = useTranslation()
......@@ -10,7 +11,7 @@ export const SearchResultsLoader = (): JSX.Element => {
<Flex gap="$spacing16">
<Flex gap="$spacing12">
<SectionHeaderText
icon={<Icons.Coin color="$neutral2" size="$icon.24" />}
icon={<Coin color="$neutral2" size="$icon.24" />}
title={t('explore.search.section.tokens')}
/>
<AnimatedFlex entering={FadeIn} exiting={FadeOut} mx="$spacing8">
......@@ -19,7 +20,7 @@ export const SearchResultsLoader = (): JSX.Element => {
</Flex>
<Flex gap="$spacing12">
<SectionHeaderText
icon={<Icons.Gallery color="$neutral2" size="$icon.24" />}
icon={<Gallery color="$neutral2" size="$icon.24" />}
title={t('explore.search.section.nft')}
/>
<AnimatedFlex entering={FadeIn} exiting={FadeOut} mx="$spacing8">
......@@ -28,7 +29,7 @@ export const SearchResultsLoader = (): JSX.Element => {
</Flex>
<Flex gap="$spacing12">
<SectionHeaderText
icon={<Icons.Person color="$neutral2" size="$icon.24" />}
icon={<Person color="$neutral2" size="$icon.24" />}
title={t('explore.search.section.wallets')}
/>
<AnimatedFlex entering={FadeIn} exiting={FadeOut} mx="$spacing8">
......
......@@ -16,7 +16,8 @@ import {
formatTokenSearchResults,
getSearchResultId,
} from 'src/components/explore/search/utils'
import { AnimatedFlex, Flex, Icons, Text } from 'ui/src'
import { AnimatedFlex, Flex, Text } from 'ui/src'
import { Coin, Gallery, Person } from 'ui/src/components/icons'
import { useExploreSearchQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import i18n from 'uniswap/src/i18n/i18n'
import { logger } from 'utilities/src/logger/logger'
......@@ -36,17 +37,17 @@ const ICON_SIZE = '$icon.24'
const ICON_COLOR = '$neutral2'
const WalletHeaderItem: SearchResultOrHeader = {
icon: <Icons.Person color={ICON_COLOR} size={ICON_SIZE} />,
icon: <Person color={ICON_COLOR} size={ICON_SIZE} />,
type: SEARCH_RESULT_HEADER_KEY,
title: i18n.t('explore.search.section.wallets'),
}
const TokenHeaderItem: SearchResultOrHeader = {
icon: <Icons.Coin color={ICON_COLOR} size={ICON_SIZE} />,
icon: <Coin color={ICON_COLOR} size={ICON_SIZE} />,
type: SEARCH_RESULT_HEADER_KEY,
title: i18n.t('explore.search.section.tokens'),
}
const NFTHeaderItem: SearchResultOrHeader = {
icon: <Icons.Gallery color={ICON_COLOR} size={ICON_SIZE} />,
icon: <Gallery color={ICON_COLOR} size={ICON_SIZE} />,
type: SEARCH_RESULT_HEADER_KEY,
title: i18n.t('explore.search.section.nft'),
}
......
......@@ -4,7 +4,8 @@ import { useAppStackNavigation } from 'src/app/navigation/types'
import { sendMobileAnalyticsEvent } from 'src/features/telemetry'
import { MobileEventName } from 'src/features/telemetry/constants'
import { Screens } from 'src/screens/Screens'
import { Flex, Icons, ImpactFeedbackStyle, Text, TouchableArea } from 'ui/src'
import { Flex, ImpactFeedbackStyle, Text, TouchableArea } from 'ui/src'
import { Verified } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { NFTViewer } from 'wallet/src/features/images/NFTViewer'
import { SearchContext } from 'wallet/src/features/search/SearchContext'
......@@ -94,7 +95,7 @@ export function SearchNFTCollectionItem({
</Text>
</Flex>
<Flex grow alignItems="flex-start" width="$spacing36">
{isVerified ? <Icons.Verified color="$accent1" size="$icon.16" /> : null}
{isVerified ? <Verified color="$accent1" size="$icon.16" /> : null}
</Flex>
</Flex>
</TouchableArea>
......
import React from 'react'
import { useTranslation } from 'react-i18next'
import { Button, Icons } from 'ui/src'
import { Button } from 'ui/src'
import { InfoCircleFilled } from 'ui/src/components/icons'
import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader'
interface FiatOnRampCtaButtonProps {
......@@ -31,7 +32,7 @@ export function FiatOnRampCtaButton({
isLoading ? (
<SpinningLoader color="$sporeWhite" />
) : !eligible ? (
<Icons.InfoCircleFilled color="$neutral3" />
<InfoCircleFilled color="$neutral3" />
) : undefined
}
size="large"
......
......@@ -9,7 +9,8 @@ import { TAB_BAR_HEIGHT, TabProps } from 'src/components/layout/TabHelpers'
import { Loader } from 'src/components/loading'
import { openModal } from 'src/features/modals/modalSlice'
import { removePendingSession } from 'src/features/walletConnect/walletConnectSlice'
import { Flex, Icons, Text, useDeviceInsets, useSporeColors } from 'ui/src'
import { Flex, Text, useDeviceInsets, useSporeColors } from 'ui/src'
import { NoTransactions } from 'ui/src/components/icons'
import { GQLQueries } from 'uniswap/src/data/graphql/uniswap-data-api/queries'
import { isAndroid } from 'uniswap/src/utils/platform'
import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard'
......@@ -86,7 +87,7 @@ export const FeedTab = memo(
<Flex grow style={containerProps?.emptyContainerStyle}>
<BaseCard.EmptyState
description={t('home.feed.empty.description')}
icon={<Icons.NoTransactions color="$neutral3" size="$icon.70" />}
icon={<NoTransactions color="$neutral3" size="$icon.70" />}
title={t('home.feed.empty.title')}
onPress={onPressReceive}
/>
......
......@@ -9,7 +9,8 @@ import { WalletEmptyState } from 'src/components/home/WalletEmptyState'
import { TabContentProps, TabProps } from 'src/components/layout/TabHelpers'
import { openModal } from 'src/features/modals/modalSlice'
import { Screens } from 'src/screens/Screens'
import { Flex, Icons } from 'ui/src'
import { Flex } from 'ui/src'
import { NoTokens } from 'ui/src/components/icons'
import { GQLQueries } from 'uniswap/src/data/graphql/uniswap-data-api/queries'
import { CurrencyId } from 'uniswap/src/types/currency'
import { BaseCard } from 'wallet/src/components/BaseCard/BaseCard'
......@@ -71,7 +72,7 @@ export const TokensTab = memo(
return isExternalProfile ? (
<BaseCard.EmptyState
description={t('home.tokens.empty.description')}
icon={<Icons.NoTokens color="$neutral3" size="$icon.70" />}
icon={<NoTokens color="$neutral3" size="$icon.70" />}
title={t('home.tokens.empty.title')}
onPress={onPressAction}
/>
......
import { useDeviceSupportsBiometricAuth } from 'src/features/biometrics/hooks'
import { Icons } from 'ui/src'
import { Faceid, Fingerprint } from 'ui/src/components/icons'
export function BiometricsIcon(): JSX.Element | null {
const { touchId: isTouchIdSupported, faceId: isFaceIdSupported } =
useDeviceSupportsBiometricAuth()
if (isTouchIdSupported) {
return <Icons.Fingerprint color="white" size="$icon.20" />
return <Fingerprint color="white" size="$icon.20" />
}
if (isFaceIdSupported) {
return <Icons.Faceid color="white" size="$icon.20" />
return <Faceid color="white" size="$icon.20" />
}
return null
......
import React from 'react'
import { useTranslation } from 'react-i18next'
import { ColorTokens, Flex, Icons, Text } from 'ui/src'
import { ColorTokens, Flex, Text } from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons'
type Props = {
size?: number
......@@ -13,7 +14,7 @@ export function BackButtonView({ size, color, showButtonLabel }: Props): JSX.Ele
return (
<Flex row alignItems="center" gap="$spacing8">
<Icons.RotatableChevron color={color ?? '$neutral2'} height={size} width={size} />
<RotatableChevron color={color ?? '$neutral2'} height={size} width={size} />
{showButtonLabel && (
<Text color="$neutral2" variant="subheading1">
{t('common.button.back')}
......
......@@ -3,7 +3,8 @@ import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ActivityIndicator, EmitterSubscription, Keyboard } from 'react-native'
import { getUniqueId } from 'react-native-device-info'
import { Button, Flex, Icons, Text, useSporeColors } from 'ui/src'
import { Button, Flex, Text, useSporeColors } from 'ui/src'
import { AlertTriangle } from 'ui/src/components/icons'
import { fonts, spacing } from 'ui/src/theme'
import { useUnitagUpdater } from 'uniswap/src/features/unitags/context'
import { UnitagErrorCodes } from 'uniswap/src/features/unitags/types'
......@@ -311,7 +312,7 @@ function ChangeUnitagConfirmModal({
height="$spacing48"
mb="$spacing8"
minWidth="$spacing48">
<Icons.AlertTriangle color="$statusCritical" size="$icon.24" />
<AlertTriangle color="$statusCritical" size="$icon.24" />
</Flex>
<Text textAlign="center" variant="subheading1">
{t('unitags.editUsername.confirm.title')}
......
......@@ -2,7 +2,8 @@ import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { selectPhotoFromLibrary } from 'src/components/unitags/AvatarSelection'
import { ChooseNftModal } from 'src/components/unitags/ChooseNftModal'
import { Flex, Icons, Text, useSporeColors } from 'ui/src'
import { Flex, Text, useSporeColors } from 'ui/src'
import { Camera, Photo, Trash } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { ElementName, ModalName } from 'wallet/src/telemetry/constants'
......@@ -115,13 +116,11 @@ const ChoosePhotoOption = ({ type }: { type: PhotoAction }): JSX.Element => {
justifyContent="flex-start"
p="$spacing24">
{type === PhotoAction.BrowseCameraRoll && (
<Icons.Camera color="$neutral1" size={iconSizes.icon24} />
)}
{type === PhotoAction.BrowseNftsList && (
<Icons.Photo color="$neutral1" size={iconSizes.icon24} />
<Camera color="$neutral1" size={iconSizes.icon24} />
)}
{type === PhotoAction.BrowseNftsList && <Photo color="$neutral1" size={iconSizes.icon24} />}
{type === PhotoAction.RemovePhoto && (
<Icons.Trash color="$statusCritical" size={iconSizes.icon24} />
<Trash color="$statusCritical" size={iconSizes.icon24} />
)}
<Flex shrink alignItems="flex-start">
<Text
......
......@@ -2,7 +2,8 @@ import { useNavigation } from '@react-navigation/native'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ActivityIndicator } from 'react-native'
import { Button, Flex, Icons, Text, useSporeColors } from 'ui/src'
import { Button, Flex, Text, useSporeColors } from 'ui/src'
import { AlertTriangle } from 'ui/src/components/icons'
import { fonts } from 'ui/src/theme'
import { useUnitagUpdater } from 'uniswap/src/features/unitags/context'
import { logger } from 'utilities/src/logger/logger'
......@@ -90,7 +91,7 @@ export function DeleteUnitagModal({
height="$spacing48"
mb="$spacing8"
minWidth="$spacing48">
<Icons.AlertTriangle color="$statusCritical" size="$icon.24" />
<AlertTriangle color="$statusCritical" size="$icon.24" />
</Flex>
<Text textAlign="center" variant="subheading1">
{t('unitags.delete.confirm.title')}
......
......@@ -8,8 +8,9 @@ import { closeModal } from 'src/features/modals/modalSlice'
import { selectModalState } from 'src/features/modals/selectModalState'
import { TermsOfService } from 'src/screens/Onboarding/TermsOfService'
import { Screens, UnitagScreens } from 'src/screens/Screens'
import { Button, Flex, GeneratedIcon, Icons, Image, Text, useIsDarkMode } from 'ui/src'
import { Button, Flex, GeneratedIcon, Image, Text, useIsDarkMode } from 'ui/src'
import { UNITAGS_INTRO_BANNER_DARK, UNITAGS_INTRO_BANNER_LIGHT } from 'ui/src/assets'
import { Lightning, Ticket, UserSquare } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { setHasCompletedUnitagsIntroModal } from 'wallet/src/features/behaviorHistory/slice'
......@@ -64,9 +65,9 @@ export function UnitagsIntroModal(): JSX.Element {
/>
</Flex>
<Flex gap="$spacing16" px="$spacing20">
<BodyItem Icon={Icons.UserSquare} title={t('unitags.intro.features.profile')} />
<BodyItem Icon={Icons.Ticket} title={t('unitags.intro.features.free')} />
<BodyItem Icon={Icons.Lightning} title={t('unitags.intro.features.ens')} />
<BodyItem Icon={UserSquare} title={t('unitags.intro.features.profile')} />
<BodyItem Icon={Ticket} title={t('unitags.intro.features.free')} />
<BodyItem Icon={Lightning} title={t('unitags.intro.features.ens')} />
</Flex>
<Flex gap="$spacing8">
<Button size="medium" theme="primary" onPress={onPressClaimOneNow}>
......
......@@ -3,7 +3,8 @@ import { useTranslation } from 'react-i18next'
import { Keyboard, TextInput } from 'react-native'
import { PasswordInput } from 'src/components/input/PasswordInput'
import { PasswordError } from 'src/features/onboarding/PasswordError'
import { Button, Flex, Icons, Text } from 'ui/src'
import { Button, Flex, Text } from 'ui/src'
import { DiamondExclamation } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { useDebounce } from 'utilities/src/time/timing'
import { ElementName } from 'wallet/src/telemetry/constants'
......@@ -118,7 +119,7 @@ export function CloudBackupPasswordForm({
</Flex>
{!isConfirmation && (
<Flex centered row gap="$spacing12" px="$spacing16">
<Icons.DiamondExclamation color="$neutral2" size={iconSizes.icon20} />
<DiamondExclamation color="$neutral2" size={iconSizes.icon20} />
<Text color="$neutral2" variant="body3">
{t('settings.setting.backup.password.disclaimer')}
</Text>
......
......@@ -13,7 +13,6 @@ import { openModal } from 'src/features/modals/modalSlice'
import {
AnimatedFlex,
Flex,
Icons,
Image,
LinearGradient,
ScrollView,
......@@ -26,6 +25,7 @@ import {
useUniconColors,
} from 'ui/src'
import { ENS_LOGO } from 'ui/src/assets'
import { SendAction, XTwitter } from 'ui/src/components/icons'
import { iconSizes, imageSizes } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
......@@ -221,7 +221,7 @@ export const ProfileHeader = memo(function ProfileHeader({
{twitter ? (
<TouchableArea onPress={onPressTwitter}>
<Flex centered row gap="$spacing4">
<Icons.XTwitter color={colors.neutral1.val} size={iconSizes.icon16} />
<XTwitter color={colors.neutral1.val} size={iconSizes.icon16} />
<Text color="$neutral1" variant="buttonLabel3">
{twitter}
</Text>
......@@ -277,7 +277,7 @@ export const ProfileHeader = memo(function ProfileHeader({
testID={ElementName.Send}
onPress={onPressSend}>
<Flex row alignItems="center" gap="$spacing8">
<Icons.SendAction color="$neutral2" size="$icon.20" />
<SendAction color="$neutral2" size="$icon.20" />
<Text
allowFontScaling={true}
color="$neutral2"
......
......@@ -16,11 +16,11 @@ import {
ColorTokens,
Flex,
HapticFeedback,
Icons,
Text,
TouchableArea,
useSporeColors,
} from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons'
import { fonts, iconSizes, spacing } from 'ui/src/theme'
import { CurrencyInfo } from 'uniswap/src/features/dataApi/types'
import { NumberType } from 'utilities/src/format/types'
......@@ -275,7 +275,7 @@ function SelectTokenButton({
<Text color={textColor} pl="$spacing1" variant="body1">
{getSymbolDisplayText(selectedCurrencyInfo.currency.symbol)}
</Text>
<Icons.RotatableChevron color={textColor} direction="end" height={iconSizes.icon16} />
<RotatableChevron color={textColor} direction="end" height={iconSizes.icon16} />
</Flex>
</TouchableArea>
)
......
import React from 'react'
import { SvgUri } from 'react-native-svg'
import Trace from 'src/components/Trace/Trace'
import { Flex, Icons, TouchableArea } from 'ui/src'
import { Flex, TouchableArea } from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { getCountryFlagSvgUrl } from 'wallet/src/features/fiatOnRamp/utils'
import { ElementName } from 'wallet/src/telemetry/constants'
......@@ -36,7 +37,7 @@ export function FiatOnRampCountryPicker({
<Flex borderRadius="$roundedFull" overflow="hidden">
<SvgUri height={ICON_SIZE} uri={countryFlagUrl} width={ICON_SIZE} />
</Flex>
<Icons.RotatableChevron color="$neutral3" direction="down" width={iconSizes.icon20} />
<RotatableChevron color="$neutral3" direction="down" width={iconSizes.icon20} />
</Flex>
</TouchableArea>
</Trace>
......
......@@ -2,8 +2,9 @@ import { default as React } from 'react'
import { useTranslation } from 'react-i18next'
import { Loader } from 'src/components/loading'
import { PriceAmount } from 'src/features/nfts/collection/ListPriceCard'
import { Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import VerifiedIcon from 'ui/src/assets/icons/verified.svg'
import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes, imageSizes, spacing } from 'ui/src/theme'
import {
Currency,
......@@ -94,7 +95,7 @@ export function CollectionPreviewCard({
</Flex>
</Flex>
{isViewableCollection ? (
<Icons.RotatableChevron
<RotatableChevron
color="$neutral1"
direction="end"
height={iconSizes.icon24}
......
import React from 'react'
import { useTranslation } from 'react-i18next'
import { Flex, Icons } from 'ui/src'
import { Flex } from 'ui/src'
import { Check, Laptop } from 'ui/src/components/icons'
import { NotificationToast } from 'wallet/src/features/notifications/components/NotificationToast'
import { ScantasticCompleteNotification as ScantasticCompleteNotificationType } from 'wallet/src/features/notifications/types'
......@@ -16,7 +17,7 @@ export function ScantasticCompleteNotification({
icon={
<Flex position="relative">
<Flex backgroundColor="$accent2" borderRadius="$roundedFull" p="$spacing12">
<Icons.Laptop color="$accent1" size="$icon.24" />
<Laptop color="$accent1" size="$icon.24" />
</Flex>
<Flex
backgroundColor="$statusSuccess"
......@@ -27,7 +28,7 @@ export function ScantasticCompleteNotification({
p="$spacing4"
position="absolute"
right={0}>
<Icons.Check color="$white" size="$icon.8" />
<Check color="$white" size="$icon.8" />
</Flex>
</Flex>
}
......
......@@ -2,7 +2,8 @@ import { useTranslation } from 'react-i18next'
import { useAppSelector } from 'src/app/hooks'
import { closeModal } from 'src/features/modals/modalSlice'
import { selectModalState } from 'src/features/modals/selectModalState'
import { Button, Flex, Icons, Text, TouchableArea } from 'ui/src'
import { Button, Flex, Text, TouchableArea } from 'ui/src'
import { AlertTriangle, DocumentList } from 'ui/src/components/icons'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { useAppDispatch } from 'wallet/src/state'
......@@ -32,9 +33,9 @@ export function ExtensionWaitlistModal(): JSX.Element {
p="$spacing12"
width="$spacing48">
{isUserOnWaitlist ? (
<Icons.DocumentList color="$neutral2" size="$icon.28" />
<DocumentList color="$neutral2" size="$icon.28" />
) : (
<Icons.AlertTriangle color="$neutral2" size="$icon.28" />
<AlertTriangle color="$neutral2" size="$icon.28" />
)}
</Flex>
<Flex alignItems="center" gap="$spacing8">
......
......@@ -4,7 +4,8 @@ import { useAppDispatch, useAppSelector } from 'src/app/hooks'
import { useBiometricAppSettings, useBiometricPrompt } from 'src/features/biometrics/hooks'
import { closeAllModals } from 'src/features/modals/modalSlice'
import { selectModalState } from 'src/features/modals/selectModalState'
import { Button, Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Button, Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import { AlertTriangle, Faceid, Laptop, LinkBrokenHorizontal } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { logger } from 'utilities/src/logger/logger'
......@@ -211,7 +212,7 @@ export function ScantasticModal(): JSX.Element | null {
}
}, [OTP, uuid])
useInterval(checkOTPState, 6000, true)
useInterval(checkOTPState, ONE_SECOND_MS, true)
if (expired) {
return (
......@@ -221,7 +222,7 @@ export function ScantasticModal(): JSX.Element | null {
onClose={onClose}>
<Flex centered gap="$spacing16" px="$spacing16" py="$spacing12">
<Flex centered backgroundColor="$surface2" borderRadius="$rounded12" p="$spacing12">
<Icons.LinkBrokenHorizontal color="$neutral2" size={iconSizes.icon24} />
<LinkBrokenHorizontal color="$neutral2" size={iconSizes.icon24} />
</Flex>
<Text variant="subheading1">{t('scantastic.error.timeout.title')}</Text>
<Text color="$neutral2" mb="$spacing12" textAlign="center" variant="body3">
......@@ -243,7 +244,7 @@ export function ScantasticModal(): JSX.Element | null {
onClose={onClose}>
<Flex centered gap="$spacing16" px="$spacing16" py="$spacing12">
<Flex centered backgroundColor="$accent2" borderRadius="$rounded12" p="$spacing12">
<Icons.Laptop color="$accent1" size={iconSizes.icon24} />
<Laptop color="$accent1" size={iconSizes.icon24} />
</Flex>
<Text variant="subheading1">{t('scantastic.code.title')}</Text>
<Text color="$neutral2" textAlign="center" variant="body3">
......@@ -269,7 +270,7 @@ export function ScantasticModal(): JSX.Element | null {
onClose={onClose}>
<Flex centered gap="$spacing16" px="$spacing16" py="$spacing12">
<Flex centered backgroundColor="$accent2" borderRadius="$rounded12" p="$spacing12">
<Icons.AlertTriangle color="$statusCritical" size={iconSizes.icon24} />
<AlertTriangle color="$statusCritical" size={iconSizes.icon24} />
</Flex>
<Text variant="subheading1">{t('common.text.error')}</Text>
<Text color="$neutral2" textAlign="center" variant="body3">
......@@ -294,7 +295,7 @@ export function ScantasticModal(): JSX.Element | null {
onClose={onClose}>
<Flex centered gap="$spacing16" px="$spacing16" py="$spacing12">
<Flex centered backgroundColor="$accent2" borderRadius="$rounded12" p="$spacing12">
<Icons.Laptop color="$accent1" size={iconSizes.icon24} />
<Laptop color="$accent1" size={iconSizes.icon24} />
</Flex>
<Text variant="subheading1">{t('scantastic.confirmation.title')}</Text>
<Text color="$neutral2" textAlign="center" variant="body3">
......@@ -334,14 +335,14 @@ export function ScantasticModal(): JSX.Element | null {
gap="$spacing8"
p="$spacing16"
width="100%">
<Icons.AlertTriangle color="$neutral2" size="$icon.20" />
<AlertTriangle color="$neutral2" size="$icon.20" />
<Text color="$neutral2" variant="body4">
{t('scantastic.confirmation.warning')}
</Text>
</Flex>
<Flex flexDirection="column" gap="$spacing4" width="100%">
<Button
icon={requiresBiometricAuth ? <Icons.Faceid size={iconSizes.icon16} /> : undefined}
icon={requiresBiometricAuth ? <Faceid size={iconSizes.icon16} /> : undefined}
mb="$spacing4"
theme="primary"
onPress={onConfirmSync}>
......
......@@ -9,7 +9,8 @@ import { UnitagProfilePicture } from 'src/components/unitags/UnitagProfilePictur
import { SafeKeyboardOnboardingScreen } from 'src/features/onboarding/SafeKeyboardOnboardingScreen'
import { UnitagName } from 'src/features/unitags/UnitagName'
import { OnboardingScreens, Screens, UnitagScreens } from 'src/screens/Screens'
import { Button, Flex, Icons, Text, useIsDarkMode, useSporeColors } from 'ui/src'
import { Button, Flex, Text, useIsDarkMode, useSporeColors } from 'ui/src'
import { Pen } from 'ui/src/components/icons'
import { fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme'
import { UnitagClaimSource } from 'uniswap/src/features/unitags/types'
import { ChainId } from 'wallet/src/constants/chains'
......@@ -133,7 +134,7 @@ export function ChooseProfilePictureScreen({
backgroundColor={isDarkMode ? '$neutral3' : '$neutral2'}
borderRadius="$roundedFull"
p={8}>
<Icons.Pen color={isDarkMode ? '$neutral1' : '$surface1'} size={iconSizes.icon16} />
<Pen color={isDarkMode ? '$neutral1' : '$surface1'} size={iconSizes.icon16} />
</Flex>
</Flex>
</Flex>
......
......@@ -17,13 +17,13 @@ import {
AnimatedFlex,
Button,
Flex,
Icons,
Image,
Text,
TouchableArea,
useSporeColors,
} from 'ui/src'
import { ENS_LOGO } from 'ui/src/assets'
import { InfoCircleFilled, LinkHorizontalAlt } from 'ui/src/components/icons'
import { fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { logger } from 'utilities/src/logger/logger'
......@@ -338,7 +338,7 @@ export function ClaimUnitagScreen({ navigation, route }: Props): JSX.Element {
Keyboard.dismiss()
setShowInfoModal(true)
}}>
<Icons.InfoCircleFilled color={colors.neutral3.get()} size="$icon.20" />
<InfoCircleFilled color={colors.neutral3.get()} size="$icon.20" />
</TouchableArea>
</AnimatedFlex>
{canClaimUnitagNameError && unitagToCheck === unitagInputValue && (
......@@ -436,7 +436,7 @@ const InfoModal = ({
width={FIXED_INFO_PILL_WIDTH}
/>
<Flex p="$spacing2" shadowColor="$accent1" shadowOpacity={1} shadowRadius="$spacing16">
<Icons.LinkHorizontalAlt color={colors.neutral3.get()} size={iconSizes.icon24} />
<LinkHorizontalAlt color={colors.neutral3.get()} size={iconSizes.icon24} />
</Flex>
<Pill
customBackgroundColor={colors.surface1.val}
......
/* eslint-disable max-lines */
import React, { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Keyboard, KeyboardAvoidingView, StyleSheet } from 'react-native'
......@@ -17,7 +16,6 @@ import { Screens, UnitagScreens } from 'src/screens/Screens'
import {
Button,
Flex,
Icons,
LinearGradient,
ScrollView,
Text,
......@@ -26,6 +24,7 @@ import {
useSporeColors,
useUniconColors,
} from 'ui/src'
import { Pen, TripleDots } from 'ui/src/components/icons'
import { borderRadii, fonts, iconSizes, imageSizes, spacing } from 'ui/src/theme'
import { useExtractedColors } from 'ui/src/utils/colors'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
......@@ -297,7 +296,7 @@ export function EditUnitagProfileScreen({
}
}}>
<Flex pr="$spacing8">
<Icons.TripleDots color="$neutral2" size={iconSizes.icon24} />
<TripleDots color="$neutral2" size={iconSizes.icon24} />
</Flex>
</ContextMenu>
) : undefined
......@@ -366,10 +365,7 @@ export function EditUnitagProfileScreen({
backgroundColor={isDarkMode ? '$neutral3' : '$neutral2'}
borderRadius="$roundedFull"
p={6}>
<Icons.Pen
color={isDarkMode ? '$neutral1' : '$surface1'}
size={iconSizes.icon16}
/>
<Pen color={isDarkMode ? '$neutral1' : '$surface1'} size={iconSizes.icon16} />
</Flex>
</Flex>
</Flex>
......
import { Flex, Icons, Text } from 'ui/src'
import { Flex, Text } from 'ui/src'
import { Unitag } from 'ui/src/components/icons'
import { fonts, spacing } from 'ui/src/theme'
export function UnitagName({
......@@ -37,7 +38,7 @@ export function UnitagName({
position="absolute"
right={-spacing.spacing4}
top={-spacing.spacing4}>
<Icons.Unitag size="$icon.24" />
<Unitag size="$icon.24" />
</Flex>
</Flex>
)
......
......@@ -82,7 +82,7 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({ method: 'eth_sendTransaction', response: signature, chainId }),
body: JSON.stringify({ method: params.method, response: signature, chainId }),
// TODO: consider adding analytics to track UwuLink usage
}).catch((error) =>
logger.error(error, {
......
......@@ -12,16 +12,8 @@ import { useFiatOnRampContext } from 'src/features/fiatOnRamp/FiatOnRampContext'
import { InitialQuoteSelection } from 'src/features/fiatOnRamp/types'
import { getServiceProviderForQuote } from 'src/features/fiatOnRamp/utils'
import { FiatOnRampScreens } from 'src/screens/Screens'
import {
AnimatedFlex,
ColorTokens,
Flex,
GeneratedIcon,
Icons,
Inset,
Separator,
Text,
} from 'ui/src'
import { AnimatedFlex, ColorTokens, Flex, GeneratedIcon, Inset, Separator, Text } from 'ui/src'
import { TimePast } from 'ui/src/components/icons'
import { HandleBar } from 'wallet/src/components/modals/HandleBar'
import { useBottomSheetFocusHook } from 'wallet/src/components/modals/hooks'
import { FORQuote } from 'wallet/src/features/fiatOnRamp/types'
......@@ -94,7 +86,7 @@ export function FiatOnRampServiceProvidersScreen({ navigation }: Props): JSX.Ele
<Flex px="$spacing12">
{type === InitialQuoteSelection.Best ? null : type === InitialQuoteSelection.MostRecent ? (
<SectionHeader
Icon={Icons.TimePast}
Icon={TimePast}
iconColor="$neutral3"
title={t('fiatOnRamp.quote.type.recent')}
/>
......
......@@ -353,15 +353,13 @@ export function HomeScreen(props?: AppStackScreenProp<Screens.Home>): JSX.Elemen
}, [dispatch])
const onPressSend = useCallback(() => dispatch(openModal({ name: ModalName.Send })), [dispatch])
const onPressReceive = useCallback(() => {
if (cexTransferProviders.length > 0) {
dispatch(
openModal({ name: ModalName.ReceiveCryptoModal, initialState: cexTransferProviders })
)
} else {
dispatch(
openModal({ name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr })
dispatch(
openModal(
cexTransferProviders.length > 0
? { name: ModalName.ReceiveCryptoModal, initialState: cexTransferProviders }
: { name: ModalName.WalletConnectScan, initialState: ScannerModalState.WalletQr }
)
}
)
}, [dispatch, cexTransferProviders])
const onPressViewOnlyLabel = useCallback(
() => dispatch(openModal({ name: ModalName.ViewOnlyExplainer })),
......
......@@ -10,8 +10,9 @@ import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen'
import { OptionCard } from 'src/features/onboarding/OptionCard'
import { OnboardingScreens } from 'src/screens/Screens'
import { useAddBackButton } from 'src/utils/useAddBackButton'
import { Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import EyeIcon from 'ui/src/assets/icons/eye.svg'
import { OSDynamicCloudIcon, PaperStack } from 'ui/src/components/icons'
import { useIsDarkMode } from 'ui/src/hooks/useIsDarkMode'
import { AppTFunction } from 'ui/src/i18n/types'
import { iconSizes } from 'ui/src/theme'
......@@ -37,7 +38,7 @@ const options: ImportMethodOption[] = [
{
title: (t: AppTFunction) => t('onboarding.import.method.import.title'),
blurb: (t: AppTFunction) => t('onboarding.import.method.import.message'),
icon: <Icons.PaperStack color="$accent1" size={18} strokeWidth={1.5} />,
icon: <PaperStack color="$accent1" size={18} strokeWidth={1.5} />,
nav: OnboardingScreens.SeedPhraseInput,
importType: ImportType.SeedPhrase,
name: ElementName.OnboardingImportSeedPhrase,
......@@ -48,7 +49,7 @@ const options: ImportMethodOption[] = [
isAndroid
? t(`onboarding.import.method.restore.message.android`)
: t(`onboarding.import.method.restore.message.ios`),
icon: <Icons.OSDynamicCloudIcon color="$accent1" size="$icon.24" />,
icon: <OSDynamicCloudIcon color="$accent1" size="$icon.24" />,
nav: OnboardingScreens.RestoreCloudBackup,
importType: ImportType.Restore,
name: ElementName.RestoreFromCloud,
......
......@@ -13,7 +13,8 @@ import { useCloudBackups } from 'src/features/CloudBackup/hooks'
import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen'
import { OnboardingScreens } from 'src/screens/Screens'
import { useAddBackButton } from 'src/utils/useAddBackButton'
import { Flex, Icons, Loader } from 'ui/src'
import { Flex, Loader } from 'ui/src'
import { OSDynamicCloudIcon } from 'ui/src/components/icons'
import { imageSizes } from 'ui/src/theme'
import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName'
import { logger } from 'utilities/src/logger/logger'
......@@ -139,7 +140,7 @@ export function RestoreCloudBackupLoadingScreen({
<Flex alignSelf="center" px="$spacing16">
<BaseCard.ErrorState
description={t('account.cloud.error.backup.message')}
icon={<Icons.OSDynamicCloudIcon color="$neutral3" size={imageSizes.image48} />}
icon={<OSDynamicCloudIcon color="$neutral3" size={imageSizes.image48} />}
retryButtonLabel={t('common.button.retry')}
title={t('account.cloud.error.backup.title')}
onRetry={fetchCloudStorageBackups}
......@@ -162,7 +163,7 @@ export function RestoreCloudBackupLoadingScreen({
description={t('account.cloud.empty.description', {
cloudProviderName: getCloudProviderName(),
})}
icon={<Icons.OSDynamicCloudIcon color="$neutral3" size={imageSizes.image48} />}
icon={<OSDynamicCloudIcon color="$neutral3" size={imageSizes.image48} />}
retryButtonLabel={t('common.button.retry')}
title={t('account.cloud.empty.title')}
onRetry={fetchCloudStorageBackups}
......
......@@ -9,7 +9,8 @@ import { CloudStorageMnemonicBackup } from 'src/features/CloudBackup/types'
import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen'
import { OnboardingScreens } from 'src/screens/Screens'
import { useAddBackButton } from 'src/utils/useAddBackButton'
import { Flex, Icons, Text, TouchableArea, Unicon, UniconV2, useIsDarkMode } from 'ui/src'
import { Flex, Text, TouchableArea, Unicon, UniconV2, useIsDarkMode } from 'ui/src'
import { RotatableChevron } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
import { useFeatureFlag } from 'uniswap/src/features/gating/hooks'
......@@ -85,7 +86,7 @@ export function RestoreCloudBackupScreen({ navigation, route: { params } }: Prop
</Text>
</Flex>
</Flex>
<Icons.RotatableChevron
<RotatableChevron
color="$neutral2"
direction="end"
height={iconSizes.icon20}
......
......@@ -9,7 +9,8 @@ import { GenericImportForm } from 'src/features/import/GenericImportForm'
import { SafeKeyboardOnboardingScreen } from 'src/features/onboarding/SafeKeyboardOnboardingScreen'
import { OnboardingScreens } from 'src/screens/Screens'
import { useAddBackButton } from 'src/utils/useAddBackButton'
import { Button, Flex, Icons, Text, TouchableArea } from 'ui/src'
import { Button, Flex, Text, TouchableArea } from 'ui/src'
import { QuestionInCircleFilled } from 'ui/src/components/icons'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { ImportType } from 'wallet/src/features/onboarding/types'
import { useNonPendingSignerAccounts } from 'wallet/src/features/wallet/hooks'
......@@ -149,7 +150,7 @@ export function SeedPhraseInputScreen({ navigation, route: { params } }: Props):
flexDirection="row"
gap="$spacing8"
onPress={isRestoringMnemonic ? onPressTryAgainButton : onPressRecoveryHelpButton}>
<Icons.QuestionInCircleFilled color="$surface1" size="$icon.20" />
<QuestionInCircleFilled color="$surface1" size="$icon.20" />
<Text $short={{ variant: 'body3' }} color="$neutral3" variant="body2">
{isRestoringMnemonic
? t('account.recoveryPhrase.helpText.restoring')
......
......@@ -12,7 +12,8 @@ import { useCompleteOnboardingCallback } from 'src/features/onboarding/hooks'
import { sendMobileAnalyticsEvent } from 'src/features/telemetry'
import { OnboardingScreens } from 'src/screens/Screens'
import { useAddBackButton } from 'src/utils/useAddBackButton'
import { Button, Flex, Icons, Text } from 'ui/src'
import { Button, Flex, Text } from 'ui/src'
import { GraduationCap } from 'ui/src/components/icons'
import { normalizeTextInput } from 'utilities/src/primitives/string'
import { ChainId } from 'wallet/src/constants/chains'
import { usePortfolioBalances } from 'wallet/src/features/dataApi/balances'
......@@ -191,7 +192,7 @@ export function WatchWalletScreen({ navigation, route: { params } }: Props): JSX
borderRadius="$rounded16"
gap="$spacing16"
p="$spacing16">
<Icons.GraduationCap color="$neutral2" size="$icon.20" />
<GraduationCap color="$neutral2" size="$icon.20" />
<Text color="$neutral2" flexShrink={1} variant="body3">
{t('account.wallet.watch.message')}
</Text>
......
......@@ -16,8 +16,9 @@ import { isCloudStorageAvailable } from 'src/features/CloudBackup/RNCloudStorage
import { OnboardingScreen } from 'src/features/onboarding/OnboardingScreen'
import { OptionCard } from 'src/features/onboarding/OptionCard'
import { OnboardingScreens, Screens } from 'src/screens/Screens'
import { Button, Flex, Icons, Text, TouchableArea, useIsDarkMode, useSporeColors } from 'ui/src'
import { Button, Flex, Text, TouchableArea, useIsDarkMode, useSporeColors } from 'ui/src'
import PaperIcon from 'ui/src/assets/icons/paper-stack.svg'
import { OSDynamicCloudIcon, QuestionInCircleFilled } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName'
import { isAndroid } from 'uniswap/src/utils/platform'
......@@ -132,7 +133,7 @@ export function BackupScreen({ navigation, route: { params } }: Props): JSX.Elem
blurb={t('onboarding.backup.option.cloud.description')}
disabled={hasCloudBackup}
elementName={ElementName.AddCloudBackup}
icon={<Icons.OSDynamicCloudIcon color="$accent1" size="$icon.16" />}
icon={<OSDynamicCloudIcon color="$accent1" size="$icon.16" />}
title={t('onboarding.backup.option.cloud.title', {
cloudProviderName: getCloudProviderName(),
})}
......@@ -199,7 +200,7 @@ function RecoveryPhraseTooltip({
gap="$spacing8"
py="$spacing8"
onPress={onPressEducationButton}>
<Icons.QuestionInCircleFilled color="$surface1" size="$icon.20" />
<QuestionInCircleFilled color="$surface1" size="$icon.20" />
<Text color="$neutral3" variant="body2">
{t('onboarding.tooltip.recoveryPhrase.trigger')}
</Text>
......
......@@ -6,13 +6,13 @@ import { selectModalState } from 'src/features/modals/selectModalState'
import {
Flex,
HapticFeedback,
Icons,
ImpactFeedbackStyle,
Separator,
Text,
TouchableArea,
useSporeColors,
} from 'ui/src'
import { CopySheets, QrCode } from 'ui/src/components/icons'
import { iconSizes } from 'ui/src/theme'
import { ScannerModalState } from 'wallet/src/components/QRCodeScanner/constants'
import { AddressDisplay } from 'wallet/src/components/accounts/AddressDisplay'
......@@ -84,7 +84,7 @@ function AccountCardItem({ onClose }: { onClose: () => void }): JSX.Element {
borderRadius={ICON_BORDER_RADIUS}
height={ICON_SIZE}
width={ICON_SIZE}>
<Icons.CopySheets color="$neutral2" size={iconSizes.icon16} />
<CopySheets color="$neutral2" size={iconSizes.icon16} />
</Flex>
</TouchableArea>
<Flex
......@@ -94,7 +94,7 @@ function AccountCardItem({ onClose }: { onClose: () => void }): JSX.Element {
borderRadius={ICON_BORDER_RADIUS}
height={ICON_SIZE}
width={ICON_SIZE}>
<Icons.QrCode color="$neutral2" size={iconSizes.icon16} />
<QrCode color="$neutral2" size={iconSizes.icon16} />
</Flex>
</Flex>
</Flex>
......
......@@ -7,7 +7,8 @@ import { BackHeader } from 'src/components/layout/BackHeader'
import { Screen } from 'src/components/layout/Screen'
import { CloudBackupPasswordForm } from 'src/features/CloudBackup/CloudBackupPasswordForm'
import { Screens } from 'src/screens/Screens'
import { Button, Flex, Icons, Text, useSporeColors } from 'ui/src'
import { Button, Flex, Text, useSporeColors } from 'ui/src'
import { OSDynamicCloudIcon } from 'ui/src/components/icons'
import { getCloudProviderName } from 'uniswap/src/utils/cloud-backup/getCloudProviderName'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { ElementName, ModalName } from 'wallet/src/telemetry/constants'
......@@ -64,7 +65,7 @@ export function SettingsCloudBackupPasswordCreateScreen({
<Flex px="$spacing16" py="$spacing12">
<Flex centered gap="$spacing16">
<Flex backgroundColor="$accentSoft" borderRadius="$rounded12" p="$spacing12">
<Icons.OSDynamicCloudIcon color="$accent1" size="$icon.24" />
<OSDynamicCloudIcon color="$accent1" size="$icon.24" />
</Flex>
<Text textAlign="center" variant="subheading1">
{t('settings.setting.backup.modal.title', {
......
......@@ -4,7 +4,8 @@ import { Action } from 'redux'
import { useAppDispatch } from 'src/app/hooks'
import { VirtualizedList } from 'src/components/layout/VirtualizedList'
import { closeModal } from 'src/features/modals/modalSlice'
import { Flex, Icons, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Flex, Text, TouchableArea, useSporeColors } from 'ui/src'
import { Check } from 'ui/src/components/icons'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { FiatCurrency, ORDERED_CURRENCIES } from 'wallet/src/features/fiatCurrency/constants'
import { useAppFiatCurrency, useFiatCurrencyInfo } from 'wallet/src/features/fiatCurrency/hooks'
......@@ -80,7 +81,7 @@ function FiatCurrencyOption({ active, currency, onPress }: FiatCurrencyOptionPro
{code}
</Text>
</Flex>
{active && <Icons.Check color={colors.accent1.val} size="$icon.24" />}
{active && <Check color={colors.accent1.val} size="$icon.24" />}
</Flex>
</TouchableArea>
)
......
......@@ -4,7 +4,8 @@ import { Linking } from 'react-native'
import { Action } from 'redux'
import { useAppDispatch } from 'src/app/hooks'
import { closeModal } from 'src/features/modals/modalSlice'
import { Button, Flex, Icons, Text } from 'ui/src'
import { Button, Flex, Text } from 'ui/src'
import { Language } from 'ui/src/components/icons'
import { isAndroid } from 'uniswap/src/utils/platform'
import { BottomSheetModal } from 'wallet/src/components/modals/BottomSheetModal'
import { ElementName, ModalName } from 'wallet/src/telemetry/constants'
......@@ -33,7 +34,7 @@ export function SettingsLanguageModal(): JSX.Element {
<BottomSheetModal name={ModalName.LanguageSelector} onClose={onClose}>
<Flex centered mt="$spacing16">
<Flex borderRadius="$rounded12" p="$spacing12" style={{ backgroundColor: LIGHT_BLUE }}>
<Icons.Language color="$DEP_blue300" size="$icon.24" strokeWidth={1.5} />
<Language color="$DEP_blue300" size="$icon.24" strokeWidth={1.5} />
</Flex>
</Flex>
<Flex gap="$spacing24" pt="$spacing24" px="$spacing24">
......
......@@ -30,7 +30,6 @@ import {
Button,
Flex,
IconProps,
Icons,
Text,
TouchableArea,
useDeviceInsets,
......@@ -45,6 +44,17 @@ import FingerprintIcon from 'ui/src/assets/icons/fingerprint.svg'
import LockIcon from 'ui/src/assets/icons/lock.svg'
import MessageQuestion from 'ui/src/assets/icons/message-question.svg'
import UniswapIcon from 'ui/src/assets/icons/uniswap-logo.svg'
import {
Chart,
Coins,
Feedback,
Key,
Language,
LineChartDots,
OSDynamicCloudIcon,
RotatableChevron,
ShieldQuestion,
} from 'ui/src/components/icons'
import { iconSizes, spacing } from 'ui/src/theme'
import { uniswapUrls } from 'uniswap/src/constants/urls'
import { FeatureFlags } from 'uniswap/src/features/gating/flags'
......@@ -152,7 +162,7 @@ export function SettingsScreen(): JSX.Element {
modal: ModalName.FiatCurrencySelector,
text: t('settings.setting.currency.title'),
currentSetting: currentFiatCurrencyInfo.code,
icon: <Icons.Coins {...iconProps} />,
icon: <Coins {...iconProps} />,
},
] as SettingsSectionItem[])
: []),
......@@ -160,22 +170,22 @@ export function SettingsScreen(): JSX.Element {
modal: ModalName.LanguageSelector,
text: t('settings.setting.language.title'),
currentSetting: currentLanguage,
icon: <Icons.Language {...iconProps} />,
icon: <Language {...iconProps} />,
},
{
screen: Screens.SettingsPrivacy,
text: t('settings.setting.privacy.title'),
icon: <Icons.LineChartDots {...iconProps} />,
icon: <LineChartDots {...iconProps} />,
},
{
text: t('settings.setting.smallBalances.title'),
icon: <Icons.Chart {...iconProps} />,
icon: <Chart {...iconProps} />,
isToggleEnabled: hideSmallBalances,
onToggle: onToggleHideSmallBalances,
},
{
text: t('settings.setting.unknownTokens.title'),
icon: <Icons.ShieldQuestion {...iconProps} />,
icon: <ShieldQuestion {...iconProps} />,
isToggleEnabled: hideSpamTokens,
onToggle: onToggleHideSpamTokens,
},
......@@ -203,7 +213,7 @@ export function SettingsScreen(): JSX.Element {
{
screen: Screens.SettingsViewSeedPhrase,
text: t('settings.setting.recoveryPhrase.title'),
icon: <Icons.Key {...iconProps} />,
icon: <Key {...iconProps} />,
screenProps: { address: signerAccount?.address ?? '', walletNeedsRestore },
isHidden: noSignerAccountImported,
},
......@@ -225,7 +235,7 @@ export function SettingsScreen(): JSX.Element {
text: t('settings.setting.backup.selected', {
cloudProviderName: getCloudProviderName(),
}),
icon: <Icons.OSDynamicCloudIcon color="$neutral2" size="$icon.24" />,
icon: <OSDynamicCloudIcon color="$neutral2" size="$icon.24" />,
isHidden: noSignerAccountImported,
},
],
......@@ -240,7 +250,7 @@ export function SettingsScreen(): JSX.Element {
headerTitle: t('settings.action.feedback'),
},
text: t('settings.action.feedback'),
icon: <Icons.Feedback color="$neutral2" size="$icon.24" />,
icon: <Feedback color="$neutral2" size="$icon.24" />,
},
{
screen: Screens.WebView,
......@@ -374,7 +384,7 @@ function OnboardingRow({ iconProps }: { iconProps: SvgProps }): JSX.Element {
Onboarding
</Text>
</Flex>
<Icons.RotatableChevron
<RotatableChevron
color="$neutral3"
direction="end"
height={iconSizes.icon24}
......@@ -444,7 +454,7 @@ function WalletSettings(): JSX.Element {
size={iconSizes.icon40}
variant="body1"
/>
<Icons.RotatableChevron
<RotatableChevron
color="$neutral3"
direction="end"
height={iconSizes.icon24}
......
......@@ -12,7 +12,8 @@ import { SettingsStackParamList } from 'src/app/navigation/types'
import { BackHeader } from 'src/components/layout/BackHeader'
import { Screen } from 'src/components/layout/Screen'
import { UnitagBanner } from 'src/components/unitags/UnitagBanner'
import { Button, Flex, Icons, Text } from 'ui/src'
import { Button, Flex, Text } from 'ui/src'
import { PenLine } from 'ui/src/components/icons'
import { fonts } from 'ui/src/theme'
import { isIOS } from 'uniswap/src/utils/platform'
import { TextInput } from 'wallet/src/components/input/TextInput'
......@@ -122,7 +123,7 @@ export function SettingsWalletEdit({
{showEditButton && accountNameIsEditable && (
<Button
backgroundless
icon={<Icons.PenLine color="$neutral3" />}
icon={<PenLine color="$neutral3" />}
m="$none"
size="medium"
onPress={onEditButtonPress}
......
......@@ -60,6 +60,7 @@ ignores: [
"pages",
"polyfills",
"rpc",
"shared-cloud",
"state",
"test-utils",
"theme",
......
......@@ -2,7 +2,11 @@
## Purpose
These functions utilize Cloudflare Functions to dynamically inject meta tags server side for richer link sharing capabilities.
These functions utilize Cloudflare Functions to dynamically inject meta tags server-side for richer link sharing capabilities.
Search engines and social media platforms' crawlers read the initial HTML of a page to index and understand its content. These crawlers often do not execute JavaScript, meaning dynamically added client-side content, including metatags, may not be indexed or recognized. This is why we render our metatags on server-side.
However, these server-side injected metatags do not automatically update during client-side navigation managed by react-router. To address this, we implement additional client-side logic - see `src/pages/metatags.ts`. This client-side metatag management is particularly important for compatibility with features like Safari's native share, which relies on metatags like `og:url` to represent the shared page.
## Functions
......
......@@ -16,26 +16,54 @@ test('should append meta tag to element', () => {
new Request('http://localhost')
)
injector.append(element, property, content)
expect(element.append).toHaveBeenCalledWith(`<meta property="${property}" content="${content}"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="${property}" content="${content}" data-rh="true">`, {
html: true,
})
injector.element(element)
expect(element.append).toHaveBeenCalledWith(`<meta property="og:title" content="test"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="og:description" content="testDescription"/>`, {
expect(element.append).toHaveBeenCalledWith(`<meta property="og:title" content="test" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(
`<meta property="og:description" content="testDescription" data-rh="true">`,
{
html: true,
}
)
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image" content="testImage" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:width" content="1200" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:height" content="630" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:alt" content="test" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="og:type" content="website" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="og:url" content="testUrl" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image" content="testImage"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:width" content="1200"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:height" content="630"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="og:image:alt" content="test"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="og:type" content="website"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="og:url" content="testUrl"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:card" content="summary_large_image"/>`, {
expect(element.append).toHaveBeenCalledWith(
`<meta property="twitter:card" content="summary_large_image" data-rh="true">`,
{
html: true,
}
)
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:title" content="test" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:image" content="testImage" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:image:alt" content="test" data-rh="true">`, {
html: true,
})
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:title" content="test"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:image" content="testImage"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="twitter:image:alt" content="test"/>`, { html: true })
expect(element.append).toHaveBeenCalledTimes(13)
})
......@@ -56,5 +84,7 @@ test('should pass through header blocked paths', () => {
request
)
injector.element(element)
expect(element.append).toHaveBeenCalledWith(`<meta property="x:blocked-paths" content="/"/>`, { html: true })
expect(element.append).toHaveBeenCalledWith(`<meta property="x:blocked-paths" content="/" data-rh="true">`, {
html: true,
})
})
type MetaTagInjectorInput = {
title: string
image?: string
url: string
description?: string
}
import { MetaTagInjectorInput } from 'shared-cloud/metatags'
/**
* Listener class for Cloudflare's HTMLRewriter {@link https://developers.cloudflare.com/workers/runtime-apis/html-rewriter}
......@@ -15,7 +10,8 @@ export class MetaTagInjector implements HTMLRewriterElementContentHandlers {
constructor(private input: MetaTagInjectorInput, private request: Request) {}
append(element: Element, property: string, content: string) {
element.append(`<meta property="${property}" content="${content}"/>`, { html: true })
// without adding data-rh="true", react-helmet-async doesn't overwrite existing metatags
element.append(`<meta property="${property}" content="${content}" data-rh="true">`, { html: true })
}
element(element: Element) {
......
......@@ -2,21 +2,19 @@ const defaultUrls = ['http://127.0.0.1:3000/', 'http://127.0.0.1:3000/swap', 'ht
test.each(defaultUrls)('should inject metadata for valid collections', async (defaultUrl) => {
const body = await fetch(new Request(defaultUrl)).then((res) => res.text())
expect(body).toContain(`<meta property="og:title" content="Uniswap Interface"/>`)
expect(body).toContain(`<meta property="og:title" content="Uniswap Interface"`)
expect(body).toContain(`<meta property="og:description" content="Swap or provide liquidity on the Uniswap Protocol"`)
expect(body).toContain(
`<meta property="og:description" content="Swap or provide liquidity on the Uniswap Protocol"/>`
`<meta property="og:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"`
)
expect(body).toContain(`<meta property="og:image:width" content="1200"`)
expect(body).toContain(`<meta property="og:image:height" content="630"`)
expect(body).toContain(`<meta property="og:type" content="website"`)
expect(body).toContain(`<meta property="og:image:alt" content="Uniswap Interface"`)
expect(body).toContain(`<meta property="twitter:card" content="summary_large_image"`)
expect(body).toContain(`<meta property="twitter:title" content="Uniswap Interface"`)
expect(body).toContain(
`<meta property="og:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/>`
`<meta property="twitter:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"`
)
expect(body).toContain(`<meta property="og:image:width" content="1200"/>`)
expect(body).toContain(`<meta property="og:image:height" content="630"/>`)
expect(body).toContain(`<meta property="og:type" content="website"/>`)
expect(body).toContain(`<meta property="og:image:alt" content="Uniswap Interface"/>`)
expect(body).toContain(`<meta property="twitter:card" content="summary_large_image"/>`)
expect(body).toContain(`<meta property="twitter:title" content="Uniswap Interface"/>`)
expect(body).toContain(
`<meta property="twitter:image" content="http://127.0.0.1:3000/images/1200x630_Rich_Link_Preview_Image.png"/>`
)
expect(body).toContain(`<meta property="twitter:image:alt" content="Uniswap Interface"/>`)
expect(body).toContain(`<meta property="twitter:image:alt" content="Uniswap Interface"`)
})
......@@ -31,18 +31,18 @@ test.each(tokens)('should inject metadata for valid tokens', async (token) => {
const url = 'http://127.0.0.1:3000/explore/tokens/' + token.network + '/' + token.address
const body = await fetch(new Request(url)).then((res) => res.text())
expect(body).toMatchSnapshot()
expect(body).toContain(`<meta property="og:title" content="Get ${token.symbol} on Uniswap"/>`)
expect(body).toContain(`<meta property="og:title" content="Get ${token.symbol} on Uniswap" data-rh="true">`)
expect(body).not.toContain(`<meta property="og:description"`)
expect(body).toContain(`<meta property="og:image" content="${token.image}"/>`)
expect(body).toContain(`<meta property="og:image:width" content="1200"/>`)
expect(body).toContain(`<meta property="og:image:height" content="630"/>`)
expect(body).toContain(`<meta property="og:type" content="website"/>`)
expect(body).toContain(`<meta property="og:url" content="${url}"/>`)
expect(body).toContain(`<meta property="og:image:alt" content="Get ${token.symbol} on Uniswap"/>`)
expect(body).toContain(`<meta property="twitter:card" content="summary_large_image"/>`)
expect(body).toContain(`<meta property="twitter:title" content="Get ${token.symbol} on Uniswap"/>`)
expect(body).toContain(`<meta property="twitter:image" content="${token.image}"/>`)
expect(body).toContain(`<meta property="twitter:image:alt" content="Get ${token.symbol} on Uniswap"/>`)
expect(body).toContain(`<meta property="og:image" content="${token.image}" data-rh="true">`)
expect(body).toContain(`<meta property="og:image:width" content="1200" data-rh="true">`)
expect(body).toContain(`<meta property="og:image:height" content="630" data-rh="true">`)
expect(body).toContain(`<meta property="og:type" content="website" data-rh="true">`)
expect(body).toContain(`<meta property="og:url" content="${url}" data-rh="true">`)
expect(body).toContain(`<meta property="og:image:alt" content="Get ${token.symbol} on Uniswap" data-rh="true">`)
expect(body).toContain(`<meta property="twitter:card" content="summary_large_image" data-rh="true">`)
expect(body).toContain(`<meta property="twitter:title" content="Get ${token.symbol} on Uniswap" data-rh="true">`)
expect(body).toContain(`<meta property="twitter:image" content="${token.image}" data-rh="true">`)
expect(body).toContain(`<meta property="twitter:image:alt" content="Get ${token.symbol} on Uniswap" data-rh="true">`)
})
const invalidTokens = [
......
/* eslint-disable import/no-unused-modules */
import getAsset from '../../utils/getAsset'
import getAsset from 'utils/getAsset'
import { transformResponse } from '../../utils/transformResponse'
export const onRequest: PagesFunction = async ({ params, request, next }) => {
......
......@@ -23,18 +23,24 @@ test.each(assets)('should inject metadata for valid assets', async (nft) => {
const url = 'http://127.0.0.1:3000/nfts/asset/' + nft.address + '/' + nft.assetId
const body = await fetch(new Request(url)).then((res) => res.text())
expect(body).toMatchSnapshot(nft.collectionName)
expect(body).toContain(`<meta property="og:title" content="${nft.collectionName} #${nft.assetId}"/>`)
expect(body).toContain(`<meta property="og:title" content="${nft.collectionName} #${nft.assetId}" data-rh="true">`)
expect(body).not.toContain(`<meta property="og:description"`)
expect(body).toContain(`<meta property="og:image" content="${nft.image}"/>`)
expect(body).toContain(`<meta property="og:image:width" content="1200"/>`)
expect(body).toContain(`<meta property="og:image:height" content="630"/>`)
expect(body).toContain(`<meta property="og:type" content="website"/>`)
expect(body).toContain(`<meta property="og:url" content="${url}"/>`)
expect(body).toContain(`<meta property="og:image:alt" content="${nft.collectionName} #${nft.assetId}"/>`)
expect(body).toContain(`<meta property="twitter:card" content="summary_large_image"/>`)
expect(body).toContain(`<meta property="twitter:title" content="${nft.collectionName} #${nft.assetId}"/>`)
expect(body).toContain(`<meta property="twitter:image" content="${nft.image}"/>`)
expect(body).toContain(`<meta property="twitter:image:alt" content="${nft.collectionName} #${nft.assetId}"/>`)
expect(body).toContain(`<meta property="og:image" content="${nft.image}" data-rh="true">`)
expect(body).toContain(`<meta property="og:image:width" content="1200" data-rh="true">`)
expect(body).toContain(`<meta property="og:image:height" content="630" data-rh="true">`)
expect(body).toContain(`<meta property="og:type" content="website" data-rh="true">`)
expect(body).toContain(`<meta property="og:url" content="${url}" data-rh="true">`)
expect(body).toContain(
`<meta property="og:image:alt" content="${nft.collectionName} #${nft.assetId}" data-rh="true">`
)
expect(body).toContain(`<meta property="twitter:card" content="summary_large_image" data-rh="true">`)
expect(body).toContain(
`<meta property="twitter:title" content="${nft.collectionName} #${nft.assetId}" data-rh="true">`
)
expect(body).toContain(`<meta property="twitter:image" content="${nft.image}" data-rh="true">`)
expect(body).toContain(
`<meta property="twitter:image:alt" content="${nft.collectionName} #${nft.assetId}" data-rh="true">`
)
})
const invalidAssets = [
......
......@@ -20,18 +20,24 @@ test.each([...collections])('should inject metadata for collections', async (col
const url = 'http://127.0.0.1:3000/nfts/collection/' + collection.address
const body = await fetch(new Request(url)).then((res) => res.text())
expect(body).toMatchSnapshot()
expect(body).toContain(`<meta property="og:title" content="${collection.collectionName} on Uniswap"/>`)
expect(body).toContain(`<meta property="og:title" content="${collection.collectionName} on Uniswap" data-rh="true">`)
expect(body).not.toContain(`<meta property="og:description"`)
expect(body).toContain(`<meta property="og:image" content="${collection.image}"/>`)
expect(body).toContain(`<meta property="og:image:width" content="1200"/>`)
expect(body).toContain(`<meta property="og:image:height" content="630"/>`)
expect(body).toContain(`<meta property="og:type" content="website"/>`)
expect(body).toContain(`<meta property="og:url" content="${url}"/>`)
expect(body).toContain(`<meta property="og:image:alt" content="${collection.collectionName} on Uniswap"/>`)
expect(body).toContain(`<meta property="twitter:card" content="summary_large_image"/>`)
expect(body).toContain(`<meta property="twitter:title" content="${collection.collectionName} on Uniswap"/>`)
expect(body).toContain(`<meta property="twitter:image" content="${collection.image}"/>`)
expect(body).toContain(`<meta property="twitter:image:alt" content="${collection.collectionName} on Uniswap"/>`)
expect(body).toContain(`<meta property="og:image" content="${collection.image}" data-rh="true">`)
expect(body).toContain(`<meta property="og:image:width" content="1200" data-rh="true">`)
expect(body).toContain(`<meta property="og:image:height" content="630" data-rh="true">`)
expect(body).toContain(`<meta property="og:type" content="website" data-rh="true">`)
expect(body).toContain(`<meta property="og:url" content="${url}" data-rh="true">`)
expect(body).toContain(
`<meta property="og:image:alt" content="${collection.collectionName} on Uniswap" data-rh="true">`
)
expect(body).toContain(`<meta property="twitter:card" content="summary_large_image" data-rh="true">`)
expect(body).toContain(
`<meta property="twitter:title" content="${collection.collectionName} on Uniswap" data-rh="true">`
)
expect(body).toContain(`<meta property="twitter:image" content="${collection.image}" data-rh="true">`)
expect(body).toContain(
`<meta property="twitter:image:alt" content="${collection.collectionName} on Uniswap" data-rh="true">`
)
})
const nonexistentCollections = [
......
......@@ -5,8 +5,11 @@
"composite": false,
"incremental": true,
"isolatedModules": false,
"paths": {
"shared-cloud/*": ["../src/shared-cloud/*"]
},
"tsBuildInfoFile": "../node_modules/.cache/tsbuildinfo/functions", // avoid clobbering the build tsbuildinfo
"types": ["jest", "node", "@cloudflare/workers-types"]
"types": ["jest", "node", "@cloudflare/workers-types"],
},
"include": ["**/*.ts", "**/*.tsx"],
}
import { formatNFTAssetMetatagTitleName } from 'shared-cloud/metatags'
import { AssetDocument, AssetQuery } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import client from '../client'
function formatTitleName(name: string | undefined, collectionName: string | undefined, tokenId: string) {
if (name) {
return name
}
if (collectionName && tokenId) {
return collectionName + ' #' + tokenId
}
if (tokenId) {
return 'Asset #' + tokenId
}
return 'View NFT on Uniswap'
}
export default async function getAsset(collectionAddress: string, tokenId: string, url: string) {
const origin = new URL(url).origin
const image = origin + '/api/image/nfts/asset/' + collectionAddress + '/' + tokenId
......@@ -30,7 +18,7 @@ export default async function getAsset(collectionAddress: string, tokenId: strin
if (!asset) {
return undefined
}
const title = formatTitleName(asset.name, asset.collection?.name, asset.tokenId)
const title = formatNFTAssetMetatagTitleName(asset.name, asset.collection?.name, asset.tokenId)
const formattedAsset = {
title,
image,
......
import { formatTokenMetatagTitleName } from 'shared-cloud/metatags'
import {
Chain,
TokenWebDocument,
......@@ -6,16 +7,6 @@ import {
import { NATIVE_CHAIN_ID } from '../../src/constants/tokens'
import client from '../client'
function formatTitleName(symbol: string | undefined, name: string | undefined) {
if (symbol) {
return 'Get ' + symbol + ' on Uniswap'
}
if (name) {
return 'Get ' + name + ' on Uniswap'
}
return 'View Token on Uniswap'
}
const convertTokenAddress = (networkName: string, tokenAddress: string) => {
if (tokenAddress === NATIVE_CHAIN_ID) {
switch (networkName) {
......@@ -47,7 +38,7 @@ export default async function getToken(networkName: string, tokenAddress: string
return undefined
}
const title = formatTitleName(asset.symbol, asset.name)
const title = formatTokenMetatagTitleName(asset.symbol, asset.name)
const formattedAsset = {
title,
......
......@@ -7,9 +7,6 @@
"ajv": "node scripts/compile-ajv-validators.js",
"check:deps:usage": "depcheck",
"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",
"graphql:schema": "node scripts/fetch-schema.js",
"graphql:generate:thegraph": "graphql-codegen --config graphql.thegraph.codegen.config.ts",
"graphql:generate": "yarn graphql:generate:thegraph",
......@@ -19,7 +16,6 @@
"i18n:download": "./scripts/crowdin.sh download",
"i18n:download:if-missing": "ONLY_IF_MISSING=1 ./scripts/crowdin.sh download",
"i18n:extract": "i18next && node ./scripts/fix-empty-i18n-values.js",
"prepare": "concurrently \"npm:ajv\"",
"start": "craco start",
"start:cloud": "NODE_OPTIONS=--dns-result-order=ipv4first PORT=3001 npx wrangler pages dev --compatibility-flags=nodejs_compat --compatibility-date=2023-08-01 --proxy=3001 --port=3000 -- yarn start",
"build:production": "yarn i18n:download:if-missing && craco build",
......@@ -198,20 +194,20 @@
"@uniswap/governance": "1.0.2",
"@uniswap/liquidity-staker": "1.0.2",
"@uniswap/merkle-distributor": "1.0.1",
"@uniswap/permit2-sdk": "1.2.1-beta.1",
"@uniswap/permit2-sdk": "1.2.1",
"@uniswap/redux-multicall": "1.1.8",
"@uniswap/router-sdk": "1.9.1-beta.2",
"@uniswap/sdk-core": "4.2.1-beta.1",
"@uniswap/router-sdk": "1.9.1",
"@uniswap/sdk-core": "4.2.1",
"@uniswap/smart-order-router": "3.17.3",
"@uniswap/token-lists": "1.0.0-beta.33",
"@uniswap/uniswapx-sdk": "2.0.3-alpha.1",
"@uniswap/universal-router-sdk": "2.0.4-beta.3",
"@uniswap/uniswapx-sdk": "2.0.4-alpha.1",
"@uniswap/universal-router-sdk": "2.0.4",
"@uniswap/v2-core": "1.0.1",
"@uniswap/v2-periphery": "1.1.0-beta.0",
"@uniswap/v2-sdk": "4.3.1-beta.2",
"@uniswap/v2-sdk": "4.3.1",
"@uniswap/v3-core": "1.0.1",
"@uniswap/v3-periphery": "1.4.4",
"@uniswap/v3-sdk": "3.11.1-beta.2",
"@uniswap/v3-sdk": "3.11.1",
"@vanilla-extract/css": "1.14.0",
"@vanilla-extract/dynamic": "2.1.0",
"@vanilla-extract/sprinkles": "1.6.1",
......@@ -274,7 +270,6 @@
"react-markdown": "4.3.1",
"react-native-gesture-handler": "2.15.0",
"react-popper": "2.3.0",
"react-query": "3.39.1",
"react-redux": "8.0.5",
"react-router-dom": "6.10.0",
"react-scroll-sync": "0.11.2",
......
......@@ -4,13 +4,9 @@
],
"scriptSrc": [
"'self'",
"'unsafe-eval'",
"'unsafe-inline'",
"'wasm-unsafe-eval'",
"data:",
"https://translate.googleapis.com/",
"https://vercel.com",
"https://vercel.live/",
"https://www.google-analytics.com",
"https://www.googletagmanager.com"
],
......@@ -21,35 +17,12 @@
],
"imgSrc": [
"*",
"'self'",
"blob:",
"data:",
"https://*.uniswap.org",
"https://uniswap.org",
"https://assets.coingecko.com/",
"https://*.amazonaws.com",
"https://basescan.org",
"https://celo-org.github.io/",
"https://cdn.center.app/",
"https://ethereum-optimism.github.io/",
"https://explorer-api.walletconnect.com/",
"https://i.seadn.io/",
"https://lh3.googleusercontent.com/",
"https://openseauserdata.com/",
"https://raw.githubusercontent.com/",
"https://raw.seadn.io/",
"https://s2.coinmarketcap.com/",
"https://static.optimism.io/",
"https://vercel.com",
"https://vercel.live/",
"https://trustwallet.com/",
"https://cloudflare-ipfs.com/"
"data:"
],
"frameSrc": [
"'self'",
"https://buy.moonpay.com/",
"https://vercel.com",
"https://vercel.live/",
"https://verify.walletconnect.com/",
"https://verify.walletconnect.org/"
],
......@@ -57,81 +30,79 @@
"'self'",
"blob:",
"data:",
"wss://*.uniswap.org",
"https://*.uniswap.org",
"https://uniswap.org",
"https://arb1.arbitrum.io",
"https://*.coingecko.com/",
"https://*.alchemy.com",
"https://buy.moonpay.com/",
"https://bsc-dataseed1.binance.org/",
"https://cdn.center.app/",
"https://cdn.jsdelivr.net/npm/@rive-app/canvas@2.8.3/rive.wasm",
"https://*.arbitrum.io",
"https://*.base.org/",
"https://*.coinbase.com",
"https://statsigapi.net",
"https://*.coingecko.com/",
"https://*.coinmarketcap.com/",
"https://*.drpc.org/",
"https://*.gemini.com",
"https://*.googleapis.com",
"https://*.infura.io",
"https://*.nodereal.io",
"https://*.optimism.io",
"https://*.quiknode.pro",
"https://*.twnodes.com",
"https://*.uniswap.org",
"https://*.walletconnect.com",
"https://*.zerion.io",
"https://alfajores-forno.celo-testnet.org",
"https://api.avax.network/ext/bc/C/rpc",
"https://api.moonpay.com/",
"https://api.mycryptoapi.com/eth",
"https://api.opensea.io",
"https://api.studio.thegraph.com/",
"https://api.thegraph.com/",
"https://arbitrum-mainnet.infura.io/",
"https://assets.coingecko.com",
"https://avalanche-mainnet.infura.io/",
"https://base-mainnet.infura.io/",
"https://bridge.arbitrum.io",
"https://celo-mainnet.infura.io/",
"https://bsc-dataseed1.binance.org/",
"https://bsc-dataseed1.bnbchain.org",
"https://buy.moonpay.com/",
"https://cdn.center.app/",
"https://cdn.jsdelivr.net/npm/@rive-app/canvas@2.8.3/rive.wasm",
"https://celo-org.github.io",
"https://cloudflare-ipfs.com",
"https://*.zerion.io",
"https://*.drpc.org/",
"https://*.base.org/",
"https://*.walletconnect.com",
"https://ethereum-optimism.github.io/",
"https://forno.celo.org/",
"https://*.gemini.com",
"https://gateway.ipfs.io/",
"https://i.seadn.io/",
"https://invalid.rpki.cloudflare.com/",
"https://ipfs.io/",
"https://ipv4-check-perf.radar.cloudflare.com",
"https://ipv6-check-perf.radar.cloudflare.com/",
"https://lh3.googleusercontent.com/",
"https://mainnet.infura.io",
"https://*.nodereal.io",
"https://mainnet.base.org/",
"https://o1037921.ingest.sentry.io",
"https://old-wispy-arrow.bsc.quiknode.pro/",
"https://openseauserdata.com/",
"https://performance.radar.cloudflare.com/",
"https://valid.rpki.cloudflare.com",
"https://sparrow.cloudflare.com/",
"https://ipv4-check-perf.radar.cloudflare.com",
"https://ipv6-check-perf.radar.cloudflare.com/",
"https://invalid.rpki.cloudflare.com/",
"https://polygon-rpc.com/",
"https://raw.githubusercontent.com",
"https://raw.seadn.io/",
"https://rpc-mainnet.maticvigil.com",
"https://rpc-mumbai.maticvigil.com",
"https://rpc.ankr.com",
"https://rpc.blast.io/",
"https://rpc.degen.tips",
"https://rpc-mainnet.maticvigil.com",
"https://rpc.goerli.mudit.blog/",
"https://rpc.mevblocker.io/",
"https://rpc.scroll.io/",
"https://*.coinmarketcap.com/",
"https://*.optimism.io",
"https://rpc.sepolia.org/",
"https://sockjs-us3.pusher.com/",
"https://api.studio.thegraph.com/",
"https://*.googleapis.com",
"https://sparrow.cloudflare.com/",
"https://statsigapi.net",
"https://trustwallet.com",
"https://*.arbitrum.io",
"https://tokens.coingecko.com",
"https://*.twnodes.com",
"https://ultra-blue-flower.quiknode.pro",
"https://uniswap.org",
"https://unpkg.com/@rive-app/canvas@2.8.3/rive.wasm",
"https://us-central1-uniswap-mobile.cloudfunctions.net/",
"https://valid.rpki.cloudflare.com",
"https://vercel.com",
"https://vercel.live/",
"https://wallet.crypto.com",
"https://web3.1inch.io",
"https://www.gemini.com",
"https://*.quiknode.pro",
"https://*.infura.io",
"wss://*.uniswap.org",
"wss://relay.walletconnect.com",
"wss://relay.walletconnect.org",
"wss://www.walletlink.org",
"wss://ws-us3.pusher.com/"
"wss://ws-us3.pusher.com/",
"wss://www.walletlink.org"
],
"workerSrc": [
"'self'",
......
......@@ -15,12 +15,18 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#fff" />
<% const cspConfig = require('./csp.json'); %>
<% let cspConfig = require('./csp.json'); %>
<!-- Allow vercel feedback in preview environments -->
<% if (process.env.REACT_APP_STAGING) { %>
<% const cspDevConfig = require('./vercel-csp.json'); %>
<% Object.entries(cspDevConfig).forEach(([key, value]) => cspConfig[key] = cspConfig[key].concat(value)) %>
<% } %>
<meta
http-equiv="Content-Security-Policy"
content="default-src <%= cspConfig.defaultSrc.join(' ') %>; script-src <%= cspConfig.scriptSrc.join(' ') %>; style-src <%= cspConfig.styleSrc.join(' ') %>; img-src <%= cspConfig.imgSrc.join(' ') %>; frame-src <%= cspConfig.frameSrc.join(' ') %>; connect-src <%= cspConfig.connectSrc.join(' ') %>; worker-src <%= cspConfig.workerSrc.join(' ') %>;"
>
<!--
Apple Smart App Banner for Safari on iOS
https://developer.apple.com/documentation/webkit/promoting_apps_with_smart_app_banners
......
{
"scriptSrc": [
"https://vercel.live/"
],
"styleSrc": [
"https://vercel.live/"
],
"frameSrc": [
"https://vercel.live/",
"https://vercel.com"
]
}
import { ChainId, Currency } from '@uniswap/sdk-core'
import { Currency } from '@uniswap/sdk-core'
import { SupportedInterfaceChainId, chainIdToBackendChain } from 'constants/chains'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { apolloClient } from 'graphql/data/apollo/client'
import { gqlTokenToCurrencyInfo } from 'graphql/data/types'
import { chainIdToBackendName } from 'graphql/data/util'
import {
SimpleTokenDocument,
SimpleTokenQuery,
Token,
} from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
export async function getCurrency(currencyId: string, chainId: ChainId): Promise<Currency | undefined> {
export async function getCurrency(
currencyId: string,
chainId: SupportedInterfaceChainId
): Promise<Currency | undefined> {
const isNative =
currencyId === NATIVE_CHAIN_ID || currencyId?.toLowerCase() === 'native' || currencyId?.toLowerCase() === 'eth'
if (isNative) {
......@@ -19,7 +22,7 @@ export async function getCurrency(currencyId: string, chainId: ChainId): Promise
query: SimpleTokenDocument,
variables: {
address: currencyId,
chain: chainIdToBackendName(chainId),
chain: chainIdToBackendChain({ chainId }),
},
})
return gqlTokenToCurrencyInfo(data?.token as Token)?.currency
......
......@@ -3,7 +3,6 @@ import { ChainId, Currency, CurrencyAmount, TradeType } from '@uniswap/sdk-core'
import UniswapXBolt from 'assets/svg/bolt.svg'
import { nativeOnChain } from 'constants/tokens'
import { t } from 'i18n'
import { useCallback } from 'react'
import { isOnChainOrder, useAllSignatures } from 'state/signatures/hooks'
import { SignatureDetails, SignatureType } from 'state/signatures/types'
import { useMultichainTransactions } from 'state/transactions/hooks'
......@@ -26,9 +25,10 @@ import { TransactionStatus } from 'uniswap/src/data/graphql/uniswap-data-api/__g
import { isAddress } from 'utilities/src/addresses'
import { NumberType, useFormatter } from 'utils/formatNumbers'
import { queryOptions, useQuery } from '@tanstack/react-query'
import { getCurrency } from 'components/AccountDrawer/MiniPortfolio/Activity/getCurrency'
import { useQuery } from 'react-query'
import { CancelledTransactionTitleTable, getActivityTitle, LimitOrderTextTable, OrderTextTable } from '../constants'
import { SupportedInterfaceChainId } from 'constants/chains'
import { CancelledTransactionTitleTable, LimitOrderTextTable, OrderTextTable, getActivityTitle } from '../constants'
import { Activity, ActivityMap } from './types'
type FormatNumberFunctionType = ReturnType<typeof useFormatter>['formatNumber']
......@@ -60,7 +60,7 @@ function buildCurrencyDescriptor(
async function parseSwap(
swap: ExactInputSwapTransactionInfo | ExactOutputSwapTransactionInfo,
chainId: ChainId,
chainId: SupportedInterfaceChainId,
formatNumber: FormatNumberFunctionType
): Promise<Partial<Activity>> {
const [tokenIn, tokenOut] = await Promise.all([
......@@ -104,7 +104,7 @@ function parseWrap(
async function parseApproval(
approval: ApproveTransactionInfo,
chainId: ChainId,
chainId: SupportedInterfaceChainId,
status: TransactionStatus
): Promise<Partial<Activity>> {
const currency = await getCurrency(approval.tokenAddress, chainId)
......@@ -126,7 +126,7 @@ type GenericLPInfo = Omit<
>
async function parseLP(
lp: GenericLPInfo,
chainId: ChainId,
chainId: SupportedInterfaceChainId,
formatNumber: FormatNumberFunctionType
): Promise<Partial<Activity>> {
const [baseCurrency, quoteCurrency] = await Promise.all([
......@@ -141,7 +141,7 @@ async function parseLP(
async function parseCollectFees(
collect: CollectFeesTransactionInfo,
chainId: ChainId,
chainId: SupportedInterfaceChainId,
formatNumber: FormatNumberFunctionType
): Promise<Partial<Activity>> {
// Adapts CollectFeesTransactionInfo to generic LP type
......@@ -160,7 +160,7 @@ async function parseCollectFees(
async function parseMigrateCreateV3(
lp: MigrateV2LiquidityToV3TransactionInfo | CreateV3PoolTransactionInfo,
chainId: ChainId
chainId: SupportedInterfaceChainId
): Promise<Partial<Activity>> {
const [baseCurrency, quoteCurrency] = await Promise.all([
getCurrency(lp.baseCurrencyId, chainId),
......@@ -175,7 +175,7 @@ async function parseMigrateCreateV3(
async function parseSend(
send: SendTransactionInfo,
chainId: ChainId,
chainId: SupportedInterfaceChainId,
formatNumber: FormatNumberFunctionType
): Promise<Partial<Activity>> {
const { currencyId, amount, recipient } = send
......@@ -202,22 +202,9 @@ export function getTransactionStatus(details: TransactionDetails): TransactionSt
: TransactionStatus.Failed
}
// useQuery wrapper for transactionToActivity, for async fetching
export function useTransactionToActivity(transaction: TransactionDetails | undefined, chainId: ChainId) {
const { formatNumber } = useFormatter()
const activityFetcher = useCallback(
async () => transactionToActivity(transaction, chainId, formatNumber),
[chainId, formatNumber, transaction]
)
const { data: activity } = useQuery(`transactionToActivity-${transaction?.hash}-${chainId}`, activityFetcher, {
enabled: !!transaction,
})
return activity
}
export async function transactionToActivity(
details: TransactionDetails | undefined,
chainId: ChainId,
chainId: SupportedInterfaceChainId,
formatNumber: FormatNumberFunctionType
): Promise<Activity | undefined> {
if (!details) return undefined
......@@ -271,13 +258,25 @@ export async function transactionToActivity(
}
}
export function useSignatureToActivity(signature: SignatureDetails | undefined) {
const { formatNumber } = useFormatter()
export function getTransactionToActivityQueryOptions(
transaction: TransactionDetails | undefined,
chainId: SupportedInterfaceChainId,
formatNumber: FormatNumberFunctionType
) {
return queryOptions({
queryKey: ['transactionToActivity', transaction, chainId],
queryFn: async () => transactionToActivity(transaction, chainId, formatNumber),
})
}
const { data: activity } = useQuery(`signatureToActivity-${signature?.orderHash}`, async () => {
return await signatureToActivity(signature, formatNumber)
export function getSignatureToActivityQueryOptions(
signature: SignatureDetails | undefined,
formatNumber: FormatNumberFunctionType
) {
return queryOptions({
queryKey: ['signatureToActivity', signature],
queryFn: async () => signatureToActivity(signature, formatNumber),
})
return activity
}
function convertToSecTimestamp(timestamp: number) {
......@@ -342,24 +341,22 @@ export function useLocalActivities(account: string): ActivityMap {
const allSignatures = useAllSignatures()
const { formatNumber } = useFormatter()
const activityParser = useCallback(async () => {
const transactions = Object.values(allTransactions)
.filter(([transaction]) => transaction.from === account)
.map(([transaction, chainId]) => transactionToActivity(transaction, chainId, formatNumber))
const signatures = Object.values(allSignatures)
.filter((signature) => signature.offerer === account)
.map((signature) => signatureToActivity(signature, formatNumber))
return (await Promise.all([...transactions, ...signatures])).reduce((acc, activity) => {
if (activity) acc[activity.hash] = activity
return acc
}, {} as ActivityMap)
}, [account, allSignatures, allTransactions, formatNumber])
const { data } = useQuery(
`localActivities-${account}-${allSignatures.length}-${allTransactions.length}`,
activityParser
)
const { data } = useQuery({
queryKey: ['localActivities', account],
queryFn: async () => {
const transactions = Object.values(allTransactions)
.filter(([transaction]) => transaction.from === account)
.map(([transaction, chainId]) => transactionToActivity(transaction, chainId, formatNumber))
const signatures = Object.values(allSignatures)
.filter((signature) => signature.offerer === account)
.map((signature) => signatureToActivity(signature, formatNumber))
return (await Promise.all([...transactions, ...signatures])).reduce((acc, activity) => {
if (activity) acc[activity.hash] = activity
return acc
}, {} as ActivityMap)
},
})
return data ?? {}
}
import { ChainId, Currency, NONFUNGIBLE_POSITION_MANAGER_ADDRESSES, TradeType, UNI_ADDRESSES } from '@uniswap/sdk-core'
import UniswapXBolt from 'assets/svg/bolt.svg'
import moonpayLogoSrc from 'assets/svg/moonpay.svg'
import { asSupportedChain } from 'constants/chains'
import { NATIVE_CHAIN_ID, nativeOnChain } from 'constants/tokens'
import { BigNumber } from 'ethers/lib/ethers'
import { formatUnits, parseUnits } from 'ethers/lib/utils'
......@@ -272,7 +271,7 @@ export function offchainOrderDetailsFromGraphQLTransactionActivity(
changes: TransactionChanges,
formatNumberOrString: FormatNumberOrStringFunctionType
): UniswapXOrderDetails | undefined {
const chainId = asSupportedChain(supportedChainIdFromGQLChain(activity.chain))
const chainId = supportedChainIdFromGQLChain(activity.chain)
if (!activity || !activity.details || !chainId) return undefined
if (changes.TokenTransfer.length < 2) return undefined
......
......@@ -8,7 +8,7 @@ import type { AddressMap } from '@uniswap/smart-order-router'
import NFTPositionManagerJSON from '@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json'
import MulticallJSON from '@uniswap/v3-periphery/artifacts/contracts/lens/UniswapInterfaceMulticall.sol/UniswapInterfaceMulticall.json'
import { useWeb3React } from '@web3-react/core'
import { isSupportedChain } from 'constants/chains'
import { useIsSupportedChainIdCallback } from 'constants/chains'
import { RPC_PROVIDERS } from 'constants/providers'
import { BaseContract } from 'ethers/lib/ethers'
import { toContractInput } from 'graphql/data/util'
......@@ -32,6 +32,7 @@ export function useContractMultichain<T extends BaseContract>(
chainIds?: ChainId[]
): ContractMap<T> {
const { chainId: walletChainId, provider: walletProvider } = useWeb3React()
const isSupportedChain = useIsSupportedChainIdCallback()
return useMemo(() => {
const relevantChains =
......@@ -52,7 +53,7 @@ export function useContractMultichain<T extends BaseContract>(
}
return acc
}, {})
}, [ABI, addressMap, chainIds, walletChainId, walletProvider])
}, [ABI, addressMap, chainIds, isSupportedChain, walletChainId, walletProvider])
}
export function useV3ManagerContracts(chainIds: ChainId[]): ContractMap<NonfungiblePositionManager> {
......
......@@ -10,6 +10,7 @@ import { UniswapV3PoolInterface } from 'uniswap/src/abis/types/v3/UniswapV3Pool'
import { DEFAULT_ERC20_DECIMALS } from 'utilities/src/tokens/constants'
import { currencyKey } from 'utils/currencyKey'
import { L1_CHAIN_IDS, L2_CHAIN_IDS, TESTNET_CHAIN_IDS } from 'constants/chains'
import { PositionInfo, useCachedPositions, useGetCachedTokens, usePoolAddressCache } from './cache'
import { Call, DEFAULT_GAS_LIMIT } from './getTokensAsync'
import { useInterfaceMulticallContracts, usePoolPriceMap, useV3ManagerContracts } from './hooks'
......@@ -39,17 +40,9 @@ type FeeAmounts = [BigNumber, BigNumber]
const MAX_UINT128 = BigNumber.from(2).pow(128).sub(1)
const DEFAULT_CHAINS = [
ChainId.MAINNET,
ChainId.ARBITRUM_ONE,
ChainId.OPTIMISM,
ChainId.POLYGON,
ChainId.CELO,
ChainId.BNB,
ChainId.AVALANCHE,
ChainId.BASE,
ChainId.BLAST,
]
const DEFAULT_CHAINS = [...L1_CHAIN_IDS, ...L2_CHAIN_IDS].filter((chain: number) => {
return !TESTNET_CHAIN_IDS.includes(chain)
})
type UseMultiChainPositionsData = { positions?: PositionInfo[]; loading: boolean }
......
......@@ -10,7 +10,7 @@ import { useRef, useState } from 'react'
import { MoreHorizontal } from 'react-feather'
import styled from 'styled-components'
import { ClickableStyle, CopyHelper, EllipsisStyle, ThemedText } from 'theme/components'
import { Unitag } from 'ui/src/components/icons/Unitag'
import { Unitag } from 'ui/src/components/icons'
import { shortenAddress } from 'utilities/src/addresses'
const Container = styled.div`
......
......@@ -103,6 +103,7 @@ const AccountDrawerWrapper = styled.div<{ open: boolean }>`
height: calc(100% - ${DRAWER_TOP_MARGIN_MOBILE_WEB});
width: 100%;
max-width: 100%;
border-bottom-right-radius: 0px;
border-bottom-left-radius: 0px;
box-shadow: unset;
......
import { ChainId } from '@uniswap/sdk-core'
import { Container, PopupContainer, StyledXButton, TextContainer } from 'components/Banner/shared/styled'
import { chainIdToBackendChain } from 'constants/chains'
import { ChainOutageData } from 'featureFlags/flags/outageBanner'
import { chainIdToBackendName } from 'graphql/data/util'
import { Trans } from 'i18n'
import { useState } from 'react'
import { Globe } from 'react-feather'
......@@ -43,7 +43,7 @@ export function OutageBanner({ chainId, version }: ChainOutageData) {
const [hidden, setHidden] = useState(false)
const theme = useTheme()
const versionName = version ? version.toString().toLowerCase() + ' data' : 'Data'
const chainName = capitalize(chainIdToBackendName(chainId).toLowerCase())
const chainName = capitalize(chainIdToBackendChain({ chainId, withFallback: true }).toLowerCase())
const versionDescription = version ? ' ' + version.toString().toLowerCase() : ''
return (
......
import { InterfacePageName } from '@uniswap/analytics-events'
import { ChainId } from '@uniswap/sdk-core'
import { OutageBanner, getOutageBannerSessionStorageKey } from 'components/Banner/Outage/OutageBanner'
import { getChainFromChainUrlParam, isChainUrlParam } from 'constants/chains'
import { manualChainOutageAtom, useOutageBanners } from 'featureFlags/flags/outageBanner'
import { getValidUrlChainId } from 'graphql/data/util'
import { useAtomValue } from 'jotai/utils'
import { useMemo } from 'react'
import { useLocation } from 'react-router-dom'
......@@ -17,11 +17,8 @@ export function Banners() {
// Calculate the chainId for the current page's contextual chain (e.g. /tokens/ethereum or /tokens/arbitrum), if it exists.
const pageChainId = useMemo(() => {
const chainName = pathname.split('/').find((maybeChainName) => {
const validatedChainId = getValidUrlChainId(maybeChainName)
return validatedChainId !== undefined
})
return chainName ? getValidUrlChainId(chainName) : ChainId.MAINNET
const chainUrlParam = pathname.split('/').find(isChainUrlParam)
return chainUrlParam ? getChainFromChainUrlParam(chainUrlParam)?.id : ChainId.MAINNET
}, [pathname])
const currentPageHasManualOutage = manualOutage?.chainId === pageChainId
......@@ -29,7 +26,7 @@ export function Banners() {
return (
currentPage &&
pageChainId &&
(outageBanners[pageChainId as ChainId] || currentPageHasManualOutage) &&
(outageBanners[pageChainId] || currentPageHasManualOutage) &&
!sessionStorage.getItem(getOutageBannerSessionStorageKey(pageChainId)) &&
[
InterfacePageName.EXPLORE_PAGE,
......
......@@ -55,6 +55,7 @@ export function ConfirmSwapModal({
fiatValueOutput,
swapResult,
swapError,
priceImpact,
clearSwapState,
onAcceptChanges,
onConfirm,
......@@ -70,6 +71,7 @@ export function ConfirmSwapModal({
fiatValueOutput: { data?: number; isLoading: boolean }
swapResult?: SwapResult
swapError?: Error
priceImpact?: Percent
clearSwapState: () => void
onAcceptChanges?: () => void
onConfirm: () => void
......@@ -219,6 +221,7 @@ export function ConfirmSwapModal({
showAcceptChanges={Boolean(showAcceptChanges)}
onAcceptChanges={onAcceptChanges}
swapErrorMessage={swapFailed ? swapError?.message : undefined}
priceImpact={priceImpact}
/>
</AutoColumn>
</FadePresence>
......
......@@ -8,7 +8,7 @@ import { LoadingOpacityContainer } from 'components/Loader/styled'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { StyledNumericalInput } from 'components/NumericalInput'
import Tooltip from 'components/Tooltip'
import { isSupportedChain } from 'constants/chains'
import { useIsSupportedChainId } from 'constants/chains'
import { Trans } from 'i18n'
import ms from 'ms'
import { darken } from 'polished'
......@@ -269,6 +269,7 @@ const SwapCurrencyInputPanel = forwardRef<HTMLInputElement, SwapCurrencyInputPan
) => {
const [modalOpen, setModalOpen] = useState(false)
const { account, chainId } = useWeb3React()
const chainAllowed = useIsSupportedChainId(chainId)
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
const theme = useTheme()
const { formatCurrencyAmount } = useFormatter()
......@@ -286,8 +287,6 @@ const SwapCurrencyInputPanel = forwardRef<HTMLInputElement, SwapCurrencyInputPan
}
}, [tooltipVisible, numericalInputSettings])
const chainAllowed = isSupportedChain(chainId)
// reset tooltip state when currency changes
useEffect(() => setTooltipVisible(false), [currency])
......
......@@ -4,7 +4,7 @@ import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core'
import { TraceEvent } from 'analytics'
import { LoadingOpacityContainer, loadingOpacityMixin } from 'components/Loader/styled'
import { isSupportedChain } from 'constants/chains'
import { useIsSupportedChainId } from 'constants/chains'
import { Trans } from 'i18n'
import { darken } from 'polished'
import { ReactNode, useCallback, useState } from 'react'
......@@ -215,6 +215,7 @@ export default function CurrencyInputPanel({
}: CurrencyInputPanelProps) {
const [modalOpen, setModalOpen] = useState(false)
const { account, chainId } = useWeb3React()
const chainAllowed = useIsSupportedChainId(chainId)
const selectedCurrencyBalance = useCurrencyBalance(account ?? undefined, currency ?? undefined)
const theme = useTheme()
const { formatCurrencyAmount } = useFormatter()
......@@ -223,8 +224,6 @@ export default function CurrencyInputPanel({
setModalOpen(false)
}, [setModalOpen])
const chainAllowed = isSupportedChain(chainId)
return (
<InputPanel id={id} hideInput={hideInput} {...rest}>
{!locked && (
......
......@@ -224,12 +224,10 @@ export default function FeatureFlagModal() {
</CloseButton>
</Header>
<FlagsColumn>
<FeatureFlagOption flag={FeatureFlags.SendEnabled} label="Send on swap component" />
<FeatureFlagOption
flag={FeatureFlags.Eip6936Enabled}
label="Enable EIP-6963: Multi Injected Provider Discovery"
/>
<FeatureFlagOption flag={FeatureFlags.LimitsEnabled} label="Enable Limits" />
<FeatureFlagOption flag={FeatureFlags.LimitsFees} label="Enable Limits fees" />
<FeatureFlagOption flag={FeatureFlags.CurrencyConversion} label="Enable currency conversion" />
<FeatureFlagOption flag={FeatureFlags.UniconsV2} label="Unicon V2" />
......@@ -238,6 +236,7 @@ export default function FeatureFlagModal() {
<FeatureFlagOption flag={FeatureFlags.V2Explore} label="Enable V2 Explore Data" />
<FeatureFlagOption flag={FeatureFlags.Realtime} label="Realtime activity updates" />
<FeatureFlagOption flag={FeatureFlags.MultipleRoutingOptions} label="Enable Multiple Routing Options" />
<FeatureFlagOption flag={FeatureFlags.MultichainUX} label="Enable Multichain Swap/Send UX" />
<FeatureFlagGroup name="Quick routes">
<FeatureFlagOption flag={FeatureFlags.QuickRouteMainnet} label="Enable quick routes for Mainnet" />
<DynamicConfigDropdown
......
......@@ -10,19 +10,7 @@ export const FEE_AMOUNT_DETAIL: Record<
[FeeAmount.LOWEST]: {
label: '0.01',
description: <Trans>Best for very stable pairs.</Trans>,
supportedChains: [
ChainId.ARBITRUM_ONE,
ChainId.BNB,
ChainId.CELO,
ChainId.CELO_ALFAJORES,
ChainId.MAINNET,
ChainId.OPTIMISM,
ChainId.POLYGON,
ChainId.POLYGON_MUMBAI,
ChainId.AVALANCHE,
ChainId.BASE,
ChainId.BLAST,
],
supportedChains: SUPPORTED_CHAINS,
},
[FeeAmount.LOW]: {
label: '0.05',
......
......@@ -74,7 +74,7 @@ export default function FiatOnrampModal() {
const closeModal = useCloseModal()
const fiatOnrampModalOpen = useModalIsOpen(ApplicationModal.FIAT_ONRAMP)
const { network, tokenAddress } = parsePathParts(location.pathname)
const { chain, tokenAddress } = parsePathParts(location.pathname)
const [signedIframeUrl, setSignedIframeUrl] = useState<string | null>(null)
const [error, setError] = useState<string | null>(null)
......@@ -100,7 +100,7 @@ export default function FiatOnrampModal() {
body: JSON.stringify({
theme: isDarkMode ? 'dark' : 'light',
colorCode: theme.accent1,
defaultCurrencyCode: getDefaultCurrencyCode(tokenAddress, network),
defaultCurrencyCode: getDefaultCurrencyCode(tokenAddress, chain?.backendChain.chain),
redirectUrl: swapUrl,
walletAddresses: JSON.stringify(
MOONPAY_SUPPORTED_CURRENCY_CODES.reduce(
......@@ -121,7 +121,7 @@ export default function FiatOnrampModal() {
} finally {
setLoading(false)
}
}, [account, isDarkMode, network, swapUrl, theme.accent1, tokenAddress])
}, [account, chain, isDarkMode, swapUrl, theme.accent1, tokenAddress])
useEffect(() => {
fetchSignedIframeUrl()
......
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.
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