ci(release): publish latest release

parent 168fd79b
* @uniswap/web-admins
IPFS hash of the deployment:
- 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)
Excited to share some new updates! Here’s what’s new:
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
\ No newline at end of file
mobile/1.28.2
\ No newline at end of file
......@@ -176,7 +176,18 @@ These are some tools you might want to familiarize yourself with to understand t
## 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
......
......@@ -136,12 +136,12 @@ android {
}
beta {
applicationIdSuffix ".beta"
versionName "1.28"
versionName "1.28.2"
dimension "variant"
}
prod {
dimension "variant"
versionName "1.28"
versionName "1.28.2"
}
}
......
......@@ -2536,7 +2536,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.widgets;
......@@ -2628,7 +2628,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.widgets;
......@@ -2713,7 +2713,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.WidgetIntentExtension;
......@@ -2799,7 +2799,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.WidgetIntentExtension;
......@@ -2873,7 +2873,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3103,7 +3103,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.OneSignalNotificationServiceExtension;
......@@ -3207,7 +3207,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
......@@ -3278,7 +3278,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 1.28;
MARKETING_VERSION = 1.28.2;
MTL_FAST_MATH = YES;
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.uniswap.mobile.beta.OneSignalNotificationServiceExtension;
......
......@@ -9,6 +9,17 @@ import { localizeMock as mockRNLocalize } from 'react-native-localize/mock'
import { AppearanceSettingType } from 'wallet/src/features/appearance/slice'
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
jest.mock('@sentry/react-native', () => ({
init: () => jest.fn(),
......
......@@ -104,7 +104,6 @@ import {
} from 'wallet/src/features/wallet/accounts/types'
import { initialWalletState, SwapProtectionSetting } from 'wallet/src/features/wallet/slice'
import { createMigrate } from 'wallet/src/state/createMigrate'
import { getAllKeysOfNestedObject } from 'wallet/src/state/testUtils'
import {
fiatPurchaseTransactionInfo,
signerMnemonicAccount,
......@@ -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', () => {
it('is able to perform all migrations starting from the initial schema', async () => {
const initialSchemaStub = {
......@@ -185,10 +204,6 @@ describe('Redux state migrations', () => {
},
}
if (!migratedSchema) {
throw new Error('Migrated schema is undefined')
}
const migratedSchemaKeys = new Set(getAllKeysOfNestedObject(migratedSchema))
const latestSchemaKeys = new Set(getAllKeysOfNestedObject(getSchema()))
const initialStateKeys = new Set(getAllKeysOfNestedObject(initialState))
......
......@@ -19,7 +19,6 @@ import {
} from 'wallet/src/features/transactions/types'
import { Account, AccountType } from 'wallet/src/features/wallet/accounts/types'
import { SwapProtectionSetting } from 'wallet/src/features/wallet/slice'
import { removeWalletIsUnlockedState } from 'wallet/src/state/sharedMigrations'
export const OLD_DEMO_ACCOUNT_ADDRESS = '0xdd0E380579dF30E38524F9477808d9eE37E2dEa6'
......@@ -877,7 +876,10 @@ export const migrations = {
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'
import * as Sentry from '@sentry/react'
import { MMKV } from 'react-native-mmkv'
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 { logger } from 'utilities/src/logger/logger'
import { fiatOnRampAggregatorApi, fiatOnRampApi } from 'wallet/src/features/fiatOnRamp/api'
......@@ -66,7 +66,7 @@ export const persistConfig = {
key: 'root',
storage: reduxStorage,
whitelist,
version: MOBILE_STATE_VERSION,
version: 63,
migrate: createMigrate(migrations),
}
......
......@@ -11,9 +11,11 @@ import {
NativeScrollEvent,
NativeSyntheticEvent,
ScrollView,
StyleProp,
View,
ViewStyle,
} 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 { Button, Flex, useDeviceInsets } from 'ui/src'
import { spacing } from 'ui/src/theme'
......@@ -30,6 +32,7 @@ type ModalWithOverlayProps = PropsWithChildren<
onReject: () => void
onConfirm: () => void
disableConfirm?: boolean
contentContainerStyle?: StyleProp<AnimatedStyle<StyleProp<ViewStyle>>>
}
>
......@@ -48,6 +51,7 @@ export function ModalWithOverlay({
onReject,
onConfirm,
disableConfirm,
contentContainerStyle,
...bottomSheetModalProps
}: ModalWithOverlayProps): JSX.Element {
const scrollViewRef = useRef<ScrollView>(null)
......@@ -114,10 +118,12 @@ export function ModalWithOverlay({
<BottomSheetModal overrideInnerContainer {...bottomSheetModalProps}>
<BottomSheetScrollView
ref={scrollViewRef}
contentContainerStyle={{
contentContainerStyle={
contentContainerStyle ?? {
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing36,
}}
}
}
showsVerticalScrollIndicator={false}
onLayout={handleScrollViewLayout}
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
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()
if (isSignTypedDataRequest(request)) {
......
......@@ -4,15 +4,18 @@ import { useTranslation } from 'react-i18next'
import Animated, { useAnimatedStyle } from 'react-native-reanimated'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { UwuLinkErc20Request } from 'src/features/walletConnect/walletConnectSlice'
import { Flex, Text } from 'ui/src'
import { iconSizes } from 'ui/src/theme'
import { Flex, Text, useIsDarkMode } from 'ui/src'
import { iconSizes, spacing } from 'ui/src/theme'
import { CurrencyInfo } from 'uniswap/src/features/dataApi/types'
import { ModalName } from 'uniswap/src/features/telemetry/constants'
import { NumberType } from 'utilities/src/format/types'
import { TokenLogo } from 'wallet/src/components/CurrencyLogo/TokenLogo'
import { SpinningLoader } from 'wallet/src/components/loading/SpinningLoader'
import { NetworkFee } from 'wallet/src/components/network/NetworkFee'
import { CHAIN_INFO } from 'wallet/src/constants/chains'
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 { NativeCurrency } from 'wallet/src/features/tokens/NativeCurrency'
import { useCurrencyInfo } from 'wallet/src/features/tokens/useCurrencyInfo'
......@@ -48,6 +51,10 @@ export function UwULinkErc20SendModal({
return (
<ModalWithOverlay
confirmationButtonText={t('common.button.pay')}
contentContainerStyle={{
paddingHorizontal: spacing.spacing24,
paddingTop: spacing.spacing8,
}}
disableConfirm={!hasSufficientTokenFunds || !hasSufficientGasFunds}
name={ModalName.UwULinkErc20SendModal}
scrollDownButtonText={t('walletConnect.request.button.scrollDown')}
......@@ -82,10 +89,12 @@ function UwULinkErc20SendModalContent({
currencyInfo: Maybe<CurrencyInfo>
}): JSX.Element {
const { t } = useTranslation()
const isDarkMode = useIsDarkMode()
const { animatedFooterHeight } = useBottomSheetInternal()
const bottomSpacerStyle = useAnimatedStyle(() => ({
height: animatedFooterHeight.value,
}))
const { convertFiatAmountFormatted } = useLocalizationContext()
const { chainId, isStablecoin } = request
const nativeCurrency = chainId && NativeCurrency.onChain(chainId)
......@@ -104,10 +113,22 @@ function UwULinkErc20SendModalContent({
currency: { name, symbol, decimals },
} = 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 (
<Flex centered gap="$spacing12" justifyContent="space-between">
{recipientLogoUrl ? (
<RemoteImage height={50} uri={recipientLogoUrl} width={200} />
) : (
<Text variant="subheading1">{request.recipient.name}</Text>
<Flex centered flex={1} gap="$spacing12" py="$spacing16">
)}
<Flex centered flex={1} gap="$spacing12" py="$spacing36">
{!hasSufficientTokenFunds && (
<Text color="red">
{t('uwulink.error.insufficientTokens', {
......@@ -116,11 +137,10 @@ function UwULinkErc20SendModalContent({
})}
</Text>
)}
<Text fontSize={64} my="$spacing4" pt={42}>{`${isStablecoin ? '$' : ''}${formatUnits(
request.amount,
decimals
)}`}</Text>
<Flex row gap="$spacing4">
<Text fontSize={64} my="$spacing4" pt={42}>
{formattedTokenAmount}
</Text>
<Flex row gap="$spacing8">
<TokenLogo
chainId={chainId}
name={name}
......@@ -128,7 +148,9 @@ function UwULinkErc20SendModalContent({
symbol={symbol}
url={logoUrl}
/>
<Text>{symbol}</Text>
<Text color="$neutral2">
{formatUnits(request.amount, decimals)} {symbol}
</Text>
</Flex>
</Flex>
<Flex alignSelf="stretch" borderTopColor="$surface3" borderTopWidth={1} pt="$spacing16">
......
......@@ -5,6 +5,7 @@ import React, { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { useAppDispatch, useAppSelector } from 'src/app/hooks'
import { ModalWithOverlay } from 'src/components/WalletConnect/ModalWithOverlay/ModalWithOverlay'
import { KidSuperCheckinModal } from 'src/components/WalletConnect/RequestModal/KidSuperCheckinModal'
import {
WalletConnectRequestModalContent,
methodCostsGas,
......@@ -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 (
<ModalWithOverlay
confirmationButtonText={
......
......@@ -226,6 +226,7 @@ export function WalletConnectModal({
recipient: {
address: parsedUwulinkRequest.recipient,
name: tokenRecipient?.name ?? '',
logo: tokenRecipient?.logo,
},
amount: parsedUwulinkRequest.amount,
tokenAddress: parsedUwulinkRequest.tokenAddress,
......
......@@ -51,7 +51,10 @@ type UwULinkAllowlistItem = {
chainId: number
address: string
name: string
icon?: string
logo?: {
dark?: string
light?: string
}
}
type UwULinkAllowlist = {
......
......@@ -55,6 +55,16 @@ export function* signWcRequest(params: SignMessageParams | SignTransactionParams
let signature = ''
if (method === EthMethod.PersonalSign || method === EthMethod.EthSign) {
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) {
signature = yield* call(signTypedDataMessage, params.message, account, signerManager)
} else if (
......
......@@ -51,6 +51,10 @@ export interface UwuLinkErc20Request extends BaseRequest {
recipient: {
address: string
name: string
logo?: {
dark?: string
light?: string
}
}
tokenAddress: string
amount: string
......
......@@ -292,7 +292,15 @@ describe('UniswapX v1', () => {
submitUniswapXOrder()
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
cy.wait('@orderStatusOpen')
......
......@@ -287,7 +287,15 @@ describe('UniswapX v2', () => {
submitUniswapXOrder()
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
cy.wait('@orderStatusOpen')
......
......@@ -80,10 +80,9 @@ describe('Token details', () => {
cy.contains(shortenAddress('0x1eFBB78C8b917f67986BcE54cE575069c0143681')).should('exist')
// 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-description"]').contains(
/This token isn’t traded on leading U.S. centralized exchanges or frequently swapped on Uniswap./
)
cy.get('[data-cy="token-safety-message"]')
.should('include.text', 'Warning')
.and('include.text', "This token isn't traded on leading U.S. centralized exchanges")
})
describe('swapping', () => {
......
......@@ -645,14 +645,4 @@
<lastmod>2024-05-20T17:20:52.753Z</lastmod>
<priority>0.7</priority>
</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>
\ No newline at end of file
......@@ -4355,219 +4355,4 @@
<lastmod>2024-05-20T17:20:52.753Z</lastmod>
<priority>0.8</priority>
</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>
\ No newline at end of file
This diff is collapsed.
......@@ -47,7 +47,7 @@ function useCancelLimitsDialogContent(
<Plural
value={orders.length}
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 />,
......@@ -143,7 +143,7 @@ export function CancelLimitsDialog(
}
buttonsConfig={{
left: {
title: <Trans i18nKey="common.neverMind" />,
title: <Trans i18nKey="common.nevermind" />,
onClick: onCancel,
textColor: 'neutral1',
},
......
......@@ -2,7 +2,7 @@ import { useOpenLimitOrders } from 'components/AccountDrawer/MiniPortfolio/Activ
import Column from 'components/Column'
import { TimeForwardIcon } from 'components/Icons/TimeForward'
import Row from 'components/Row'
import { Plural, Trans, t } from 'i18n'
import { Plural, t, Trans } from 'i18n'
import { ChevronRight } from 'react-feather'
import styled, { useTheme } from 'styled-components'
import { ClickableStyle, ThemedText } from 'theme/components'
......
......@@ -3,7 +3,7 @@ import Row from 'components/Row'
import Tooltip, { TooltipSize } from 'components/Tooltip'
import { useScreenSize } from 'hooks/screenSize'
import useCopyClipboard from 'hooks/useCopyClipboard'
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { useCallback, useState } from 'react'
import { Copy } from 'react-feather'
import { Link } from 'react-router-dom'
......
......@@ -8,7 +8,7 @@ import { ExternalLink } from 'theme/components'
const StyledExternalLink = styled(ExternalLink)`
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......
......@@ -247,7 +247,7 @@ function CandlestickTooltip({ data }: { data: PriceChartData }) {
<div>{formatFiatPrice({ price: data.low })}</div>
</RowBetween>
<RowBetween gap="sm">
<Trans i18nKey="common.close" />
<Trans i18nKey="chart.price.close" />
<div>{formatFiatPrice({ price: data.close })}</div>
</RowBetween>
</TooltipText>
......
......@@ -14,7 +14,7 @@ describe('ConfirmSwapModal/Error', () => {
['limit order', PendingModalError.CONFIRMATION_ERROR, LIMIT_ORDER_TRADE, 'Limit failed'],
['limit order', PendingModalError.WRAP_ERROR, LIMIT_ORDER_TRADE, 'Wrap failed'],
])('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(screen.getByText(expectedError)).toBeInTheDocument()
})
......
......@@ -4,6 +4,7 @@ import { SwapResult } from 'hooks/useSwapCallback'
import { Trans } from 'i18n'
import { InterfaceTrade, TradeFillType } from 'state/routing/types'
import { isLimitTrade, isUniswapXTrade } from 'state/routing/utils'
import { useTheme } from 'styled-components'
import { TradeSummary } from 'components/ConfirmSwapModal/TradeSummary'
import { DialogButtonType, DialogContent } from 'components/Dialog/Dialog'
......@@ -23,7 +24,6 @@ export enum PendingModalError {
interface ErrorModalContentProps {
errorType: PendingModalError
trade?: InterfaceTrade
showTrade?: boolean
swapResult?: SwapResult
onRetry: () => void
}
......@@ -49,7 +49,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
case PendingModalError.XV2_HARD_QUOTE_ERROR:
return {
title: <Trans i18nKey="common.swap.failed" />,
message: <Trans i18nKey="swap.fail.uniswapX" />,
message: <Trans i18nKey="common.swap.failed.uniswapX" />,
supportArticleURL: SupportArticleURL.UNISWAP_X_FAILURE,
}
case PendingModalError.CONFIRMATION_ERROR:
......@@ -61,7 +61,7 @@ function getErrorContent({ errorType, trade }: { errorType: PendingModalError; t
} else {
return {
title: <Trans i18nKey="common.swap.failed" />,
message: <Trans i18nKey="swap.fail.message" />,
message: <Trans i18nKey="common.swap.failed.message" />,
supportArticleURL: isUniswapXTrade(trade)
? SupportArticleURL.UNISWAP_X_FAILURE
: SupportArticleURL.TRANSACTION_FAILURE,
......@@ -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 })
return (
<DialogContent
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}
description={message}
body={
<ColumnCenter gap="sm">
{showTrade && trade && <TradeSummary trade={trade} />}
<ColumnCenter gap="md">
{trade && <TradeSummary trade={trade} />}
{supportArticleURL && (
<ExternalLink href={supportArticleURL}>
<Trans i18nKey="common.learnMore.link" />
......@@ -114,7 +115,7 @@ export default function Error({ errorType, trade, showTrade, swapResult, onRetry
}
buttonsConfig={{
left: {
type: DialogButtonType.Primary,
type: DialogButtonType.Accent,
title: <Trans i18nKey="common.tryAgain.error" />,
onClick: onRetry,
},
......
......@@ -5,7 +5,7 @@ import Row from 'components/Row'
import { SupportArticleURL } from 'constants/supportArticles'
import { SwapResult } from 'hooks/useSwapCallback'
import { useUnmountingAnimation } from 'hooks/useUnmountingAnimation'
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { ReactNode, useMemo, useRef } from 'react'
import { InterfaceTrade, TradeFillType } from 'state/routing/types'
import { isLimitTrade, isUniswapXTradeType } from 'state/routing/utils'
......@@ -26,8 +26,8 @@ import {
LoadingIndicatorOverlay,
LogoContainer,
} from '../AccountDrawer/MiniPortfolio/Activity/Logos'
import { TradeSummary } from './TradeSummary'
import { slideInAnimation, slideOutAnimation } from './animations'
import { TradeSummary } from './TradeSummary'
const Container = styled(ColumnCenter)`
margin: 48px 0 8px;
......
......@@ -130,7 +130,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a Limit order 1`] = `
width: -moz-fit-content;
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......@@ -380,7 +380,7 @@ exports[`ConfirmSwapModal/Head should render correctly for a classic swap 1`] =
width: -moz-fit-content;
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......
......@@ -272,10 +272,8 @@ export function ConfirmSwapModal({
)}
{/* Error screen handles all error types with custom messaging and retry logic */}
{errorType && showError && (
<Container $padding="16px">
<SwapError
trade={trade}
showTrade={errorType !== PendingModalError.XV2_HARD_QUOTE_ERROR}
swapResult={swapResult}
errorType={errorType}
onRetry={() => {
......@@ -287,7 +285,6 @@ export function ConfirmSwapModal({
}
}}
/>
</Container>
)}
</SwapModal>
</ThemeProvider>
......
......@@ -97,15 +97,13 @@ export interface DialogProps {
export function DialogContent({ icon, title, description, body, buttonsConfig }: DialogProps) {
const { left, right, gap } = buttonsConfig ?? {}
return (
<ColumnCenter gap="lg">
<ColumnCenter gap="16px">
<>
<ColumnCenter gap="md">
<IconContainer>{icon}</IconContainer>
<ColumnCenter gap="sm">
<TitleText>{title}</TitleText>
<DescriptionText>{description}</DescriptionText>
{body}
</ColumnCenter>
</ColumnCenter>
<Row align="center" justify="center" gap={gap ?? 'md'}>
{left && (
<StyledButton
......@@ -130,7 +128,7 @@ export function DialogContent({ icon, title, description, body, buttonsConfig }:
</StyledButton>
)}
</Row>
</ColumnCenter>
</>
)
}
......
......@@ -5,11 +5,14 @@ import { StyledSVG } from './shared'
export default function AlertTriangleFilled({ size = '16px', ...rest }: { size?: string; [k: string]: any }) {
const theme = useTheme()
return (
<StyledSVG fill={theme.neutral2} viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" size={size} {...rest}>
<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"
fill="#9b9b9b"
/>
<StyledSVG
viewBox="0 0 16 16"
fill={theme.deprecated_accentWarning}
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>
)
}
......@@ -10,7 +10,7 @@ import useDebounce from 'hooks/useDebounce'
import { useDisableNFTRoutes } from 'hooks/useDisableNFTRoutes'
import { useIsNftPage } from 'hooks/useIsNftPage'
import { useOnClickOutside } from 'hooks/useOnClickOutside'
import { useTranslation } from 'i18n/useTranslation'
import { useTranslation } from 'i18n'
import { organizeSearchResults } from 'lib/utils/searchBar'
import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex'
......
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { useOpenModal } from 'state/application/hooks'
import { ApplicationModal } from 'state/application/reducer'
import styled from 'styled-components'
......
......@@ -3,7 +3,7 @@ import SettingsTab from 'components/Settings'
import { Trans } from 'i18n'
import { ReactNode } from 'react'
import { ArrowLeft } from 'react-feather'
import { Link, useLocation } from 'react-router-dom'
import { Link, useNavigate } from 'react-router-dom'
import { Box } from 'rebass'
import { useAppDispatch } from 'state/hooks'
import { resetMintState } from 'state/mint/actions'
......@@ -68,13 +68,11 @@ export function AddRemoveTabs({
adding,
creating,
autoSlippage,
positionID,
children,
}: {
adding: boolean
creating: boolean
autoSlippage: Percent
positionID?: string
showBackLink?: boolean
children?: ReactNode
}) {
......@@ -82,23 +80,17 @@ export function AddRemoveTabs({
const theme = useTheme()
// reset states on back
const dispatch = useAppDispatch()
const { state } = useLocation()
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
const navigate = useNavigate()
return (
<Tabs>
<RowBetween style={{ padding: '1rem 1rem 0 1rem' }} align="center">
<StyledLink
to={target}
onClick={() => {
to=".."
onClick={(e) => {
e.preventDefault()
navigate(-1)
if (adding) {
// not 100% sure both of these are needed
dispatch(resetMintState())
......
......@@ -60,18 +60,14 @@ interface InputProps extends Omit<React.HTMLProps<HTMLInputElement>, 'ref' | 'on
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>(
({ value, onUserInput, placeholder, prependSymbol, maxDecimals, ...rest }: InputProps, ref) => {
const { formatterLocale } = useFormatterLocales()
const enforcer = (nextUserInput: string) => {
if (nextUserInput === '' || inputRegex.test(escapeRegExp(nextUserInput))) {
if (isInputGreaterThanDecimals(nextUserInput, maxDecimals)) {
const decimalGroups = nextUserInput.split('.')
if (maxDecimals && decimalGroups.length > 1 && decimalGroups[1].length > maxDecimals) {
return
}
......
......@@ -54,18 +54,15 @@ const StyledChart: typeof Chart = styled(Chart)`
const PDPChartTypeSelector = ({
chartType,
onChartTypeChange,
disabledOption,
}: {
chartType: PoolsDetailsChartType
onChartTypeChange: (c: PoolsDetailsChartType) => void
disabledOption?: PoolsDetailsChartType
}) => (
<ChartTypeSelectorContainer>
<ChartTypeDropdown
options={PDP_CHART_SELECTOR_OPTIONS}
currentChartType={chartType}
onSelectOption={onChartTypeChange}
disabledOption={disabledOption}
/>
</ChartTypeSelectorContainer>
)
......@@ -199,17 +196,11 @@ export default function ChartSection(props: ChartSectionProps) {
return DEFAULT_PILL_TIME_SELECTOR_OPTIONS
}, [activeQuery.chartType, setTimePeriod, timePeriod])
const disabledChartOption = props.poolData?.protocolVersion === ProtocolVersion.V2 ? ChartType.LIQUIDITY : undefined
return (
<div data-testid="pdp-chart-container">
{ChartBody}
<ChartActionsContainer>
<PDPChartTypeSelector
chartType={activeQuery.chartType}
onChartTypeChange={setChartType}
disabledOption={disabledChartOption}
/>
<PDPChartTypeSelector chartType={activeQuery.chartType} onChartTypeChange={setChartType} />
{activeQuery.chartType !== ChartType.LIQUIDITY && (
<TimePeriodSelectorContainer>
<PillMultiToggle
......
......@@ -19,7 +19,7 @@ import { Trans } from 'i18n'
import { Swap } from 'pages/Swap'
import { useMemo, useReducer } from 'react'
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 { BREAKPOINTS } from 'theme'
import { ClickableStyle, ThemedText } from 'theme/components'
......@@ -159,7 +159,6 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load
const tokenId = position?.details.tokenId
const switchChain = useSwitchChain()
const navigate = useNavigate()
const location = useLocation()
const currency0 = token0 && gqlToCurrency(token0)
const currency1 = token1 && gqlToCurrency(token1)
......@@ -198,9 +197,7 @@ export function PoolDetailsStatsButtons({ chainId, token0, token1, feeTier, load
if (walletChainId !== chainId && chainId) {
await switchChain(chainId)
}
navigate(`/add/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}`, {
state: { from: location.pathname },
})
navigate(`/add/${currencyId(currency0)}/${currencyId(currency1)}/${feeTier}${tokenId ? `/${tokenId}` : ''}`)
}
}
const [swapModalOpen, toggleSwapModalOpen] = useReducer((state) => !state, false)
......
import { Settings } from 'components/Icons/Settings'
import Row from 'components/Row'
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { InterfaceTrade } from 'state/routing/types'
import { isUniswapXTrade } from 'state/routing/utils'
import { useUserSlippageTolerance } from 'state/user/hooks'
......
import { useTranslation } from 'i18n/useTranslation'
import { useTranslation } from 'i18n'
/**
* Displays the time as a human-readable string.
......
......@@ -58,15 +58,15 @@ export default function TokenSafetyMessage({
const { heading, description } = getWarningCopy(warning, plural, tokenSymbol)
return (
<Label color={textColor} backgroundColor={backgroundColor}>
<Label data-cy="token-safety-message" color={textColor} backgroundColor={backgroundColor}>
{displayWarningLabel(warning) && (
<TitleRow data-cy="token-safety-message">
<TitleRow>
{warning.canProceed ? <AlertTriangle size="16px" /> : <Slash size="16px" />}
<Title marginLeft="7px">{warning.message}</Title>
</TitleRow>
)}
<DetailsRow data-cy="token-safety-description">
<DetailsRow>
{heading}
{Boolean(heading) && ' '}
{description}
......
......@@ -214,7 +214,7 @@ export function TransactionsTable({
header: () => (
<Cell minWidth={160} justifyContent="flex-end">
<ThemedText.BodySecondary>
<Trans i18nKey="common.for" />
<Trans i18nKey="common.for.label" />
</ThemedText.BodySecondary>
</Cell>
),
......
......@@ -122,7 +122,7 @@ export function useTokenTransactions(
if (!tx) {
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()
return (
tx.type === PoolTransactionType.Swap &&
......@@ -133,7 +133,7 @@ export function useTokenTransactions(
if (!tx) {
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()
return (
tx.type === PoolTransactionType.Swap &&
......
......@@ -3,13 +3,13 @@ import { useMemo } from 'react'
import store from 'state'
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
* @param maybeSupportedLocale the fuzzy locale identifier
*/
export function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined {
function parseLocale(maybeSupportedLocale: unknown): SupportedLocale | undefined {
if (typeof maybeSupportedLocale !== 'string') {
return undefined
}
......@@ -36,10 +36,13 @@ export function navigatorLocale(): SupportedLocale | undefined {
return parseLocale(language)
}
export function storeLocale(): SupportedLocale | undefined {
function storeLocale(): SupportedLocale | undefined {
return store.getState().user.userLocale ?? undefined
}
export const initialLocale =
parseLocale(parsedQueryString().lng) ?? storeLocale() ?? navigatorLocale() ?? DEFAULT_LOCALE
function useUrlLocale() {
const parsed = useParsedQueryString()
return parseLocale(parsed.lng)
......
import { useEffect } from 'react'
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 { initReactI18next } from 'react-i18next'
import i18n, { t } from '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'
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
.use(initReactI18next)
.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
if (!language.includes('-')) {
return
}
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) => {
......@@ -32,7 +58,13 @@ i18n
i18n
.init({
resources: {
'en-US': {
translations: enUsLocale,
},
},
returnEmptyString: false,
defaultNS: 'translations',
keySeparator: false,
lng: 'en-US',
fallbackLng: 'en-US',
......@@ -42,11 +74,31 @@ i18n
})
.catch(() => undefined)
// add default english ns right away
i18n.addResourceBundle('en-US', 'translations', {
'en-US': {
translation: enUsLocale,
},
})
let changingTo = ''
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 = ''
}
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 @@
"chart.error.tokens": "Unable to display historical data for the current token.",
"chart.line": "Line chart",
"chart.missingData": "Missing chart data",
"chart.price.close": "Close",
"chart.price.high": "High",
"chart.price.low": "Low",
"chart.price.open": "Open",
......@@ -124,7 +125,6 @@
"common.claimUni": "Claim UNI token",
"common.claimUnis": "Claim your UNI tokens",
"common.clearAll": "Clear all",
"common.close": "Close",
"common.closed": "Closed",
"common.collect.button": "Collect",
"common.collect.fees.cancelled": "Collect fees cancelled",
......@@ -207,8 +207,8 @@
"common.extension": "Uniswap Extension",
"common.failed.error": "Failed",
"common.failedSwitchNetwork": "Failed to switch networks",
"common.fee.caps": "Fee",
"common.fee": "fee",
"common.fee.caps": "Fee",
"common.fees": "Fees",
"common.feesEarned.label": "{{symbol}} Fees Earned:",
"common.feesEarnedPerBase": "{{symbolA}} per {{symbolB}}",
......@@ -225,7 +225,6 @@
"common.governance": "Governance",
"common.happyHolidays": "Happy Holidays from the Uniswap team!",
"common.helpCenter": "Help Center",
"common.hidden": "Hidden",
"common.hide.button": "Hide",
"common.highPrice": "High price",
"common.includes": "Includes",
......@@ -283,7 +282,7 @@
"common.navigationButton": "Navigation button",
"common.needHelp": "Need help?",
"common.networkCost": "Network cost",
"common.neverMind": "Never mind",
"common.nevermind": "Nevermind",
"common.nfts": "NFTs",
"common.noActivity": "No activity yet",
"common.noAmount.error": "Enter an amount",
......@@ -550,7 +549,7 @@
"explore.uniVolume": "Uniswap volume",
"fee.bestForExotic": "Best for exotic 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.percentEarned": "The % you will earn in fees.",
"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'
import 'inter-ui'
import 'polyfills'
import 'tracing'
import './i18n' // ensure translations load before things
/* eslint-enable prettier/prettier */
import { getDeviceId } from '@amplitude/analytics-browser'
......@@ -14,7 +13,6 @@ import { useWeb3React } from '@web3-react/core'
import { AssetActivityProvider } from 'graphql/data/apollo/AssetActivityProvider'
import { TokenBalancesProvider } from 'graphql/data/apollo/TokenBalancesProvider'
import { apolloClient } from 'graphql/data/apollo/client'
import { LanguageProvider } from 'i18n/LanguageProvider'
import { BlockNumberProvider } from 'lib/hooks/useBlockNumber'
import { MulticallUpdater } from 'lib/state/multicall'
import { PropsWithChildren, StrictMode, useMemo } from 'react'
......@@ -32,6 +30,7 @@ import { getEnvName, isBrowserRouterEnabled } from 'utils/env'
import { unregister as unregisterServiceWorker } from 'utils/serviceWorker'
import { getCanonicalUrl } from 'utils/urlRoutes'
import Web3Provider from './components/Web3Provider'
import { LanguageProvider } from './i18n'
import App from './pages/App'
import store from './state'
import ApplicationUpdater from './state/application/updater'
......
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 { useSellAsset } from 'nft/hooks'
import { useMemo, useState } from 'react'
......
import { ButtonPrimary } from 'components/Button'
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 { Overlay } from 'nft/components/modals/Overlay'
import { Listing, WalletAsset } from 'nft/types'
......
......@@ -3,7 +3,7 @@ import { useWeb3React } from '@web3-react/core'
import { useToggleAccountDrawer } from 'components/AccountDrawer/MiniPortfolio/hooks'
import { ButtonPrimary } from 'components/Button'
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 { ListPage } from 'nft/components/profile/list/ListPage'
import { ProfilePage } from 'nft/components/profile/view/ProfilePage'
......
......@@ -677,7 +677,6 @@ function AddLiquidity() {
<AddRemoveTabs
creating={false}
adding={true}
positionID={tokenId}
autoSlippage={DEFAULT_ADD_IN_RANGE_SLIPPAGE_TOLERANCE}
showBackLink={!hasExistingPosition}
>
......
import Row from 'components/Row'
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { useMemo } from 'react'
import { ArrowRightCircle } from 'react-feather'
import styled from 'styled-components'
......
import Row from 'components/Row'
import { Trans, t } from 'i18n'
import { t, Trans } from 'i18n'
import { useLimitContext } from 'state/limit/LimitContext'
import styled from 'styled-components'
import { ClickableStyle, ThemedText } from 'theme/components'
......
......@@ -5,7 +5,7 @@ import { ButtonLight } from 'components/Button'
import Column from 'components/Column'
import { ReverseArrow } from 'components/Icons/ReverseArrow'
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 CurrencySearchModal from 'components/SearchModal/CurrencySearchModal'
import { getChain, useSupportedChainId } from 'constants/chains'
......@@ -277,20 +277,13 @@ export default function SendCurrencyInputForm({
onCurrencyChange?.({ inputCurrency: currency, outputCurrency: undefined })
if (fiatCurrency.equals(currency)) {
setSendState((prev) => {
let updatedExactAmountToken = exactAmountToken ?? exactAmountFiat
const maxDecimals = inputInFiat ? 6 : currency.decimals
if (isInputGreaterThanDecimals(updatedExactAmountToken, maxDecimals)) {
updatedExactAmountToken = parseFloat(updatedExactAmountToken).toFixed(maxDecimals)
}
return {
setSendState((prev) => ({
...prev,
exactAmountToken: updatedExactAmountToken,
exactAmountToken: exactAmountToken ?? exactAmountFiat,
exactAmountFiat: undefined,
inputInFiat: false,
inputCurrency: currency,
}
})
}))
return
}
......@@ -299,7 +292,7 @@ export default function SendCurrencyInputForm({
inputCurrency: currency,
}))
},
[exactAmountFiat, exactAmountToken, fiatCurrency, inputInFiat, onCurrencyChange, setSendState]
[exactAmountFiat, exactAmountToken, fiatCurrency, onCurrencyChange, setSendState]
)
const toggleFiatInputAmountEnabled = useCallback(() => {
......
......@@ -434,7 +434,7 @@ exports[`SendCurrencyInputform should render input in fiat correctly 1`] = `
width: -moz-fit-content;
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......@@ -1164,7 +1164,7 @@ exports[`SendCurrencyInputform should render input in token amount correctly 1`]
width: -moz-fit-content;
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......
......@@ -52,7 +52,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
gap: 4px;
}
.c21 {
.c20 {
width: 100%;
display: -webkit-box;
display: -webkit-flex;
......@@ -70,7 +70,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
gap: 12px;
}
.c17 {
.c16 {
color: #222222;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
......@@ -78,7 +78,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
letter-spacing: -0.01em;
}
.c19 {
.c18 {
color: #7D7D7D;
-webkit-letter-spacing: -0.01em;
-moz-letter-spacing: -0.01em;
......@@ -123,7 +123,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
opacity: 0.4;
}
.c25 {
.c24 {
background-color: transparent;
bottom: 0;
border-radius: inherit;
......@@ -137,7 +137,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
width: 100%;
}
.c22 {
.c21 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
......@@ -172,26 +172,26 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
user-select: none;
}
.c22:active .c24 {
.c21:active .c23 {
background-color: #B8C0DC3d;
}
.c22:focus .c24 {
.c21:focus .c23 {
background-color: #B8C0DC3d;
}
.c22:hover .c24 {
.c21:hover .c23 {
background-color: #98A1C014;
}
.c22:disabled {
.c21:disabled {
cursor: default;
opacity: 0.6;
}
.c22:disabled:active .c24,
.c22:disabled:focus .c24,
.c22:disabled:hover .c24 {
.c21:disabled:active .c23,
.c21:disabled:focus .c23,
.c21:disabled:hover .c23 {
background-color: transparent;
}
......@@ -200,7 +200,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
width: -moz-fit-content;
width: fit-content;
border-radius: 16px;
padding: 4px 8px;
padding: 4px 6px;
font-size: 14px;
font-weight: 485;
line-height: 20px;
......@@ -246,22 +246,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
-webkit-justify-content: flex-start;
-ms-flex-pack: start;
justify-content: flex-start;
gap: 16px;
}
.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;
gap: 12px;
}
.c3 {
......@@ -342,14 +327,14 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
border-radius: 12px;
}
.c18 {
.c17 {
font-size: 24px;
line-height: 32px;
text-align: center;
font-weight: 500;
}
.c20 {
.c19 {
font-size: 16px;
font-weight: 500;
line-height: 24px;
......@@ -360,7 +345,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
text-align: center;
}
.c23 {
.c22 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
......@@ -380,7 +365,7 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
.c14 path {
background: #7D7D7D;
fill: #7D7D7D;
fill: #EEB317;
}
.c15 path {
......@@ -507,9 +492,6 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
/>
</svg>
</div>
<div
class="c2 c3"
>
<div
class="c12 c3"
>
......@@ -518,47 +500,42 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
>
<svg
class="c14 c15"
fill="#7D7D7D"
viewBox="0 0 28 28"
fill="#EEB317"
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<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"
fill="#9b9b9b"
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"
/>
</svg>
</div>
<div
class="c16 c3"
>
<div
class="c17 c18 css-4kpdx7"
class="c16 c17 css-4kpdx7"
>
Is this a wallet address?
</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.
</div>
</div>
</div>
<div
class="c9 c21"
class="c9 c20"
>
<button
class="c22 c23"
class="c21 c22"
>
<div
class="c24 c25"
class="c23 c24"
/>
Cancel
</button>
<button
class="c22 c23"
class="c21 c22"
>
<div
class="c24 c25"
class="c23 c24"
/>
Continue
</button>
......@@ -568,7 +545,6 @@ exports[`SmartContractSpeedBumpModal should render correctly 1`] = `
</div>
</div>
</div>
</div>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
......
......@@ -207,7 +207,7 @@ export function useDerivedMintInfo(
<Trans
i18nKey="common.insufficientTokenBalance.error"
values={{
tokenSymbol: currencies[Field.CURRENCY_B]?.symbol,
symbol: currencies[Field.CURRENCY_B]?.symbol,
}}
/>
)
......
......@@ -330,7 +330,7 @@ export function useInitialCurrencyState(): {
const account = useAccount()
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 { initialInputCurrencyAddress, chainId } = useMemo(() => {
// Handle query params or disconnected state
......
......@@ -210,28 +210,16 @@ if (updatedGraphQLfile) {
}
// Migrations + schema warnings
const updatedMobileSchemaFile = danger.git.modified_files.find((file) =>
file.includes('mobile/src/app/schema.ts')
const updatedSchemaFile = danger.git.modified_files.find((file) =>
file.includes('src/app/schema.ts')
)
const updatedMobileMigrationsFile = danger.git.modified_files.find((file) =>
file.includes('mobile/src/app/migrations.ts')
const updatedMigrationsFile = danger.git.modified_files.find((file) =>
file.includes('src/app/migrations.ts')
)
const updatedMobileMigrationsTestFile = danger.git.modified_files.find((file) =>
file.includes('mobile/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 updatedMigrationsTestFile = danger.git.modified_files.find((file) =>
file.includes('src/app/migrations.test.ts')
)
const createdSliceFile = danger.git.created_files.find((file) =>
......@@ -246,50 +234,32 @@ const deletedSliceFile = danger.git.deleted_files.find((file) =>
file.toLowerCase().includes('slice')
)
if (modifiedSliceFile && ((!updatedMobileSchemaFile || !updatedMobileMigrationsFile) || (!updatedExtensionSchemaFile || !updatedExtensionMigrationsFile))) {
if (modifiedSliceFile && (!updatedSchemaFile || !updatedMigrationsFile)) {
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.'
)
}
if (updatedMobileSchemaFile && !updatedMobileMigrationsFile) {
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) {
if (updatedSchemaFile && !updatedMigrationsFile) {
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(
'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)) {
warn('You created or deleted a slice file. Make sure to update the schema and create migration if needed.')
if ((createdSliceFile || deletedSliceFile) && (!updatedSchemaFile || !updatedMigrationsFile)) {
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(
'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 @@
"common.button.back": "Back",
"common.button.buy": "Buy",
"common.button.cancel": "Cancel",
"common.button.checkin": "Check in",
"common.button.close": "Close",
"common.button.confirm": "Confirm",
"common.button.connect": "Connect",
......
......@@ -157,8 +157,9 @@ export function SwapDetails({
<Flex row alignItems="center" gap="$spacing4">
<Text color="$neutral2" numberOfLines={3} variant="body3">
{t('swap.details.slippage')}
</Text>
&nbsp;
<InfoCircleFilled color="$neutral3" size="$icon.16" />
</Text>
</Flex>
</TouchableArea>
<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