ci(release): publish latest release

parent 168fd79b
* @uniswap/web-admins
IPFS hash of the deployment: Excited to share some new updates! Here’s what’s new:
- CIDv0: `QmeHTibtaHFy2CsJfYCkWUkz6iRTJPbPQEnAa7syJ8TJuV`
- CIDv1: `bafybeihm5hjaazzxpktgygpmi4gvtkhdfsozzxhuxmyjnbq5jiwuxskfna`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
You can also access the Uniswap Interface from an IPFS gateway.
**BEWARE**: The Uniswap interface uses [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) to remember your settings, such as which tokens you have imported.
**You should always use an IPFS gateway that enforces origin separation**, or our hosted deployment of the latest release at [app.uniswap.org](https://app.uniswap.org).
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeihm5hjaazzxpktgygpmi4gvtkhdfsozzxhuxmyjnbq5jiwuxskfna.ipfs.dweb.link/
- https://bafybeihm5hjaazzxpktgygpmi4gvtkhdfsozzxhuxmyjnbq5jiwuxskfna.ipfs.cf-ipfs.com/
- [ipfs://QmeHTibtaHFy2CsJfYCkWUkz6iRTJPbPQEnAa7syJ8TJuV/](ipfs://QmeHTibtaHFy2CsJfYCkWUkz6iRTJPbPQEnAa7syJ8TJuV/)
### 5.30.1 (2024-05-31)
Expanded support for NFTs — In addition to mainnet Ethereum, you are now able to see NFTs across all other networks that we currently support, including: Polygon, Arbitrum, Optimism, Base, BSC, and Blast.
Improved Unicons — We gave your wallet’s unique Unicon a makeover. Check out the rest of your accounts to see your upgraded icons.
web/5.30.1 mobile/1.28.2
\ No newline at end of file \ No newline at end of file
...@@ -176,7 +176,18 @@ These are some tools you might want to familiarize yourself with to understand t ...@@ -176,7 +176,18 @@ These are some tools you might want to familiarize yourself with to understand t
## Migrations ## Migrations
We use `redux-persist` to persist the Redux state between user sessions. Most of this state is shared between the mobile app and the extension. Please review the [Wallet Migrations README](../../packages/wallet/src/state//README.md) for details on how to write migrations when you add or remove anything from the Redux state structure. We use `redux-persist` to persist Redux state between user sessions. When the Redux state schema is altered, a migration may be needed to transfer the existing persisted state to the new Redux schema. Failing to define a migration results in the app defaulting to the persisted schema, which will very likely cause `undefined` errors because the code has references to Redux state properties that were dropped in favor the persisted schema.
### When to define a migration
Anytime a required property is added or any property is renamed or deleted to/from Redux state. Migrations are not necessary when optional properties are added to an existing slice. Make sure to always add new required properties to the `schema.ts` file as well.
### How to migrate
1. Increment the `version` of `persistConfig` defined within `store.ts`
2. Create a migration function within `migrations.ts`. The migration key should be the same as the `version` defined in the previous step
3. Write a test for your migration within `migrations.test.ts`
4. Create a new schema within `schema.ts` and ensure it is being exported by the `getSchema` function at the bottom of the file
## Troubleshooting ## Troubleshooting
......
...@@ -136,12 +136,12 @@ android { ...@@ -136,12 +136,12 @@ android {
} }
beta { beta {
applicationIdSuffix ".beta" applicationIdSuffix ".beta"
versionName "1.28" versionName "1.28.2"
dimension "variant" dimension "variant"
} }
prod { prod {
dimension "variant" dimension "variant"
versionName "1.28" versionName "1.28.2"
} }
} }
......
...@@ -2536,7 +2536,7 @@ ...@@ -2536,7 +2536,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets;
...@@ -2628,7 +2628,7 @@ ...@@ -2628,7 +2628,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets;
...@@ -2713,7 +2713,7 @@ ...@@ -2713,7 +2713,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension;
...@@ -2799,7 +2799,7 @@ ...@@ -2799,7 +2799,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension;
...@@ -2873,7 +2873,7 @@ ...@@ -2873,7 +2873,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3103,7 +3103,7 @@ ...@@ -3103,7 +3103,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension;
...@@ -3207,7 +3207,7 @@ ...@@ -3207,7 +3207,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
"$(inherited)", "$(inherited)",
"-ObjC", "-ObjC",
...@@ -3278,7 +3278,7 @@ ...@@ -3278,7 +3278,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.28; MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension; PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension;
......
...@@ -9,6 +9,17 @@ import { localizeMock as mockRNLocalize } from 'react-native-localize/mock' ...@@ -9,6 +9,17 @@ import { localizeMock as mockRNLocalize } from 'react-native-localize/mock'
import { AppearanceSettingType } from 'wallet/src/features/appearance/slice' import { AppearanceSettingType } from 'wallet/src/features/appearance/slice'
import { mockLocalizationContext } from 'wallet/src/test/mocks/utils' import { mockLocalizationContext } from 'wallet/src/test/mocks/utils'
// avoids polluting console in test runs, while keeping important log levels
global.console = {
...console,
// uncomment to ignore a specific log level
log: jest.fn(),
debug: jest.fn(),
info: jest.fn(),
// warn: jest.fn(),
// error: jest.fn(),
}
// Mock Sentry crash reporting // Mock Sentry crash reporting
jest.mock('@sentry/react-native', () => ({ jest.mock('@sentry/react-native', () => ({
init: () => jest.fn(), init: () => jest.fn(),
......
...@@ -104,7 +104,6 @@ import { ...@@ -104,7 +104,6 @@ import {
} from 'wallet/src/features/wallet/accounts/types' } from 'wallet/src/features/wallet/accounts/types'
import { initialWalletState, SwapProtectionSetting } from 'wallet/src/features/wallet/slice' import { initialWalletState, SwapProtectionSetting } from 'wallet/src/features/wallet/slice'
import { createMigrate } from 'wallet/src/state/createMigrate' import { createMigrate } from 'wallet/src/state/createMigrate'
import { getAllKeysOfNestedObject } from 'wallet/src/state/testUtils'
import { import {
fiatPurchaseTransactionInfo, fiatPurchaseTransactionInfo,
signerMnemonicAccount, signerMnemonicAccount,
...@@ -125,6 +124,26 @@ const fiatOnRampTxDetailsFailed = transactionDetails({ ...@@ -125,6 +124,26 @@ const fiatOnRampTxDetailsFailed = transactionDetails({
}), }),
}) })
// helps with object assignment
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getAllKeysOfNestedObject = (obj: any, prefix = ''): string[] => {
const keys = Object.keys(obj)
if (!keys.length && prefix !== '') {
return [prefix.slice(0, -1)]
}
return keys.reduce<string[]>((res, el) => {
if (Array.isArray(obj[el])) {
return [...res]
}
if (typeof obj[el] === 'object' && obj[el] !== null) {
return [...res, ...getAllKeysOfNestedObject(obj[el], prefix + el + '.')]
}
return [...res, prefix + el]
}, [])
}
describe('Redux state migrations', () => { describe('Redux state migrations', () => {
it('is able to perform all migrations starting from the initial schema', async () => { it('is able to perform all migrations starting from the initial schema', async () => {
const initialSchemaStub = { const initialSchemaStub = {
...@@ -185,10 +204,6 @@ describe('Redux state migrations', () => { ...@@ -185,10 +204,6 @@ describe('Redux state migrations', () => {
}, },
} }
if (!migratedSchema) {
throw new Error('Migrated schema is undefined')
}
const migratedSchemaKeys = new Set(getAllKeysOfNestedObject(migratedSchema)) const migratedSchemaKeys = new Set(getAllKeysOfNestedObject(migratedSchema))
const latestSchemaKeys = new Set(getAllKeysOfNestedObject(getSchema())) const latestSchemaKeys = new Set(getAllKeysOfNestedObject(getSchema()))
const initialStateKeys = new Set(getAllKeysOfNestedObject(initialState)) const initialStateKeys = new Set(getAllKeysOfNestedObject(initialState))
......
...@@ -19,7 +19,6 @@ import { ...@@ -19,7 +19,6 @@ import {
} from 'wallet/src/features/transactions/types' } from 'wallet/src/features/transactions/types'
import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types' import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types'
import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice' import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice'
import { removeWalletIsUnlockedState } from 'wallet/src/state/sharedMigrations'
export const OLD_DEMO_ACCOUNT_ADDRESS = '0xdd0E380579dF30E38524F9477808d9eE37E2dEa6' export const OLD_DEMO_ACCOUNT_ADDRESS = '0xdd0E380579dF30E38524F9477808d9eE37E2dEa6'
...@@ -877,7 +876,10 @@ export const migrations = { ...@@ -877,7 +876,10 @@ export const migrations = {
return newState return newState
}, },
63: removeWalletIsUnlockedState, 63: function removeWalletIsUnlockedState(state: any) {
} const newState = { ...state }
delete newState.wallet.isUnlocked
export const MOBILE_STATE_VERSION = 63 return newState
},
}
...@@ -3,7 +3,7 @@ import { isRejectedWithValue } from '@reduxjs/toolkit' ...@@ -3,7 +3,7 @@ import { isRejectedWithValue } from '@reduxjs/toolkit'
import * as Sentry from '@sentry/react' import * as Sentry from '@sentry/react'
import { MMKV } from 'react-native-mmkv' import { MMKV } from 'react-native-mmkv'
import { Storage, persistReducer, persistStore } from 'redux-persist' import { Storage, persistReducer, persistStore } from 'redux-persist'
import { MOBILE_STATE_VERSION, migrations } from 'src/app/migrations' import { migrations } from 'src/app/migrations'
import { isNonJestDev } from 'utilities/src/environment' import { isNonJestDev } from 'utilities/src/environment'
import { logger } from 'utilities/src/logger/logger' import { logger } from 'utilities/src/logger/logger'
import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiatOnRamp/api' import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiatOnRamp/api'
...@@ -66,7 +66,7 @@ export const persistConfig = { ...@@ -66,7 +66,7 @@ export const persistConfig = {
key: 'root', key: 'root',
storage: reduxStorage, storage: reduxStorage,
whitelist, whitelist,
version: MOBILE_STATE_VERSION, version: 63,
migrate: createMigrate(migrations), migrate: createMigrate(migrations),
} }
......
...@@ -11,9 +11,11 @@ import { ...@@ -11,9 +11,11 @@ import {
NativeScrollEvent, NativeScrollEvent,
NativeSyntheticEvent, NativeSyntheticEvent,
ScrollView, ScrollView,
StyleProp,
View, View,
ViewStyle,
} from 'react-native' } from 'react-native'
import { useDerivedValue } from 'react-native-reanimated' import { AnimatedStyle, useDerivedValue } from 'react-native-reanimated'
import { ScrollDownOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay' import { ScrollDownOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ScrollDownOverlay'
import { Button, Flex, useDeviceInsets } from 'ui/src' import { Button, Flex, useDeviceInsets } from 'ui/src'
import { spacing } from 'ui/src/theme' import { spacing } from 'ui/src/theme'
...@@ -30,6 +32,7 @@ type ModalWithOverlayProps = PropsWithChildren< ...@@ -30,6 +32,7 @@ type ModalWithOverlayProps = PropsWithChildren<
onReject: () => void onReject: () => void
onConfirm: () => void onConfirm: () => void
disableConfirm?: boolean disableConfirm?: boolean
contentContainerStyle?: StyleProp<AnimatedStyle<StyleProp<ViewStyle>>>
} }
> >
...@@ -48,6 +51,7 @@ export function ModalWithOverlay({ ...@@ -48,6 +51,7 @@ export function ModalWithOverlay({
onReject, onReject,
onConfirm, onConfirm,
disableConfirm, disableConfirm,
contentContainerStyle,
...bottomSheetModalProps ...bottomSheetModalProps
}: ModalWithOverlayProps): JSX.Element { }: ModalWithOverlayProps): JSX.Element {
const scrollViewRef = useRef<ScrollView>(null) const scrollViewRef = useRef<ScrollView>(null)
...@@ -114,10 +118,12 @@ export function ModalWithOverlay({ ...@@ -114,10 +118,12 @@ export function ModalWithOverlay({
<BottomSheetModal overrideInnerContainer {...bottomSheetModalProps}> <BottomSheetModal overrideInnerContainer {...bottomSheetModalProps}>
<BottomSheetScrollView <BottomSheetScrollView
ref={scrollViewRef} ref={scrollViewRef}
contentContainerStyle={{ contentContainerStyle={
contentContainerStyle ?? {
paddingHorizontal: spacing.spacing24, paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing36, paddingTop: spacing.spacing36,
}} }
}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
onLayout={handleScrollViewLayout} onLayout={handleScrollViewLayout}
onScroll={handleScroll}> onScroll={handleScroll}>
......
import { useBottomSheetInternal } from '@gorhom/bottom-sheet'
import { useTranslation } from 'react-i18next'
import Animated, { useAnimatedStyle } from 'react-native-reanimated'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { RequestDetailsContent } from 'src/components/WalletConnect/RequestModal/RequestDetails'
import { useUwuLinkContractAllowlist } from 'src/components/WalletConnect/ScanSheet/util'
import { SignRequest } from 'src/features/walletConnect/walletConnectSlice'
import { Flex, useIsDarkMode } from 'ui/src'
import { spacing } from 'ui/src/theme'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
type Props = {
onClose: () => void
onConfirm: () => void
onReject: () => void
request: SignRequest
}
export function KidSuperCheckinModal({
onClose,
onConfirm,
onReject,
request,
}: Props): JSX.Element {
const { t } = useTranslation()
return (
<ModalWithOverlay
confirmationButtonText={t('common.button.checkin')}
contentContainerStyle={{
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing8,
}}
name={ModalName.UwULinkErc20SendModal}
scrollDownButtonText={t('walletConnect.request.button.scrollDown')}
onClose={onClose}
onConfirm={onConfirm}
onReject={onReject}>
<KidSuperCheckinModalContent request={request} />
</ModalWithOverlay>
)
}
function useUniswapCafeLogo(): string | undefined {
const isDarkMode = useIsDarkMode()
const uwuLinkContractAllowlist = useUwuLinkContractAllowlist()
const logos = uwuLinkContractAllowlist.tokenRecipients.find(
(recipient) => recipient.name === 'Uniswap Cafe'
)?.logo
if (!logos) {
return
}
return isDarkMode ? logos.dark : logos.light
}
function KidSuperCheckinModalContent({ request }: { request: SignRequest }): JSX.Element {
const { animatedFooterHeight } = useBottomSheetInternal()
const bottomSpacerStyle = useAnimatedStyle(() => ({
height: animatedFooterHeight.value,
}))
const logo = useUniswapCafeLogo()
return (
<Flex centered gap="$spacing12" justifyContent="space-between" pb="$spacing12">
<Flex centered gap="$spacing20">
{logo && <RemoteImage height={50} uri={logo} width={200} />}
<Flex
centered
borderColor="$surface3"
borderRadius="$rounded20"
borderWidth={1}
gap="$spacing12"
px="$spacing24"
py="$spacing24">
<RequestDetailsContent request={request} />
</Flex>
</Flex>
<Animated.View style={bottomSpacerStyle} />
</Flex>
)
}
...@@ -194,7 +194,7 @@ function isSignTypedDataRequest(request: WalletConnectRequest): request is SignR ...@@ -194,7 +194,7 @@ function isSignTypedDataRequest(request: WalletConnectRequest): request is SignR
return request.type === EthMethod.SignTypedData || request.type === EthMethod.SignTypedDataV4 return request.type === EthMethod.SignTypedData || request.type === EthMethod.SignTypedDataV4
} }
function RequestDetailsContent({ request }: Props): JSX.Element { export function RequestDetailsContent({ request }: Props): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
if (isSignTypedDataRequest(request)) { if (isSignTypedDataRequest(request)) {
......
...@@ -4,15 +4,18 @@ import { useTranslation } from 'react-i18next' ...@@ -4,15 +4,18 @@ import { useTranslation } from 'react-i18next'
import Animated, { useAnimatedStyle } from 'react-native-reanimated' import Animated, { useAnimatedStyle } from 'react-native-reanimated'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay' import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { UwuLinkErc20Request } from 'src/features/walletConnect/walletConnectSlice' import { UwuLinkErc20Request } from 'src/features/walletConnect/walletConnectSlice'
import { Flex, Text } from 'ui/src' import { Flex, Text, useIsDarkMode } from 'ui/src'
import { iconSizes } from 'ui/src/theme' import { iconSizes, spacing } from 'ui/src/theme'
import { CurrencyInfo } from 'uniswap/src/features/dataApi/types' import { CurrencyInfo } from 'uniswap/src/features/dataApi/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants' import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { NumberType } from 'utilities/src/format/types'
import { TokenLogo } from 'wallet/src/components/CurrencyLogo/TokenLogo' import { TokenLogo } from 'wallet/src/components/CurrencyLogo/TokenLogo'
import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader' import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader'
import { NetworkFee } from 'wallet/src/components/network/NetworkFee' import { NetworkFee } from 'wallet/src/components/network/NetworkFee'
import { CHAIN_INFO } from 'wallet/src/constants/chains' import { CHAIN_INFO } from 'wallet/src/constants/chains'
import { GasFeeResult } from 'wallet/src/features/gas/types' import { GasFeeResult } from 'wallet/src/features/gas/types'
import { RemoteImage } from 'wallet/src/features/images/RemoteImage'
import { useLocalizationContext } from 'wallet/src/features/language/LocalizationContext'
import { useOnChainCurrencyBalance } from 'wallet/src/features/portfolio/api' import { useOnChainCurrencyBalance } from 'wallet/src/features/portfolio/api'
import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency' import { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency'
import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo' import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo'
...@@ -48,6 +51,10 @@ export function UwULinkErc20SendModal({ ...@@ -48,6 +51,10 @@ export function UwULinkErc20SendModal({
return ( return (
<ModalWithOverlay <ModalWithOverlay
confirmationButtonText={t('common.button.pay')} confirmationButtonText={t('common.button.pay')}
contentContainerStyle={{
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing8,
}}
disableConfirm={!hasSufficientTokenFunds || !hasSufficientGasFunds} disableConfirm={!hasSufficientTokenFunds || !hasSufficientGasFunds}
name={ModalName.UwULinkErc20SendModal} name={ModalName.UwULinkErc20SendModal}
scrollDownButtonText={t('walletConnect.request.button.scrollDown')} scrollDownButtonText={t('walletConnect.request.button.scrollDown')}
...@@ -82,10 +89,12 @@ function UwULinkErc20SendModalContent({ ...@@ -82,10 +89,12 @@ function UwULinkErc20SendModalContent({
currencyInfo: Maybe<CurrencyInfo> currencyInfo: Maybe<CurrencyInfo>
}): JSX.Element { }): JSX.Element {
const { t } = useTranslation() const { t } = useTranslation()
const isDarkMode = useIsDarkMode()
const { animatedFooterHeight } = useBottomSheetInternal() const { animatedFooterHeight } = useBottomSheetInternal()
const bottomSpacerStyle = useAnimatedStyle(() => ({ const bottomSpacerStyle = useAnimatedStyle(() => ({
height: animatedFooterHeight.value, height: animatedFooterHeight.value,
})) }))
const { convertFiatAmountFormatted } = useLocalizationContext()
const { chainId, isStablecoin } = request const { chainId, isStablecoin } = request
const nativeCurrency = chainId && NativeCurrency.onChain(chainId) const nativeCurrency = chainId && NativeCurrency.onChain(chainId)
...@@ -104,10 +113,22 @@ function UwULinkErc20SendModalContent({ ...@@ -104,10 +113,22 @@ function UwULinkErc20SendModalContent({
currency: { name, symbol, decimals }, currency: { name, symbol, decimals },
} = currencyInfo } = currencyInfo
const recipientLogoUrl = isDarkMode
? request?.recipient?.logo?.dark
: request?.recipient?.logo?.light
const formattedTokenAmount = isStablecoin
? convertFiatAmountFormatted(formatUnits(request.amount, decimals), NumberType.FiatStandard)
: formatUnits(request.amount, decimals)
return ( return (
<Flex centered gap="$spacing12" justifyContent="space-between"> <Flex centered gap="$spacing12" justifyContent="space-between">
{recipientLogoUrl ? (
<RemoteImage height={50} uri={recipientLogoUrl} width={200} />
) : (
<Text variant="subheading1">{request.recipient.name}</Text> <Text variant="subheading1">{request.recipient.name}</Text>
<Flex centered flex={1} gap="$spacing12" py="$spacing16"> )}
<Flex centered flex={1} gap="$spacing12" py="$spacing36">
{!hasSufficientTokenFunds && ( {!hasSufficientTokenFunds && (
<Text color="red"> <Text color="red">
{t('uwulink.error.insufficientTokens', { {t('uwulink.error.insufficientTokens', {
...@@ -116,11 +137,10 @@ function UwULinkErc20SendModalContent({ ...@@ -116,11 +137,10 @@ function UwULinkErc20SendModalContent({
})} })}
</Text> </Text>
)} )}
<Text fontSize={64} my="$spacing4" pt={42}>{`${isStablecoin ? '$' : ''}${formatUnits( <Text fontSize={64} my="$spacing4" pt={42}>
request.amount, {formattedTokenAmount}
decimals </Text>
)}`}</Text> <Flex row gap="$spacing8">
<Flex row gap="$spacing4">
<TokenLogo <TokenLogo
chainId={chainId} chainId={chainId}
name={name} name={name}
...@@ -128,7 +148,9 @@ function UwULinkErc20SendModalContent({ ...@@ -128,7 +148,9 @@ function UwULinkErc20SendModalContent({
symbol={symbol} symbol={symbol}
url={logoUrl} url={logoUrl}
/> />
<Text>{symbol}</Text> <Text color="$neutral2">
{formatUnits(request.amount, decimals)} {symbol}
</Text>
</Flex> </Flex>
</Flex> </Flex>
<Flex alignSelf="stretch" borderTopColor="$surface3" borderTopWidth={1} pt="$spacing16"> <Flex alignSelf="stretch" borderTopColor="$surface3" borderTopWidth={1} pt="$spacing16">
......
...@@ -5,6 +5,7 @@ import React, { useMemo, useRef } from 'react' ...@@ -5,6 +5,7 @@ import React, { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useAppDispatch, useAppSelector } from 'src/app/hooks' import { useAppDispatch, useAppSelector } from 'src/app/hooks'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay' import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { KidSuperCheckinModal } from 'src/components/WalletConnect/RequestModal/KidSuperCheckinModal'
import { import {
WalletConnectRequestModalContent, WalletConnectRequestModalContent,
methodCostsGas, methodCostsGas,
...@@ -238,6 +239,18 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem ...@@ -238,6 +239,18 @@ export function WalletConnectRequestModal({ onClose, request }: Props): JSX.Elem
) )
} }
// KidSuper Uniswap Cafe check-in screen
if (request.type === EthMethod.PersonalSign && request.dapp.name === 'Uniswap Cafe') {
return (
<KidSuperCheckinModal
request={request}
onClose={handleClose}
onConfirm={onConfirmPress}
onReject={onReject}
/>
)
}
return ( return (
<ModalWithOverlay <ModalWithOverlay
confirmationButtonText={ confirmationButtonText={
......
...@@ -226,6 +226,7 @@ export function WalletConnectModal({ ...@@ -226,6 +226,7 @@ export function WalletConnectModal({
recipient: { recipient: {
address: parsedUwulinkRequest.recipient, address: parsedUwulinkRequest.recipient,
name: tokenRecipient?.name ?? '', name: tokenRecipient?.name ?? '',
logo: tokenRecipient?.logo,
}, },
amount: parsedUwulinkRequest.amount, amount: parsedUwulinkRequest.amount,
tokenAddress: parsedUwulinkRequest.tokenAddress, tokenAddress: parsedUwulinkRequest.tokenAddress,
......
...@@ -51,7 +51,10 @@ type UwULinkAllowlistItem = { ...@@ -51,7 +51,10 @@ type UwULinkAllowlistItem = {
chainId: number chainId: number
address: string address: string
name: string name: string
icon?: string logo?: {
dark?: string
light?: string
}
} }
type UwULinkAllowlist = { type UwULinkAllowlist = {
......
...@@ -55,6 +55,16 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams ...@@ -55,6 +55,16 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams
let signature = '' let signature = ''
if (method === EthMethod.PersonalSign || method === EthMethod.EthSign) { if (method === EthMethod.PersonalSign || method === EthMethod.EthSign) {
signature = yield* call(signMessage, params.message, account, signerManager) signature = yield* call(signMessage, params.message, account, signerManager)
// TODO: add `isCheckIn` type to uwulink request info so that this can be generalized
if (params.dapp.source === 'uwulink' && params.dapp.name === 'Uniswap Cafe') {
yield* put(
pushNotification({
type: AppNotificationType.Success,
title: 'Checked in',
})
)
}
} else if (method === EthMethod.SignTypedData || method === EthMethod.SignTypedDataV4) { } else if (method === EthMethod.SignTypedData || method === EthMethod.SignTypedDataV4) {
signature = yield* call(signTypedDataMessage, params.message, account, signerManager) signature = yield* call(signTypedDataMessage, params.message, account, signerManager)
} else if ( } else if (
......
...@@ -51,6 +51,10 @@ export interface UwuLinkErc20Request extends BaseRequest { ...@@ -51,6 +51,10 @@ export interface UwuLinkErc20Request extends BaseRequest {
recipient: { recipient: {
address: string address: string
name: string name: string
logo?: {
dark?: string
light?: string
}
} }
tokenAddress: string tokenAddress: string
amount: string amount: string
......
...@@ -292,7 +292,15 @@ describe('UniswapX v1', () => { ...@@ -292,7 +292,15 @@ describe('UniswapX v1', () => {
submitUniswapXOrder() submitUniswapXOrder()
cy.get(getTestSelector('confirmation-close-icon')).click() cy.get(getTestSelector('confirmation-close-icon')).click()
cy.interceptGraphqlOperation('PortfolioBalancesWeb', 'mini-portfolio/tokens.json') cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v1\/graphql/, (req) => {
if (req.body.operationName === 'PortfolioBalancesWeb') {
req.alias = 'PortfolioBalancesWeb'
// Reply with a fixture to speed up test
req.reply({ fixture: 'mini-portfolio/tokens.json' })
} else {
req.continue()
}
})
// Expect balances to refetch after filling // Expect balances to refetch after filling
cy.wait('@orderStatusOpen') cy.wait('@orderStatusOpen')
......
...@@ -287,7 +287,15 @@ describe('UniswapX v2', () => { ...@@ -287,7 +287,15 @@ describe('UniswapX v2', () => {
submitUniswapXOrder() submitUniswapXOrder()
cy.get(getTestSelector('confirmation-close-icon')).click() cy.get(getTestSelector('confirmation-close-icon')).click()
cy.interceptGraphqlOperation('PortfolioBalancesWeb', 'mini-portfolio/tokens.json') cy.intercept(/(?:interface|beta).gateway.uniswap.org\/v1\/graphql/, (req) => {
if (req.body.operationName === 'PortfolioBalancesWeb') {
req.alias = 'PortfolioBalancesWeb'
// Reply with a fixture to speed up test
req.reply({ fixture: 'mini-portfolio/tokens.json' })
} else {
req.continue()
}
})
// Expect balances to refetch after filling // Expect balances to refetch after filling
cy.wait('@orderStatusOpen') cy.wait('@orderStatusOpen')
......
...@@ -80,10 +80,9 @@ describe('Token details', () => { ...@@ -80,10 +80,9 @@ describe('Token details', () => {
cy.contains(shortenAddress('0x1eFBB78C8b917f67986BcE54cE575069c0143681')).should('exist') cy.contains(shortenAddress('0x1eFBB78C8b917f67986BcE54cE575069c0143681')).should('exist')
// Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e)) // Warning label should show if relevant ([spec](https://www.notion.so/3f7fce6f93694be08a94a6984d50298e))
cy.get('[data-cy="token-safety-message"]').contains(/Warning/) cy.get('[data-cy="token-safety-message"]')
cy.get('[data-cy="token-safety-description"]').contains( .should('include.text', 'Warning')
/This token isn’t traded on leading U.S. centralized exchanges or frequently swapped on Uniswap./ .and('include.text', "This token isn't traded on leading U.S. centralized exchanges")
)
}) })
describe('swapping', () => { describe('swapping', () => {
......
...@@ -645,14 +645,4 @@ ...@@ -645,14 +645,4 @@
<lastmod>2024-05-20T17:20:52.753Z</lastmod> <lastmod>2024-05-20T17:20:52.753Z</lastmod>
<priority>0.7</priority> <priority>0.7</priority>
</url> </url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x273f7f8e6489682df756151f5525576e322d51a3</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.7</priority>
</url>
<url>
<loc>https://app.uniswap.org/nfts/collection/0x77372a4cc66063575b05b44481f059be356964a4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.7</priority>
</url>
</urlset> </urlset>
\ No newline at end of file
...@@ -4355,219 +4355,4 @@ ...@@ -4355,219 +4355,4 @@
<lastmod>2024-05-20T17:20:52.753Z</lastmod> <lastmod>2024-05-20T17:20:52.753Z</lastmod>
<priority>0.8</priority> <priority>0.8</priority>
</url> </url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0x7baece5d47f1bc5e1953fbe0e9931d54dab6d810</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0x83abecf7204d5afc1bea5df734f085f2535a9976</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/ethereum/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0xb2eb5849e2606f99fc492e9add0103c667f806d3</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0x53c6ca2597711ca7a73b6921faf4031eedf71339</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/arbitrum/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/optimism/0xd35937ecd47b04a1474f8569f457fc5ac395921a</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/optimism/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/optimism/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/optimism/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0x6b75f2189f0e11c52e814e09e280eb1a9a8a094a</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0xb372b5abdb7c2ab8ad9e614be9835a42d0009153</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0xf369277650ad6654f25412ea8bfbd5942733babc</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/polygon/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x4898cf312fbff8814cab80a8d7f6ee5ad0dc73fb</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x5e78afc6c804d4382bede3a0712d210e657e9b4f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x86b211ca7915a0c8d4659dd98242d9e801d88ab4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0xb637f7c82fd774c280e23cebc725e7cd807c66d0</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0xd249c43faabc58d6dd4b0a4de598b5a956c5d8d7</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x1fbae785ce68b79f7ed4f7b27c3af3ef0e0bc3d4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x3c1376fb8487da57d4ffb263d9d01b578c7b586b</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x7b24bed19856f4bb1d4c0421cfb328026cd936bd</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x7cf887a863d81e6a483ee947dee05cb51914923c</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x588c8cf031809486f015908864ee8699b44017e4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x3987d38a4ff8520a8ef6bcc6f98d6da8bcd69b89</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/base/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/bnb/0xde67d05242b18af00b28678db34feec883cc9cd6</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/bnb/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/bnb/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/bnb/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0x4a5a8b0108f446df7c1c8a459fcfb54e844b7343</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0xf6ba006abf768ab2d1b5bba2d22d9f13eb1269d4</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0x4eefe02fce5b53ca33c7717bbd8ad3c9cb0609f1</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0xaf996125e98b5804c00ffdb4f7ff386307c99a00</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
<url>
<loc>https://app.uniswap.org/explore/pools/celo/0x7924a818013f39cf800f5589ff1f1f0def54f31f</loc>
<lastmod>2024-05-24T23:13:47.908Z</lastmod>
<priority>0.8</priority>
</url>
</urlset> </urlset>
\ No newline at end of file
This diff is collapsed.
...@@ -47,7 +47,7 @@ function useCancelLimitsDialogContent( ...@@ -47,7 +47,7 @@ function useCancelLimitsDialogContent(
<Plural <Plural
value={orders.length} value={orders.length}
one={t('common.limit.cancel')} one={t('common.limit.cancel')}
other={t(`common.limit.cancel.amount`, { count: orders.length })} other={t(`common.limit.cancel.amount`, { amount: orders.length })}
/> />
), ),
icon: <Slash />, icon: <Slash />,
...@@ -143,7 +143,7 @@ export function CancelLimitsDialog( ...@@ -143,7 +143,7 @@ export function CancelLimitsDialog(
} }
buttonsConfig={{ buttonsConfig={{
left: { left: {
title: <Trans i18nKey="common.neverMind" />, title: <Trans i18nKey="common.nevermind" />,
onClick: onCancel, onClick: onCancel,
textColor: 'neutral1', textColor: 'neutral1',
}, },
......
...@@ -2,7 +2,7 @@ import { useOpenLimitOrders } from 'components/AccountDrawer/MiniPortfolio/Activ ...@@ -2,7 +2,7 @@ import { useOpenLimitOrders } from 'components/AccountDrawer/MiniPortfolio/Activ
import Column from 'components/Column' import Column from 'components/Column'
import { TimeForwardIcon } from 'components/Icons/TimeForward' import { TimeForwardIcon } from 'components/Icons/TimeForward'
import Row from 'components/Row' import Row from 'components/Row'
import { Plural, Trans, t } from 'i18n' import { Plural, t, Trans } from 'i18n'
import { ChevronRight } from 'react-feather' import { ChevronRight } from 'react-feather'
import styled, { useTheme } from 'styled-components' import styled, { useTheme } from 'styled-components'
import { ClickableStyle, ThemedText } from 'theme/components' import { ClickableStyle, ThemedText } from 'theme/components'
......
...@@ -3,7 +3,7 @@ import Row from 'components/Row' ...@@ -3,7 +3,7 @@ import Row from 'components/Row'
import Tooltip, { TooltipSize } from 'components/Tooltip' import Tooltip, { TooltipSize } from 'components/Tooltip'
import { useScreenSize } from 'hooks/screenSize' import { useScreenSize } from 'hooks/screenSize'
import useCopyClipboard from 'hooks/useCopyClipboard' import useCopyClipboard from 'hooks/useCopyClipboard'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { useCallback, useState } from 'react' import { useCallback, useState } from 'react'
import { Copy } from 'react-feather' import { Copy } from 'react-feather'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
......
...@@ -8,7 +8,7 @@ import { ExternalLink } from 'theme/components' ...@@ -8,7 +8,7 @@ import { ExternalLink } from 'theme/components'
const StyledExternalLink = styled(ExternalLink)` const StyledExternalLink = styled(ExternalLink)`
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
......
...@@ -247,7 +247,7 @@ function CandlestickTooltip({ data }: { data: PriceChartData }) { ...@@ -247,7 +247,7 @@ function CandlestickTooltip({ data }: { data: PriceChartData }) {
<div>{formatFiatPrice({ price: data.low })}</div> <div>{formatFiatPrice({ price: data.low })}</div>
</RowBetween> </RowBetween>
<RowBetween gap="sm"> <RowBetween gap="sm">
<Trans i18nKey="common.close" /> <Trans i18nKey="chart.price.close" />
<div>{formatFiatPrice({ price: data.close })}</div> <div>{formatFiatPrice({ price: data.close })}</div>
</RowBetween> </RowBetween>
</TooltipText> </TooltipText>
......
...@@ -14,7 +14,7 @@ describe('ConfirmSwapModal/Error', () => { ...@@ -14,7 +14,7 @@ describe('ConfirmSwapModal/Error', () => {
['limit order', PendingModalError.CONFIRMATION_ERROR, LIMIT_ORDER_TRADE, 'Limit failed'], ['limit order', PendingModalError.CONFIRMATION_ERROR, LIMIT_ORDER_TRADE, 'Limit failed'],
['limit order', PendingModalError.WRAP_ERROR, LIMIT_ORDER_TRADE, 'Wrap failed'], ['limit order', PendingModalError.WRAP_ERROR, LIMIT_ORDER_TRADE, 'Wrap failed'],
])('renders %p correctly, with error= %p', async (testCaseName, errorType, trade, expectedError) => { ])('renders %p correctly, with error= %p', async (testCaseName, errorType, trade, expectedError) => {
const { asFragment } = render(<Error errorType={errorType} trade={trade} onRetry={jest.fn()} showTrade={true} />) const { asFragment } = render(<Error errorType={errorType} trade={trade} onRetry={jest.fn()} />)
expect(asFragment()).toMatchSnapshot() expect(asFragment()).toMatchSnapshot()
expect(screen.getByText(expectedError)).toBeInTheDocument() expect(screen.getByText(expectedError)).toBeInTheDocument()
}) })
......
...@@ -4,6 +4,7 @@ import { SwapResult } from 'hooks/useSwapCallback' ...@@ -4,6 +4,7 @@ import { SwapResult } from 'hooks/useSwapCallback'
import { Trans } from 'i18n' import { Trans } from 'i18n'
import { InterfaceTrade, TradeFillType } from 'state/routing/types' import { InterfaceTrade, TradeFillType } from 'state/routing/types'
import { isLimitTrade, isUniswapXTrade } from 'state/routing/utils' import { isLimitTrade, isUniswapXTrade } from 'state/routing/utils'
import { useTheme } from 'styled-components'
import { TradeSummary } from 'components/ConfirmSwapModal/TradeSummary' import { TradeSummary } from 'components/ConfirmSwapModal/TradeSummary'
import { DialogButtonType, DialogContent } from 'components/Dialog/Dialog' import { DialogButtonType, DialogContent } from 'components/Dialog/Dialog'
...@@ -23,7 +24,6 @@ export enum PendingModalError { ...@@ -23,7 +24,6 @@ export enum PendingModalError {
interface ErrorModalContentProps { interface ErrorModalContentProps {
errorType: PendingModalError errorType: PendingModalError
trade?: InterfaceTrade trade?: InterfaceTrade
showTrade?: boolean
swapResult?: SwapResult swapResult?: SwapResult
onRetry: () => void onRetry: () => void
} }
...@@ -49,7 +49,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t ...@@ -49,7 +49,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
case PendingModalError.XV2_HARD_QUOTE_ERROR: case PendingModalError.XV2_HARD_QUOTE_ERROR:
return { return {
title: <Trans i18nKey="common.swap.failed" />, title: <Trans i18nKey="common.swap.failed" />,
message: <Trans i18nKey="swap.fail.uniswapX" />, message: <Trans i18nKey="common.swap.failed.uniswapX" />,
supportArticleURL: SupportArticleURL.UNISWAP_X_FAILURE, supportArticleURL: SupportArticleURL.UNISWAP_X_FAILURE,
} }
case PendingModalError.CONFIRMATION_ERROR: case PendingModalError.CONFIRMATION_ERROR:
...@@ -61,7 +61,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t ...@@ -61,7 +61,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
} else { } else {
return { return {
title: <Trans i18nKey="common.swap.failed" />, title: <Trans i18nKey="common.swap.failed" />,
message: <Trans i18nKey="swap.fail.message" />, message: <Trans i18nKey="common.swap.failed.message" />,
supportArticleURL: isUniswapXTrade(trade) supportArticleURL: isUniswapXTrade(trade)
? SupportArticleURL.UNISWAP_X_FAILURE ? SupportArticleURL.UNISWAP_X_FAILURE
: SupportArticleURL.TRANSACTION_FAILURE, : SupportArticleURL.TRANSACTION_FAILURE,
...@@ -81,18 +81,19 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t ...@@ -81,18 +81,19 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
} }
} }
export default function Error({ errorType, trade, showTrade, swapResult, onRetry }: ErrorModalContentProps) { export default function Error({ errorType, trade, swapResult, onRetry }: ErrorModalContentProps) {
const theme = useTheme()
const { title, message, supportArticleURL } = getErrorContent({ errorType, trade }) const { title, message, supportArticleURL } = getErrorContent({ errorType, trade })
return ( return (
<DialogContent <DialogContent
isVisible={true} isVisible={true}
icon={<AlertTriangleFilled data-testid="pending-modal-failure-icon" size="24px" />} icon={<AlertTriangleFilled data-testid="pending-modal-failure-icon" fill={theme.neutral2} size="24px" />}
title={title} title={title}
description={message} description={message}
body={ body={
<ColumnCenter gap="sm"> <ColumnCenter gap="md">
{showTrade && trade && <TradeSummary trade={trade} />} {trade && <TradeSummary trade={trade} />}
{supportArticleURL && ( {supportArticleURL && (
<ExternalLink href={supportArticleURL}> <ExternalLink href={supportArticleURL}>
<Trans i18nKey="common.learnMore.link" /> <Trans i18nKey="common.learnMore.link" />
...@@ -114,7 +115,7 @@ export default function Error({ errorType, trade, showTrade, swapResult, onRetry ...@@ -114,7 +115,7 @@ export default function Error({ errorType, trade, showTrade, swapResult, onRetry
} }
buttonsConfig={{ buttonsConfig={{
left: { left: {
type: DialogButtonType.Primary, type: DialogButtonType.Accent,
title: <Trans i18nKey="common.tryAgain.error" />, title: <Trans i18nKey="common.tryAgain.error" />,
onClick: onRetry, onClick: onRetry,
}, },
......
...@@ -5,7 +5,7 @@ import Row from 'components/Row' ...@@ -5,7 +5,7 @@ import Row from 'components/Row'
import { SupportArticleURL } from 'constants/supportArticles' import { SupportArticleURL } from 'constants/supportArticles'
import { SwapResult } from 'hooks/useSwapCallback' import { SwapResult } from 'hooks/useSwapCallback'
import { useUnmountingAnimation } from 'hooks/useUnmountingAnimation' import { useUnmountingAnimation } from 'hooks/useUnmountingAnimation'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { ReactNode, useMemo, useRef } from 'react' import { ReactNode, useMemo, useRef } from 'react'
import { InterfaceTrade, TradeFillType } from 'state/routing/types' import { InterfaceTrade, TradeFillType } from 'state/routing/types'
import { isLimitTrade, isUniswapXTradeType } from 'state/routing/utils' import { isLimitTrade, isUniswapXTradeType } from 'state/routing/utils'
...@@ -26,8 +26,8 @@ import { ...@@ -26,8 +26,8 @@ import {
LoadingIndicatorOverlay, LoadingIndicatorOverlay,
LogoContainer, LogoContainer,
} from '../AccountDrawer/MiniPortfolio/Activity/Logos' } from '../AccountDrawer/MiniPortfolio/Activity/Logos'
import { TradeSummary } from './TradeSummary'
import { slideInAnimation, slideOutAnimation } from './animations' import { slideInAnimation, slideOutAnimation } from './animations'
import { TradeSummary } from './TradeSummary'
const Container = styled(ColumnCenter)` const Container = styled(ColumnCenter)`
margin: 48px 0 8px; margin: 48px 0 8px;
......
...@@ -130,7 +130,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a Limit order 1`] = ` ...@@ -130,7 +130,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a Limit order 1`] = `
width: -moz-fit-content; width: -moz-fit-content;
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
...@@ -380,7 +380,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a classic swap 1`] = ...@@ -380,7 +380,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a classic swap 1`] =
width: -moz-fit-content; width: -moz-fit-content;
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
......
...@@ -272,10 +272,8 @@ export function ConfirmSwapModal({ ...@@ -272,10 +272,8 @@ export function ConfirmSwapModal({
)} )}
{/* Error screen handles all error types with custom messaging and retry logic */} {/* Error screen handles all error types with custom messaging and retry logic */}
{errorType && showError && ( {errorType && showError && (
<Container $padding="16px">
<SwapError <SwapError
trade={trade} trade={trade}
showTrade={errorType !== PendingModalError.XV2_HARD_QUOTE_ERROR}
swapResult={swapResult} swapResult={swapResult}
errorType={errorType} errorType={errorType}
onRetry={() => { onRetry={() => {
...@@ -287,7 +285,6 @@ export function ConfirmSwapModal({ ...@@ -287,7 +285,6 @@ export function ConfirmSwapModal({
} }
}} }}
/> />
</Container>
)} )}
</SwapModal> </SwapModal>
</ThemeProvider> </ThemeProvider>
......
...@@ -97,15 +97,13 @@ export interface DialogProps { ...@@ -97,15 +97,13 @@ export interface DialogProps {
export function DialogContent({ icon, title, description, body, buttonsConfig }: DialogProps) { export function DialogContent({ icon, title, description, body, buttonsConfig }: DialogProps) {
const { left, right, gap } = buttonsConfig ?? {} const { left, right, gap } = buttonsConfig ?? {}
return ( return (
<ColumnCenter gap="lg"> <>
<ColumnCenter gap="16px"> <ColumnCenter gap="md">
<IconContainer>{icon}</IconContainer> <IconContainer>{icon}</IconContainer>
<ColumnCenter gap="sm">
<TitleText>{title}</TitleText> <TitleText>{title}</TitleText>
<DescriptionText>{description}</DescriptionText> <DescriptionText>{description}</DescriptionText>
{body} {body}
</ColumnCenter> </ColumnCenter>
</ColumnCenter>
<Row align="center" justify="center" gap={gap ?? 'md'}> <Row align="center" justify="center" gap={gap ?? 'md'}>
{left && ( {left && (
<StyledButton <StyledButton
...@@ -130,7 +128,7 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }: ...@@ -130,7 +128,7 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }:
</StyledButton> </StyledButton>
)} )}
</Row> </Row>
</ColumnCenter> </>
) )
} }
......
...@@ -5,11 +5,14 @@ import { StyledSVG } from './shared' ...@@ -5,11 +5,14 @@ import { StyledSVG } from './shared'
export default function AlertTriangleFilled({ size = '16px', ...rest }: { size?: string; [k: string]: any }) { export default function AlertTriangleFilled({ size = '16px', ...rest }: { size?: string; [k: string]: any }) {
const theme = useTheme() const theme = useTheme()
return ( return (
<StyledSVG fill={theme.neutral2} viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" size={size} {...rest}> <StyledSVG
<path viewBox="0 0 16 16"
d="m25.2086 20.0103-7.7081-14.41555c-1.4933-2.793-5.5055-2.793-7 0l-7.70806 14.41555c-1.36966 2.562.49005 5.6559 3.40088 5.6559h15.61438c2.9097 0 4.7706-3.095 3.4009-5.6559zm-12.0831-8.3441c0-.483.392-.875.875-.875s.875.392.875.875v4.6667c0 .483-.392.875-.875.875s-.875-.392-.875-.875zm.8984 9.3333c-.644 0-1.1727-.5226-1.1727-1.1666s.517-1.1667 1.161-1.1667h.0117c.6452 0 1.1667.5227 1.1667 1.1667s-.5227 1.1666-1.1667 1.1666z" fill={theme.deprecated_accentWarning}
fill="#9b9b9b" xmlns="http://www.w3.org/2000/svg"
/> size={size}
{...rest}
>
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2a1 1 0 0 1 0-2z" />
</StyledSVG> </StyledSVG>
) )
} }
...@@ -10,7 +10,7 @@ import useDebounce from 'hooks/useDebounce' ...@@ -10,7 +10,7 @@ import useDebounce from 'hooks/useDebounce'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes' import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsNftPage } from 'hooks/useIsNftPage' import { useIsNftPage } from 'hooks/useIsNftPage'
import { useOnClickOutside } from 'hooks/useOnClickOutside' import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useTranslation } from 'i18n/useTranslation' import { useTranslation } from 'i18n'
import { organizeSearchResults } from 'lib/utils/searchBar' import { organizeSearchResults } from 'lib/utils/searchBar'
import { Box } from 'nft/components/Box' import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex' import { Column, Row } from 'nft/components/Flex'
......
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { useOpenModal } from 'state/application/hooks' import { useOpenModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer' import { ApplicationModal } from 'state/application/reducer'
import styled from 'styled-components' import styled from 'styled-components'
......
...@@ -3,7 +3,7 @@ import SettingsTab from 'components/Settings' ...@@ -3,7 +3,7 @@ import SettingsTab from 'components/Settings'
import { Trans } from 'i18n' import { Trans } from 'i18n'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import { ArrowLeft } from 'react-feather' import { ArrowLeft } from 'react-feather'
import { Link, useLocation } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import { Box } from 'rebass' import { Box } from 'rebass'
import { useAppDispatch } from 'state/hooks' import { useAppDispatch } from 'state/hooks'
import { resetMintState } from 'state/mint/actions' import { resetMintState } from 'state/mint/actions'
...@@ -68,13 +68,11 @@ export function AddRemoveTabs({ ...@@ -68,13 +68,11 @@ export function AddRemoveTabs({
adding, adding,
creating, creating,
autoSlippage, autoSlippage,
positionID,
children, children,
}: { }: {
adding: boolean adding: boolean
creating: boolean creating: boolean
autoSlippage: Percent autoSlippage: Percent
positionID?: string
showBackLink?: boolean showBackLink?: boolean
children?: ReactNode children?: ReactNode
}) { }) {
...@@ -82,23 +80,17 @@ export function AddRemoveTabs({ ...@@ -82,23 +80,17 @@ export function AddRemoveTabs({
const theme = useTheme() const theme = useTheme()
// reset states on back // reset states on back
const dispatch = useAppDispatch() const dispatch = useAppDispatch()
const { state } = useLocation() const navigate = useNavigate()
const location = useLocation()
// detect if back should redirect to v3 or v2 pool page
const poolLink = location.pathname.includes('add/v2')
? '/pools/v2'
: '/pools' + (positionID ? `/${positionID.toString()}` : '')
// If the 'from' state is set by the previous page route back to the previous page, if not, route back to base pool
const target = state?.from ?? poolLink
return ( return (
<Tabs> <Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }} align="center"> <RowBetween style={{ padding: '1rem 1rem 0 1rem' }} align="center">
<StyledLink <StyledLink
to={target} to=".."
onClick={() => { onClick={(e) => {
e.preventDefault()
navigate(-1)
if (adding) { if (adding) {
// not 100% sure both of these are needed // not 100% sure both of these are needed
dispatch(resetMintState()) dispatch(resetMintState())
......
...@@ -60,18 +60,14 @@ interface InputProps extends Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'on ...@@ -60,18 +60,14 @@ interface InputProps extends Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'on
maxDecimals?: number maxDecimals?: number
} }
export function isInputGreaterThanDecimals(value: string, maxDecimals?: number): boolean {
const decimalGroups = value.split('.')
return !!maxDecimals && decimalGroups.length > 1 && decimalGroups[1].length > maxDecimals
}
const Input = forwardRef<HTMLInputElement, InputProps>( const Input = forwardRef<HTMLInputElement, InputProps>(
({ value, onUserInput, placeholder, prependSymbol, maxDecimals, ...rest }: InputProps, ref) => { ({ value, onUserInput, placeholder, prependSymbol, maxDecimals, ...rest }: InputProps, ref) => {
const { formatterLocale } = useFormatterLocales() const { formatterLocale } = useFormatterLocales()
const enforcer = (nextUserInput: string) => { const enforcer = (nextUserInput: string) => {
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) { if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
if (isInputGreaterThanDecimals(nextUserInput, maxDecimals)) { const decimalGroups = nextUserInput.split('.')
if (maxDecimals && decimalGroups.length > 1 && decimalGroups[1].length > maxDecimals) {
return return
} }
......
...@@ -54,18 +54,15 @@ const StyledChart: typeof Chart = styled(Chart)` ...@@ -54,18 +54,15 @@ const StyledChart: typeof Chart = styled(Chart)`
const PDPChartTypeSelector = ({ const PDPChartTypeSelector = ({
chartType, chartType,
onChartTypeChange, onChartTypeChange,
disabledOption,
}: { }: {
chartType: PoolsDetailsChartType chartType: PoolsDetailsChartType
onChartTypeChange: (c: PoolsDetailsChartType) => void onChartTypeChange: (c: PoolsDetailsChartType) => void
disabledOption?: PoolsDetailsChartType
}) => ( }) => (
<ChartTypeSelectorContainer> <ChartTypeSelectorContainer>
<ChartTypeDropdown <ChartTypeDropdown
options={PDP_CHART_SELECTOR_OPTIONS} options={PDP_CHART_SELECTOR_OPTIONS}
currentChartType={chartType} currentChartType={chartType}
onSelectOption={onChartTypeChange} onSelectOption={onChartTypeChange}
disabledOption={disabledOption}
/> />
</ChartTypeSelectorContainer> </ChartTypeSelectorContainer>
) )
...@@ -199,17 +196,11 @@ export default function ChartSection(props: ChartSectionProps) { ...@@ -199,17 +196,11 @@ export default function ChartSection(props: ChartSectionProps) {
return DEFAULT_PILL_TIME_SELECTOR_OPTIONS return DEFAULT_PILL_TIME_SELECTOR_OPTIONS
}, [activeQuery.chartType, setTimePeriod, timePeriod]) }, [activeQuery.chartType, setTimePeriod, timePeriod])
const disabledChartOption = props.poolData?.protocolVersion === ProtocolVersion.V2 ? ChartType.LIQUIDITY : undefined
return ( return (
<div data-testid="pdp-chart-container"> <div data-testid="pdp-chart-container">
{ChartBody} {ChartBody}
<ChartActionsContainer> <ChartActionsContainer>
<PDPChartTypeSelector <PDPChartTypeSelector chartType={activeQuery.chartType} onChartTypeChange={setChartType} />
chartType={activeQuery.chartType}
onChartTypeChange={setChartType}
disabledOption={disabledChartOption}
/>
{activeQuery.chartType !== ChartType.LIQUIDITY && ( {activeQuery.chartType !== ChartType.LIQUIDITY && (
<TimePeriodSelectorContainer> <TimePeriodSelectorContainer>
<PillMultiToggle <PillMultiToggle
......
...@@ -19,7 +19,7 @@ import { Trans } from 'i18n' ...@@ -19,7 +19,7 @@ import { Trans } from 'i18n'
import { Swap } from 'pages/Swap' import { Swap } from 'pages/Swap'
import { useMemo, useReducer } from 'react' import { useMemo, useReducer } from 'react'
import { Plus, X } from 'react-feather' import { Plus, X } from 'react-feather'
import { useLocation, useNavigate } from 'react-router-dom' import { useNavigate } from 'react-router-dom'
import styled from 'styled-components' import styled from 'styled-components'
import { BREAKPOINTS } from 'theme' import { BREAKPOINTS } from 'theme'
import { ClickableStyle, ThemedText } from 'theme/components' import { ClickableStyle, ThemedText } from 'theme/components'
...@@ -159,7 +159,6 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load ...@@ -159,7 +159,6 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load
const tokenId = position?.details.tokenId const tokenId = position?.details.tokenId
const switchChain = useSwitchChain() const switchChain = useSwitchChain()
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation()
const currency0 = token0 && gqlToCurrency(token0) const currency0 = token0 && gqlToCurrency(token0)
const currency1 = token1 && gqlToCurrency(token1) const currency1 = token1 && gqlToCurrency(token1)
...@@ -198,9 +197,7 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load ...@@ -198,9 +197,7 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load
if (walletChainId !== chainId && chainId) { if (walletChainId !== chainId && chainId) {
await switchChain(chainId) await switchChain(chainId)
} }
navigate(`/add/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}`, { navigate(`/add/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}`)
state: { from: location.pathname },
})
} }
} }
const [swapModalOpen, toggleSwapModalOpen] = useReducer((state) => !state, false) const [swapModalOpen, toggleSwapModalOpen] = useReducer((state) => !state, false)
......
import { Settings } from 'components/Icons/Settings' import { Settings } from 'components/Icons/Settings'
import Row from 'components/Row' import Row from 'components/Row'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { InterfaceTrade } from 'state/routing/types' import { InterfaceTrade } from 'state/routing/types'
import { isUniswapXTrade } from 'state/routing/utils' import { isUniswapXTrade } from 'state/routing/utils'
import { useUserSlippageTolerance } from 'state/user/hooks' import { useUserSlippageTolerance } from 'state/user/hooks'
......
import { useTranslation } from 'i18n/useTranslation' import { useTranslation } from 'i18n'
/** /**
* Displays the time as a human-readable string. * Displays the time as a human-readable string.
......
...@@ -58,15 +58,15 @@ export default function TokenSafetyMessage({ ...@@ -58,15 +58,15 @@ export default function TokenSafetyMessage({
const { heading, description } = getWarningCopy(warning, plural, tokenSymbol) const { heading, description } = getWarningCopy(warning, plural, tokenSymbol)
return ( return (
<Label color={textColor} backgroundColor={backgroundColor}> <Label data-cy="token-safety-message" color={textColor} backgroundColor={backgroundColor}>
{displayWarningLabel(warning) && ( {displayWarningLabel(warning) && (
<TitleRow data-cy="token-safety-message"> <TitleRow>
{warning.canProceed ? <AlertTriangle size="16px" /> : <Slash size="16px" />} {warning.canProceed ? <AlertTriangle size="16px" /> : <Slash size="16px" />}
<Title marginLeft="7px">{warning.message}</Title> <Title marginLeft="7px">{warning.message}</Title>
</TitleRow> </TitleRow>
)} )}
<DetailsRow data-cy="token-safety-description"> <DetailsRow>
{heading} {heading}
{Boolean(heading) && ' '} {Boolean(heading) && ' '}
{description} {description}
......
...@@ -214,7 +214,7 @@ export function TransactionsTable({ ...@@ -214,7 +214,7 @@ export function TransactionsTable({
header: () => ( header: () => (
<Cell minWidth={160} justifyContent="flex-end"> <Cell minWidth={160} justifyContent="flex-end">
<ThemedText.BodySecondary> <ThemedText.BodySecondary>
<Trans i18nKey="common.for" /> <Trans i18nKey="common.for.label" />
</ThemedText.BodySecondary> </ThemedText.BodySecondary>
</Cell> </Cell>
), ),
......
...@@ -122,7 +122,7 @@ export function useTokenTransactions( ...@@ -122,7 +122,7 @@ export function useTokenTransactions(
if (!tx) { if (!tx) {
return false return false
} }
const tokenBeingSold = parseFloat(tx.token0Quantity) > 0 ? tx.token0 : tx.token1 const tokenBeingSold = parseFloat(tx.token0Quantity) < 0 ? tx.token0 : tx.token1
const isSell = tokenBeingSold.address?.toLowerCase() === address.toLowerCase() const isSell = tokenBeingSold.address?.toLowerCase() === address.toLowerCase()
return ( return (
tx.type === PoolTransactionType.Swap && tx.type === PoolTransactionType.Swap &&
...@@ -133,7 +133,7 @@ export function useTokenTransactions( ...@@ -133,7 +133,7 @@ export function useTokenTransactions(
if (!tx) { if (!tx) {
return false return false
} }
const tokenBeingSold = parseFloat(tx.token0Quantity) > 0 ? tx.token0 : tx.token1 const tokenBeingSold = parseFloat(tx.token0Quantity) < 0 ? tx.token0 : tx.token1
const isSell = tokenBeingSold.address?.toLowerCase() === address.toLowerCase() const isSell = tokenBeingSold.address?.toLowerCase() === address.toLowerCase()
return ( return (
tx.type === PoolTransactionType.Swap && tx.type === PoolTransactionType.Swap &&
......
...@@ -3,13 +3,13 @@ import { useMemo } from 'react' ...@@ -3,13 +3,13 @@ import { useMemo } from 'react'
import store from 'state' import store from 'state'
import { useUserLocale } from 'state/user/hooks' import { useUserLocale } from 'state/user/hooks'
import useParsedQueryString from './useParsedQueryString' import useParsedQueryString, { parsedQueryString } from './useParsedQueryString'
/** /**
* Given a locale string (e.g. from user agent), return the best match for corresponding SupportedLocale * Given a locale string (e.g. from user agent), return the best match for corresponding SupportedLocale
* @param maybeSupportedLocale the fuzzy locale identifier * @param maybeSupportedLocale the fuzzy locale identifier
*/ */
export function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined { function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined {
if (typeof maybeSupportedLocale !== 'string') { if (typeof maybeSupportedLocale !== 'string') {
return undefined return undefined
} }
...@@ -36,10 +36,13 @@ export function navigatorLocale(): SupportedLocale | undefined { ...@@ -36,10 +36,13 @@ export function navigatorLocale(): SupportedLocale | undefined {
return parseLocale(language) return parseLocale(language)
} }
export function storeLocale(): SupportedLocale | undefined { function storeLocale(): SupportedLocale | undefined {
return store.getState().user.userLocale ?? undefined return store.getState().user.userLocale ?? undefined
} }
export const initialLocale =
parseLocale(parsedQueryString().lng) ?? storeLocale() ?? navigatorLocale() ?? DEFAULT_LOCALE
function useUrlLocale() { function useUrlLocale() {
const parsed = useParsedQueryString() const parsed = useParsedQueryString()
return parseLocale(parsed.lng) return parseLocale(parsed.lng)
......
import { useEffect } from 'react'
import enUsLocale from './i18n/locales/source/en-US.json' import enUsLocale from './i18n/locales/source/en-US.json'
import { initialLocale } from 'i18n/initialLocale' import { initialLocale, useActiveLocale } from 'hooks/useActiveLocale'
import { ReactNode } from 'react'
import { useUserLocaleManager } from 'state/user/hooks'
import i18n from 'i18next' import i18n, { t } from 'i18next'
import { initReactI18next } from 'react-i18next' import { Trans as OGTrans, Translation, initReactI18next, useTranslation as useTranslationOG } from 'react-i18next'
import { dynamicActivate } from 'i18n/dynamicActivate' import { SupportedLocale } from 'constants/locales'
import resourcesToBackend from 'i18next-resources-to-backend' import resourcesToBackend from 'i18next-resources-to-backend'
export { t } from 'i18next' export { t } from 'i18next'
export { Plural } from './i18n/Plural'
export { Trans } from './i18n/Trans' export const Trans = ((props) => {
// forces re-render on language change because it doesn't by default
useTranslation()
return <OGTrans {...props}>{props.children}</OGTrans>
}) satisfies typeof OGTrans
export function useTranslation() {
if (process.env.NODE_ENV === 'test') {
return { i18n, t }
}
// eslint-disable-next-line react-hooks/rules-of-hooks
return useTranslationOG()
}
export function Plural({ value, one, other }: { value: number; one: string; other: string }) {
const children = value === 1 ? one : other
if (process.env.NODE_ENV === 'test') {
return <>{children}</>
}
// ensures it re-renders when language changes
return <Translation>{() => children}</Translation>
}
i18n i18n
.use(initReactI18next) .use(initReactI18next)
.use( .use(
resourcesToBackend((language: string) => { resourcesToBackend((language: string, namespace: string) => {
// not sure why but it tries to load es THEN es-ES, for any language, but we just want the second // not sure why but it tries to load es THEN es-ES, for any language, but we just want the second
if (!language.includes('-')) { if (!language.includes('-')) {
return return
} }
if (language === 'en-US') { if (language === 'en-US') {
return enUsLocale if (process.env.NODE_ENV === 'test') {
return import('./i18n/locales/source/en-US.json')
} }
return import(`./i18n/locales/translations/${language}.json`) }
return import(`./i18n/locales/${namespace}/${language}.json`)
}) })
) )
.on('failedLoading', (language, namespace, msg) => { .on('failedLoading', (language, namespace, msg) => {
...@@ -32,7 +58,13 @@ i18n ...@@ -32,7 +58,13 @@ i18n
i18n i18n
.init({ .init({
resources: {
'en-US': {
translations: enUsLocale,
},
},
returnEmptyString: false, returnEmptyString: false,
defaultNS: 'translations',
keySeparator: false, keySeparator: false,
lng: 'en-US', lng: 'en-US',
fallbackLng: 'en-US', fallbackLng: 'en-US',
...@@ -42,11 +74,31 @@ i18n ...@@ -42,11 +74,31 @@ i18n
}) })
.catch(() => undefined) .catch(() => undefined)
// add default english ns right away let changingTo = ''
i18n.addResourceBundle('en-US', 'translations', {
'en-US': { async function dynamicActivate(locale: SupportedLocale) {
translation: enUsLocale, if (i18n.language === locale || locale === changingTo) {
}, return
}) }
// since its async we need to "lock" while its changing
changingTo = locale
await i18n.changeLanguage(locale)
i18n.emit('')
changingTo = ''
}
dynamicActivate(initialLocale) dynamicActivate(initialLocale)
export function LanguageProvider({ children }: { children: ReactNode }): JSX.Element {
const activeLocale = useActiveLocale()
const [userLocale, setUserLocale] = useUserLocaleManager()
const locale = userLocale || activeLocale
useEffect(() => {
dynamicActivate(locale)
document.documentElement.setAttribute('lang', locale)
setUserLocale(locale) // stores the selected locale to persist across sessions
}, [setUserLocale, locale])
return <>{children}</>
}
import { useActiveLocale } from 'hooks/useActiveLocale'
import { dynamicActivate } from 'i18n/dynamicActivate'
import { ReactNode, useEffect } from 'react'
import { useUserLocaleManager } from 'state/user/hooks'
export function LanguageProvider({ children }: { children: ReactNode }): JSX.Element {
const activeLocale = useActiveLocale()
const [userLocale, setUserLocale] = useUserLocaleManager()
const locale = userLocale || activeLocale
useEffect(() => {
dynamicActivate(locale)
document.documentElement.setAttribute('lang', locale)
setUserLocale(locale) // stores the selected locale to persist across sessions
}, [setUserLocale, locale])
return <>{children}</>
}
import { Translation } from 'react-i18next'
export function Plural({ value, one, other }: { value: number; one: string; other: string }) {
const children = value === 1 ? one : other
if (process.env.NODE_ENV === 'test') {
return <>{children}</>
}
// ensures it re-renders when language changes
return <Translation>{() => children}</Translation>
}
import { useTranslation } from 'i18n/useTranslation'
import { Trans as OGTrans } from 'react-i18next'
export const Trans = ((props) => {
// forces re-render on language change because it doesn't by default
useTranslation()
return <OGTrans {...props}>{props.children}</OGTrans>
}) satisfies typeof OGTrans
import { SupportedLocale } from 'constants/locales'
import i18n from 'i18next'
let changingTo = ''
export async function dynamicActivate(locale: SupportedLocale) {
if (i18n.language === locale || locale === changingTo) {
return
}
// since its async we need to "lock" while its changing
changingTo = locale
await i18n.changeLanguage(locale)
i18n.emit('')
changingTo = ''
}
import { DEFAULT_LOCALE } from 'constants/locales'
import { navigatorLocale, parseLocale, storeLocale } from '../hooks/useActiveLocale'
import { parsedQueryString } from '../hooks/useParsedQueryString'
export const initialLocale =
parseLocale(parsedQueryString().lng) ?? storeLocale() ?? navigatorLocale() ?? DEFAULT_LOCALE
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
"chart.error.tokens": "Unable to display historical data for the current token.", "chart.error.tokens": "Unable to display historical data for the current token.",
"chart.line": "Line chart", "chart.line": "Line chart",
"chart.missingData": "Missing chart data", "chart.missingData": "Missing chart data",
"chart.price.close": "Close",
"chart.price.high": "High", "chart.price.high": "High",
"chart.price.low": "Low", "chart.price.low": "Low",
"chart.price.open": "Open", "chart.price.open": "Open",
...@@ -124,7 +125,6 @@ ...@@ -124,7 +125,6 @@
"common.claimUni": "Claim UNI token", "common.claimUni": "Claim UNI token",
"common.claimUnis": "Claim your UNI tokens", "common.claimUnis": "Claim your UNI tokens",
"common.clearAll": "Clear all", "common.clearAll": "Clear all",
"common.close": "Close",
"common.closed": "Closed", "common.closed": "Closed",
"common.collect.button": "Collect", "common.collect.button": "Collect",
"common.collect.fees.cancelled": "Collect fees cancelled", "common.collect.fees.cancelled": "Collect fees cancelled",
...@@ -207,8 +207,8 @@ ...@@ -207,8 +207,8 @@
"common.extension": "Uniswap Extension", "common.extension": "Uniswap Extension",
"common.failed.error": "Failed", "common.failed.error": "Failed",
"common.failedSwitchNetwork": "Failed to switch networks", "common.failedSwitchNetwork": "Failed to switch networks",
"common.fee.caps": "Fee",
"common.fee": "fee", "common.fee": "fee",
"common.fee.caps": "Fee",
"common.fees": "Fees", "common.fees": "Fees",
"common.feesEarned.label": "{{symbol}} Fees Earned:", "common.feesEarned.label": "{{symbol}} Fees Earned:",
"common.feesEarnedPerBase": "{{symbolA}} per {{symbolB}}", "common.feesEarnedPerBase": "{{symbolA}} per {{symbolB}}",
...@@ -225,7 +225,6 @@ ...@@ -225,7 +225,6 @@
"common.governance": "Governance", "common.governance": "Governance",
"common.happyHolidays": "Happy Holidays from the Uniswap team!", "common.happyHolidays": "Happy Holidays from the Uniswap team!",
"common.helpCenter": "Help Center", "common.helpCenter": "Help Center",
"common.hidden": "Hidden",
"common.hide.button": "Hide", "common.hide.button": "Hide",
"common.highPrice": "High price", "common.highPrice": "High price",
"common.includes": "Includes", "common.includes": "Includes",
...@@ -283,7 +282,7 @@ ...@@ -283,7 +282,7 @@
"common.navigationButton": "Navigation button", "common.navigationButton": "Navigation button",
"common.needHelp": "Need help?", "common.needHelp": "Need help?",
"common.networkCost": "Network cost", "common.networkCost": "Network cost",
"common.neverMind": "Never mind", "common.nevermind": "Nevermind",
"common.nfts": "NFTs", "common.nfts": "NFTs",
"common.noActivity": "No activity yet", "common.noActivity": "No activity yet",
"common.noAmount.error": "Enter an amount", "common.noAmount.error": "Enter an amount",
...@@ -550,7 +549,7 @@ ...@@ -550,7 +549,7 @@
"explore.uniVolume": "Uniswap volume", "explore.uniVolume": "Uniswap volume",
"fee.bestForExotic": "Best for exotic pairs.", "fee.bestForExotic": "Best for exotic pairs.",
"fee.bestForMost": "Best for most pairs.", "fee.bestForMost": "Best for most pairs.",
"fee.bestForStable": "Best for stable pairs.", "fee.bestForStable.": "Best for stable pairs.",
"fee.bestForVeryStable": "Best for very stable pairs.", "fee.bestForVeryStable": "Best for very stable pairs.",
"fee.percentEarned": "The % you will earn in fees.", "fee.percentEarned": "The % you will earn in fees.",
"fee.selectPercent": "{{pct}}% select", "fee.selectPercent": "{{pct}}% select",
......
import i18n, { t } from 'i18next'
import { useTranslation as useTranslationOG } from 'react-i18next'
export function useTranslation() {
if (process.env.NODE_ENV === 'test') {
return { i18n, t }
}
// eslint-disable-next-line react-hooks/rules-of-hooks
return useTranslationOG()
}
...@@ -4,7 +4,6 @@ import '@reach/dialog/styles.css' ...@@ -4,7 +4,6 @@ import '@reach/dialog/styles.css'
import 'inter-ui' import 'inter-ui'
import 'polyfills' import 'polyfills'
import 'tracing' import 'tracing'
import './i18n' // ensure translations load before things
/* eslint-enable prettier/prettier */ /* eslint-enable prettier/prettier */
import { getDeviceId } from '@amplitude/analytics-browser' import { getDeviceId } from '@amplitude/analytics-browser'
...@@ -14,7 +13,6 @@ import { useWeb3React } from '@web3-react/core' ...@@ -14,7 +13,6 @@ import { useWeb3React } from '@web3-react/core'
import { AssetActivityProvider } from 'graphql/data/apollo/AssetActivityProvider' import { AssetActivityProvider } from 'graphql/data/apollo/AssetActivityProvider'
import { TokenBalancesProvider } from 'graphql/data/apollo/TokenBalancesProvider' import { TokenBalancesProvider } from 'graphql/data/apollo/TokenBalancesProvider'
import { apolloClient } from 'graphql/data/apollo/client' import { apolloClient } from 'graphql/data/apollo/client'
import { LanguageProvider } from 'i18n/LanguageProvider'
import { BlockNumberProvider } from 'lib/hooks/useBlockNumber' import { BlockNumberProvider } from 'lib/hooks/useBlockNumber'
import { MulticallUpdater } from 'lib/state/multicall' import { MulticallUpdater } from 'lib/state/multicall'
import { PropsWithChildren, StrictMode, useMemo } from 'react' import { PropsWithChildren, StrictMode, useMemo } from 'react'
...@@ -32,6 +30,7 @@ import { getEnvName, isBrowserRouterEnabled } from 'utils/env' ...@@ -32,6 +30,7 @@ import { getEnvName, isBrowserRouterEnabled } from 'utils/env'
import { unregister as unregisterServiceWorker } from 'utils/serviceWorker' import { unregister as unregisterServiceWorker } from 'utils/serviceWorker'
import { getCanonicalUrl } from 'utils/urlRoutes' import { getCanonicalUrl } from 'utils/urlRoutes'
import Web3Provider from './components/Web3Provider' import Web3Provider from './components/Web3Provider'
import { LanguageProvider } from './i18n'
import App from './pages/App' import App from './pages/App'
import store from './state' import store from './state'
import ApplicationUpdater from './state/application/updater' import ApplicationUpdater from './state/application/updater'
......
import { BaseButton } from 'components/Button' import { BaseButton } from 'components/Button'
import { Plural, Trans, t } from 'i18n' import { Plural, t, Trans } from 'i18n'
import { BelowFloorWarningModal } from 'nft/components/profile/list/Modal/BelowFloorWarningModal' import { BelowFloorWarningModal } from 'nft/components/profile/list/Modal/BelowFloorWarningModal'
import { useSellAsset } from 'nft/hooks' import { useSellAsset } from 'nft/hooks'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
......
import { ButtonPrimary } from 'components/Button' import { ButtonPrimary } from 'components/Button'
import Column from 'components/Column' import Column from 'components/Column'
import { Plural, Trans, t } from 'i18n' import { Plural, t, Trans } from 'i18n'
import { Portal } from 'nft/components/common/Portal' import { Portal } from 'nft/components/common/Portal'
import { Overlay } from 'nft/components/modals/Overlay' import { Overlay } from 'nft/components/modals/Overlay'
import { Listing, WalletAsset } from 'nft/types' import { Listing, WalletAsset } from 'nft/types'
......
...@@ -3,7 +3,7 @@ import { useWeb3React } from '@web3-react/core' ...@@ -3,7 +3,7 @@ import { useWeb3React } from '@web3-react/core'
import { useToggleAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks' import { useToggleAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks'
import { ButtonPrimary } from 'components/Button' import { ButtonPrimary } from 'components/Button'
import useENSName from 'hooks/useENSName' import useENSName from 'hooks/useENSName'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { XXXL_BAG_WIDTH } from 'nft/components/bag/Bag' import { XXXL_BAG_WIDTH } from 'nft/components/bag/Bag'
import { ListPage } from 'nft/components/profile/list/ListPage' import { ListPage } from 'nft/components/profile/list/ListPage'
import { ProfilePage } from 'nft/components/profile/view/ProfilePage' import { ProfilePage } from 'nft/components/profile/view/ProfilePage'
......
...@@ -677,7 +677,6 @@ function AddLiquidity() { ...@@ -677,7 +677,6 @@ function AddLiquidity() {
<AddRemoveTabs <AddRemoveTabs
creating={false} creating={false}
adding={true} adding={true}
positionID={tokenId}
autoSlippage={DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE} autoSlippage={DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE}
showBackLink={!hasExistingPosition} showBackLink={!hasExistingPosition}
> >
......
import Row from 'components/Row' import Row from 'components/Row'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { useMemo } from 'react' import { useMemo } from 'react'
import { ArrowRightCircle } from 'react-feather' import { ArrowRightCircle } from 'react-feather'
import styled from 'styled-components' import styled from 'styled-components'
......
import Row from 'components/Row' import Row from 'components/Row'
import { Trans, t } from 'i18n' import { t, Trans } from 'i18n'
import { useLimitContext } from 'state/limit/LimitContext' import { useLimitContext } from 'state/limit/LimitContext'
import styled from 'styled-components' import styled from 'styled-components'
import { ClickableStyle, ThemedText } from 'theme/components' import { ClickableStyle, ThemedText } from 'theme/components'
......
...@@ -5,7 +5,7 @@ import { ButtonLight } from 'components/Button' ...@@ -5,7 +5,7 @@ import { ButtonLight } from 'components/Button'
import Column from 'components/Column' import Column from 'components/Column'
import { ReverseArrow } from 'components/Icons/ReverseArrow' import { ReverseArrow } from 'components/Icons/ReverseArrow'
import { LoadingOpacityContainer } from 'components/Loader/styled' import { LoadingOpacityContainer } from 'components/Loader/styled'
import { Input as NumericalInput, isInputGreaterThanDecimals } from 'components/NumericalInput' import { Input as NumericalInput } from 'components/NumericalInput'
import Row, { RowBetween } from 'components/Row' import Row, { RowBetween } from 'components/Row'
import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal' import CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import { getChain, useSupportedChainId } from 'constants/chains' import { getChain, useSupportedChainId } from 'constants/chains'
...@@ -277,20 +277,13 @@ export default function SendCurrencyInputForm({ ...@@ -277,20 +277,13 @@ export default function SendCurrencyInputForm({
onCurrencyChange?.({ inputCurrency: currency, outputCurrency: undefined }) onCurrencyChange?.({ inputCurrency: currency, outputCurrency: undefined })
if (fiatCurrency.equals(currency)) { if (fiatCurrency.equals(currency)) {
setSendState((prev) => { setSendState((prev) => ({
let updatedExactAmountToken = exactAmountToken ?? exactAmountFiat
const maxDecimals = inputInFiat ? 6 : currency.decimals
if (isInputGreaterThanDecimals(updatedExactAmountToken, maxDecimals)) {
updatedExactAmountToken = parseFloat(updatedExactAmountToken).toFixed(maxDecimals)
}
return {
...prev, ...prev,
exactAmountToken: updatedExactAmountToken, exactAmountToken: exactAmountToken ?? exactAmountFiat,
exactAmountFiat: undefined, exactAmountFiat: undefined,
inputInFiat: false, inputInFiat: false,
inputCurrency: currency, inputCurrency: currency,
} }))
})
return return
} }
...@@ -299,7 +292,7 @@ export default function SendCurrencyInputForm({ ...@@ -299,7 +292,7 @@ export default function SendCurrencyInputForm({
inputCurrency: currency, inputCurrency: currency,
})) }))
}, },
[exactAmountFiat, exactAmountToken, fiatCurrency, inputInFiat, onCurrencyChange, setSendState] [exactAmountFiat, exactAmountToken, fiatCurrency, onCurrencyChange, setSendState]
) )
const toggleFiatInputAmountEnabled = useCallback(() => { const toggleFiatInputAmountEnabled = useCallback(() => {
......
...@@ -434,7 +434,7 @@ exports[`SendCurrencyInputform should render input in fiat correctly 1`] = ` ...@@ -434,7 +434,7 @@ exports[`SendCurrencyInputform should render input in fiat correctly 1`] = `
width: -moz-fit-content; width: -moz-fit-content;
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
...@@ -1164,7 +1164,7 @@ exports[`SendCurrencyInputform should render input in token amount correctly 1`] ...@@ -1164,7 +1164,7 @@ exports[`SendCurrencyInputform should render input in token amount correctly 1`]
width: -moz-fit-content; width: -moz-fit-content;
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
......
...@@ -52,7 +52,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -52,7 +52,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
gap: 4px; gap: 4px;
} }
.c21 { .c20 {
width: 100%; width: 100%;
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
...@@ -70,7 +70,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -70,7 +70,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
gap: 12px; gap: 12px;
} }
.c17 { .c16 {
color: #222222; color: #222222;
-webkit-letter-spacing: -0.01em; -webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em; -moz-letter-spacing: -0.01em;
...@@ -78,7 +78,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -78,7 +78,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
letter-spacing: -0.01em; letter-spacing: -0.01em;
} }
.c19 { .c18 {
color: #7D7D7D; color: #7D7D7D;
-webkit-letter-spacing: -0.01em; -webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em; -moz-letter-spacing: -0.01em;
...@@ -123,7 +123,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -123,7 +123,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
opacity: 0.4; opacity: 0.4;
} }
.c25 { .c24 {
background-color: transparent; background-color: transparent;
bottom: 0; bottom: 0;
border-radius: inherit; border-radius: inherit;
...@@ -137,7 +137,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -137,7 +137,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
width: 100%; width: 100%;
} }
.c22 { .c21 {
-webkit-align-items: center; -webkit-align-items: center;
-webkit-box-align: center; -webkit-box-align: center;
-ms-flex-align: center; -ms-flex-align: center;
...@@ -172,26 +172,26 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -172,26 +172,26 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
user-select: none; user-select: none;
} }
.c22:active .c24 { .c21:active .c23 {
background-color: #B8C0DC3d; background-color: #B8C0DC3d;
} }
.c22:focus .c24 { .c21:focus .c23 {
background-color: #B8C0DC3d; background-color: #B8C0DC3d;
} }
.c22:hover .c24 { .c21:hover .c23 {
background-color: #98A1C014; background-color: #98A1C014;
} }
.c22:disabled { .c21:disabled {
cursor: default; cursor: default;
opacity: 0.6; opacity: 0.6;
} }
.c22:disabled:active .c24, .c21:disabled:active .c23,
.c22:disabled:focus .c24, .c21:disabled:focus .c23,
.c22:disabled:hover .c24 { .c21:disabled:hover .c23 {
background-color: transparent; background-color: transparent;
} }
...@@ -200,7 +200,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -200,7 +200,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
width: -moz-fit-content; width: -moz-fit-content;
width: fit-content; width: fit-content;
border-radius: 16px; border-radius: 16px;
padding: 4px 8px; padding: 4px 6px;
font-size: 14px; font-size: 14px;
font-weight: 485; font-weight: 485;
line-height: 20px; line-height: 20px;
...@@ -246,22 +246,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -246,22 +246,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
-webkit-justify-content: flex-start; -webkit-justify-content: flex-start;
-ms-flex-pack: start; -ms-flex-pack: start;
justify-content: flex-start; justify-content: flex-start;
gap: 16px; gap: 12px;
}
.c16 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 8px;
} }
.c3 { .c3 {
...@@ -342,14 +327,14 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -342,14 +327,14 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
border-radius: 12px; border-radius: 12px;
} }
.c18 { .c17 {
font-size: 24px; font-size: 24px;
line-height: 32px; line-height: 32px;
text-align: center; text-align: center;
font-weight: 500; font-weight: 500;
} }
.c20 { .c19 {
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: 500;
line-height: 24px; line-height: 24px;
...@@ -360,7 +345,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -360,7 +345,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
text-align: center; text-align: center;
} }
.c23 { .c22 {
display: -webkit-box; display: -webkit-box;
display: -webkit-flex; display: -webkit-flex;
display: -ms-flexbox; display: -ms-flexbox;
...@@ -380,7 +365,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -380,7 +365,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
.c14 path { .c14 path {
background: #7D7D7D; background: #7D7D7D;
fill: #7D7D7D; fill: #EEB317;
} }
.c15 path { .c15 path {
...@@ -507,9 +492,6 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -507,9 +492,6 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
/> />
</svg> </svg>
</div> </div>
<div
class="c2 c3"
>
<div <div
class="c12 c3" class="c12 c3"
> >
...@@ -518,47 +500,42 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -518,47 +500,42 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
> >
<svg <svg
class="c14 c15" class="c14 c15"
fill="#7D7D7D" fill="#EEB317"
viewBox="0 0 28 28" viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="m25.2086 20.0103-7.7081-14.41555c-1.4933-2.793-5.5055-2.793-7 0l-7.70806 14.41555c-1.36966 2.562.49005 5.6559 3.40088 5.6559h15.61438c2.9097 0 4.7706-3.095 3.4009-5.6559zm-12.0831-8.3441c0-.483.392-.875.875-.875s.875.392.875.875v4.6667c0 .483-.392.875-.875.875s-.875-.392-.875-.875zm.8984 9.3333c-.644 0-1.1727-.5226-1.1727-1.1666s.517-1.1667 1.161-1.1667h.0117c.6452 0 1.1667.5227 1.1667 1.1667s-.5227 1.1666-1.1667 1.1666z" d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2a1 1 0 0 1 0-2z"
fill="#9b9b9b"
/> />
</svg> </svg>
</div> </div>
<div <div
class="c16 c3" class="c16 c17 css-4kpdx7"
>
<div
class="c17 c18 css-4kpdx7"
> >
Is this a wallet address? Is this a wallet address?
</div> </div>
<div <div
class="c19 c20 css-1urox24" class="c18 c19 css-1urox24"
> >
You’re about to send tokens to a special type of address - a smart contract. Double-check it’s the address you intended to send to. If it’s wrong, your tokens could be lost forever. You’re about to send tokens to a special type of address - a smart contract. Double-check it’s the address you intended to send to. If it’s wrong, your tokens could be lost forever.
</div> </div>
</div> </div>
</div>
<div <div
class="c9 c21" class="c9 c20"
> >
<button <button
class="c22 c23" class="c21 c22"
> >
<div <div
class="c24 c25" class="c23 c24"
/> />
Cancel Cancel
</button> </button>
<button <button
class="c22 c23" class="c21 c22"
> >
<div <div
class="c24 c25" class="c23 c24"
/> />
Continue Continue
</button> </button>
...@@ -568,7 +545,6 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = ` ...@@ -568,7 +545,6 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
</div> </div>
</div> </div>
</div> </div>
</div>
<div <div
data-focus-guard="true" data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;" style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
......
...@@ -207,7 +207,7 @@ export function useDerivedMintInfo( ...@@ -207,7 +207,7 @@ export function useDerivedMintInfo(
<Trans <Trans
i18nKey="common.insufficientTokenBalance.error" i18nKey="common.insufficientTokenBalance.error"
values={{ values={{
tokenSymbol: currencies[Field.CURRENCY_B]?.symbol, symbol: currencies[Field.CURRENCY_B]?.symbol,
}} }}
/> />
) )
......
...@@ -330,7 +330,7 @@ export function useInitialCurrencyState(): { ...@@ -330,7 +330,7 @@ export function useInitialCurrencyState(): {
const account = useAccount() const account = useAccount()
const supportedChainId = useSupportedChainId(parsedCurrencyState.chainId ?? account.chainId) ?? ChainId.MAINNET const supportedChainId = useSupportedChainId(parsedCurrencyState.chainId ?? account.chainId) ?? ChainId.MAINNET
const { data: balanceQuery } = useTokenBalancesQuery({ cacheOnly: !multichainUXEnabled }) const { data: balanceQuery } = useTokenBalancesQuery()
const balances = balanceQuery?.portfolios?.[0]?.tokenBalances const balances = balanceQuery?.portfolios?.[0]?.tokenBalances
const { initialInputCurrencyAddress, chainId } = useMemo(() => { const { initialInputCurrencyAddress, chainId } = useMemo(() => {
// Handle query params or disconnected state // Handle query params or disconnected state
......
...@@ -210,28 +210,16 @@ if (updatedGraphQLfile) { ...@@ -210,28 +210,16 @@ if (updatedGraphQLfile) {
} }
// Migrations + schema warnings // Migrations + schema warnings
const updatedMobileSchemaFile = danger.git.modified_files.find((file) => const updatedSchemaFile = danger.git.modified_files.find((file) =>
file.includes('mobile/src/app/schema.ts') file.includes('src/app/schema.ts')
) )
const updatedMobileMigrationsFile = danger.git.modified_files.find((file) => const updatedMigrationsFile = danger.git.modified_files.find((file) =>
file.includes('mobile/src/app/migrations.ts') file.includes('src/app/migrations.ts')
) )
const updatedMobileMigrationsTestFile = danger.git.modified_files.find((file) => const updatedMigrationsTestFile = danger.git.modified_files.find((file) =>
file.includes('mobile/src/app/migrations.test.ts') file.includes('src/app/migrations.test.ts')
)
const updatedExtensionSchemaFile = danger.git.modified_files.find((file) =>
file.includes('stretch/src/app/schema.ts')
)
const updatedExtensionMigrationsFile = danger.git.modified_files.find((file) =>
file.includes('stretch/src/store/migrations.ts')
)
const updatedExtensionMigrationsTestFile = danger.git.modified_files.find((file) =>
file.includes('stretch/src/store/migrations.test.ts')
) )
const createdSliceFile = danger.git.created_files.find((file) => const createdSliceFile = danger.git.created_files.find((file) =>
...@@ -246,50 +234,32 @@ const deletedSliceFile = danger.git.deleted_files.find((file) => ...@@ -246,50 +234,32 @@ const deletedSliceFile = danger.git.deleted_files.find((file) =>
file.toLowerCase().includes('slice') file.toLowerCase().includes('slice')
) )
if (modifiedSliceFile && ((!updatedMobileSchemaFile || !updatedMobileMigrationsFile) || (!updatedExtensionSchemaFile || !updatedExtensionMigrationsFile))) { if (modifiedSliceFile && (!updatedSchemaFile || !updatedMigrationsFile)) {
warn( warn(
'You modified a slice file. If you added, renamed, or deleted required properties from state, then make sure to define a new schema and a create a migration.' 'You modified a slice file. If you added, renamed, or deleted required properties from state, then make sure to define a new schema and a create a migration.'
) )
} }
if (updatedMobileSchemaFile && !updatedMobileMigrationsFile) { if (updatedSchemaFile && !updatedMigrationsFile) {
warn(
'You updated the mobile schema file but not the migrations file. Make sure to also define a migration.'
)
}
if (updatedExtensionSchemaFile && !updatedExtensionMigrationsFile) {
warn(
'You updated the extension schema file but not the migrations file. Make sure to also define a migration.'
)
}
if (!updatedMobileSchemaFile && updatedMobileMigrationsFile) {
warn( warn(
'You updated the mobile migrations file but not the schema. Schema always needs to be updated when a new migration is defined.' 'You updated the schema file but not the migrations file. Make sure to also define a migration.'
) )
} }
if (!updatedExtensionSchemaFile && updatedExtensionMigrationsFile) { if (!updatedSchemaFile && updatedMigrationsFile) {
warn( warn(
'You updated the extension migrations file but not the schema. Schema always needs to be updated when a new migration is defined.' 'You updated the migrations file but not the schema. Schema always needs to be updated when a new migration is defined.'
) )
} }
if ((createdSliceFile || deletedSliceFile) && (!updatedMobileSchemaFile || !updatedMobileMigrationsFile || !updatedExtensionSchemaFile || !updatedExtensionMigrationsFile)) { if ((createdSliceFile || deletedSliceFile) && (!updatedSchemaFile || !updatedMigrationsFile)) {
warn('You created or deleted a slice file. Make sure to update the schema and create migration if needed.') warn('You created or deleted a slice file. Make sure to create check schema and migration is updated if needed.')
} }
if ((updatedMobileMigrationsFile && !updatedMobileMigrationsTestFile) || (updatedExtensionMigrationsFile && !updatedExtensionMigrationsTestFile)) { if (updatedMigrationsFile && !updatedMigrationsTestFile) {
fail( fail(
'You updated the migrations file but did not write any new tests. Each migration must have a test!' 'You updated the migrations file but did not write any new tests. Each migration must have a test!'
) )
} }
if (updatedMobileMigrationsFile !== updatedExtensionMigrationsFile) {
warn(
'You updated the migrations file in one app but not the other. Make sure to update both migration files if needed.'
)
}
...@@ -89,6 +89,7 @@ ...@@ -89,6 +89,7 @@
"common.button.back": "Back", "common.button.back": "Back",
"common.button.buy": "Buy", "common.button.buy": "Buy",
"common.button.cancel": "Cancel", "common.button.cancel": "Cancel",
"common.button.checkin": "Check in",
"common.button.close": "Close", "common.button.close": "Close",
"common.button.confirm": "Confirm", "common.button.confirm": "Confirm",
"common.button.connect": "Connect", "common.button.connect": "Connect",
......
...@@ -157,8 +157,9 @@ export function SwapDetails({ ...@@ -157,8 +157,9 @@ export function SwapDetails({
<Flex row alignItems="center" gap="$spacing4"> <Flex row alignItems="center" gap="$spacing4">
<Text color="$neutral2" numberOfLines={3} variant="body3"> <Text color="$neutral2" numberOfLines={3} variant="body3">
{t('swap.details.slippage')} {t('swap.details.slippage')}
</Text> &nbsp;
<InfoCircleFilled color="$neutral3" size="$icon.16" /> <InfoCircleFilled color="$neutral3" size="$icon.16" />
</Text>
</Flex> </Flex>
</TouchableArea> </TouchableArea>
<Flex centered row gap="$spacing8"> <Flex centered row gap="$spacing8">
......
import { AddressStringFormat, normalizeAddress } from 'wallet/src/utils/addresses'
export function getAccountId(address: Address): string {
return normalizeAddress(address, AddressStringFormat.Lowercase)
}
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