ci(release): publish latest release

parent b0f334f8
IPFS hash of the deployment:
- CIDv0: `QmWC1YfS71qk5GNiRpuTpRLhuFhExHbp2NwvmY3v1qpS8C`
- CIDv1: `bafybeiduu3kcz453gevvdoogt6cl4d2hjzr2jpgxiolhoo6mmwjzhmlvxu`
- CIDv0: `QmbnHL5TQqNFjVrQtUdXNUci2KGYPBypTjbTEm8PQJjBSX`
- CIDv1: `bafybeighxdnvvampcjgznnlroji2h4t4fes5acglqdugvuezoob3hvkmga`
The latest release is always mirrored at [app.uniswap.org](https://app.uniswap.org).
......@@ -10,9 +10,14 @@ You can also access the Uniswap Interface from an IPFS gateway.
Your Uniswap settings are never remembered across different URLs.
IPFS gateways:
- https://bafybeiduu3kcz453gevvdoogt6cl4d2hjzr2jpgxiolhoo6mmwjzhmlvxu.ipfs.dweb.link/
- [ipfs://QmWC1YfS71qk5GNiRpuTpRLhuFhExHbp2NwvmY3v1qpS8C/](ipfs://QmWC1YfS71qk5GNiRpuTpRLhuFhExHbp2NwvmY3v1qpS8C/)
- https://bafybeighxdnvvampcjgznnlroji2h4t4fes5acglqdugvuezoob3hvkmga.ipfs.dweb.link/
- [ipfs://QmbnHL5TQqNFjVrQtUdXNUci2KGYPBypTjbTEm8PQJjBSX/](ipfs://QmbnHL5TQqNFjVrQtUdXNUci2KGYPBypTjbTEm8PQJjBSX/)
### 5.99.2 (2025-06-17)
### 5.99.3 (2025-06-18)
### Bug Fixes
* **web:** dynamic fee hotfix prod (#21010) 6fb9aea
web/5.99.2
\ No newline at end of file
web/5.99.3
\ No newline at end of file
import ms from 'ms'
import { FeeData } from 'pages/Pool/Positions/create/types'
import { useMemo } from 'react'
import { V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools'
import { DEFAULT_TICK_SPACING, V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools'
import {
ProtocolVersion,
Token,
......@@ -24,7 +25,7 @@ interface RewardsCampaign {
export interface PoolData {
// basic pool info
idOrAddress: string
feeTier?: number
feeTier?: FeeData
txCount?: number
protocolVersion?: ProtocolVersion
hookAddress?: string
......@@ -123,7 +124,11 @@ export function usePoolData(
const anyLoading = Boolean(loadingV4 || loadingV3 || loadingV2)
const pool = dataV4?.v4Pool ?? dataV3?.v3Pool ?? dataV2?.v2Pair ?? undefined
const feeTier = dataV4?.v4Pool?.feeTier ?? dataV3?.v3Pool?.feeTier ?? V2_DEFAULT_FEE_TIER
const feeTier: FeeData = {
feeAmount: dataV4?.v4Pool?.feeTier ?? dataV3?.v3Pool?.feeTier ?? V2_DEFAULT_FEE_TIER,
tickSpacing: DEFAULT_TICK_SPACING,
isDynamic: dataV4?.v4Pool?.isDynamicFee ?? false,
}
const poolId = dataV4?.v4Pool?.poolId ?? dataV3?.v3Pool?.address ?? dataV2?.v2Pair?.address ?? poolIdOrAddress
return {
......
......@@ -6,7 +6,7 @@ import {
sortPools,
} from 'appGraphql/data/pools/useTopPools'
import { useCallback, useMemo, useRef } from 'react'
import { V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools'
import { DEFAULT_TICK_SPACING, V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools'
import {
useTopV2PairsQuery,
useTopV3PoolsQuery,
......@@ -42,6 +42,7 @@ export function usePoolsFromTokenAddress({
chain,
},
})
const {
loading: loadingV3,
error: errorV3,
......@@ -160,7 +161,13 @@ export function usePoolsFromTokenAddress({
tvl: pool.totalLiquidity?.value,
feeTier: pool.feeTier,
}),
feeTier: pool.feeTier,
feeTier: pool.feeTier
? {
feeAmount: pool.feeTier,
tickSpacing: DEFAULT_TICK_SPACING,
isDynamic: pool.isDynamicFee ?? false,
}
: undefined,
protocolVersion: pool.protocolVersion,
hookAddress: pool.hook?.address,
} as TablePool
......@@ -181,7 +188,13 @@ export function usePoolsFromTokenAddress({
tvl: pool.totalLiquidity?.value,
feeTier: pool.feeTier,
}),
feeTier: pool.feeTier,
feeTier: pool.feeTier
? {
feeAmount: pool.feeTier,
tickSpacing: DEFAULT_TICK_SPACING,
isDynamic: false,
}
: undefined,
protocolVersion: pool.protocolVersion,
} as TablePool
}) ?? []
......@@ -200,7 +213,11 @@ export function usePoolsFromTokenAddress({
tvl: pool.totalLiquidity?.value,
feeTier: V2_DEFAULT_FEE_TIER,
}),
feeTier: V2_DEFAULT_FEE_TIER,
feeTier: {
feeAmount: V2_DEFAULT_FEE_TIER,
tickSpacing: DEFAULT_TICK_SPACING,
isDynamic: false,
},
protocolVersion: pool.protocolVersion,
} as TablePool
}) ?? []
......
import { Percent } from '@uniswap/sdk-core'
import { OrderDirection } from 'appGraphql/data/util'
import { FeeData } from 'pages/Pool/Positions/create/types'
import { BIPS_BASE } from 'uniswap/src/constants/misc'
import { ProtocolVersion, Token } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
......@@ -67,7 +68,7 @@ export interface TablePool {
volume30d: number
apr: Percent
volOverTvl: number
feeTier: number
feeTier: FeeData
protocolVersion: ProtocolVersion
hookAddress?: string
boostedApr?: number
......
......@@ -142,7 +142,7 @@ export function IncreaseLiquidityReview({ onClose }: { onClose: () => void }) {
analytics: {
...getLPBaseAnalyticsProperties({
trace,
fee: feeTier,
fee: feeTier?.feeAmount,
tickSpacing,
tickLower,
tickUpper,
......
......@@ -176,7 +176,7 @@ export function IncreaseLiquidityTxContextProvider({ children }: PropsWithChildr
pool: {
token0: token0.isNative ? ZERO_ADDRESS : token0.address,
token1: token1.isNative ? ZERO_ADDRESS : token1.address,
fee: positionInfo.feeTier ? Number(positionInfo.feeTier) : undefined,
fee: positionInfo.feeTier?.feeAmount,
tickSpacing: positionInfo.tickSpacing ? Number(positionInfo.tickSpacing) : undefined,
hooks: positionInfo.v4hook,
},
......
......@@ -290,6 +290,7 @@ export function FeeTierSearchModal() {
setPositionState((prevState) => ({
...prevState,
fee: {
isDynamic: false,
feeAmount: feeHundredthsOfBips,
tickSpacing: calculateTickSpacingFromFeeAmount(feeHundredthsOfBips),
},
......@@ -390,6 +391,7 @@ export function FeeTierSearchModal() {
setPositionState((prevState) => ({
...prevState,
fee: {
isDynamic: pool.fee.isDynamic,
feeAmount: pool.fee.feeAmount,
tickSpacing: pool.fee.tickSpacing,
},
......
import { LiquidityPositionInfoBadges } from 'components/Liquidity/LiquidityPositionInfoBadges'
import { render } from 'test-utils/render'
import { DEFAULT_TICK_SPACING } from 'uniswap/src/constants/pools'
describe('LiquidityPositionInfoBadges', () => {
it('should render with default size', () => {
const { getByText } = render(<LiquidityPositionInfoBadges versionLabel="2" feeTier="100" size="default" />)
const { getByText } = render(
<LiquidityPositionInfoBadges
versionLabel="2"
feeTier={{ feeAmount: 100, tickSpacing: DEFAULT_TICK_SPACING, isDynamic: false }}
size="default"
/>,
)
expect(getByText('2')).toBeInTheDocument()
})
it('should render with small size', () => {
const { getByText } = render(<LiquidityPositionInfoBadges versionLabel="2" feeTier="100" size="small" />)
const { getByText } = render(
<LiquidityPositionInfoBadges
versionLabel="2"
feeTier={{ feeAmount: 100, tickSpacing: DEFAULT_TICK_SPACING, isDynamic: false }}
size="small"
/>,
)
expect(getByText('2')).toBeInTheDocument()
})
it('should render with multiple badges', () => {
const { getByText } = render(<LiquidityPositionInfoBadges versionLabel="2" feeTier="100" size="default" />)
const { getByText } = render(
<LiquidityPositionInfoBadges
versionLabel="2"
feeTier={{ feeAmount: 100, tickSpacing: DEFAULT_TICK_SPACING, isDynamic: false }}
size="default"
/>,
)
expect(getByText('2')).toBeInTheDocument()
expect(getByText('0.01%')).toBeInTheDocument()
})
......
import { FeeAmount } from '@uniswap/v3-sdk'
import { isDynamicFeeTierAmount } from 'components/Liquidity/utils'
import { isDynamicFeeTier } from 'components/Liquidity/utils'
import { ZERO_ADDRESS } from 'constants/misc'
import { FeeData } from 'pages/Pool/Positions/create/types'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { CopyHelper } from 'theme/components/CopyHelper'
......@@ -62,7 +62,7 @@ export function LiquidityPositionInfoBadges({
}: {
versionLabel?: string
v4hook?: string
feeTier?: string | FeeAmount
feeTier?: FeeData
size: 'small' | 'default'
}): JSX.Element {
const { t } = useTranslation()
......@@ -73,10 +73,10 @@ export function LiquidityPositionInfoBadges({
v4hook && v4hook !== ZERO_ADDRESS
? { label: v4hook, copyable: true, icon: <DocumentList color="$neutral2" size={16} /> }
: undefined,
feeTier !== undefined && feeTier !== '' && (typeof feeTier === 'number' || !isNaN(Number(feeTier)))
? isDynamicFeeTierAmount(feeTier)
feeTier
? isDynamicFeeTier(feeTier)
? { label: t('common.dynamic') }
: { label: `${Number(feeTier) / 10000}%` }
: { label: `${feeTier.feeAmount / 10000}%` }
: undefined,
].filter(Boolean) as BadgeData[]
}, [versionLabel, v4hook, feeTier, t])
......
......@@ -4,6 +4,7 @@ import { UniverseChainId } from 'uniswap/src/features/chains/types'
interface FeeDataWithChain {
feeData: {
isDynamic: boolean
feeAmount: FeeAmount
tickSpacing: number
}
......@@ -11,22 +12,30 @@ interface FeeDataWithChain {
}
export const defaultFeeTiers: Record<FeeAmount, FeeDataWithChain> = {
[FeeAmount.LOWEST]: { feeData: { feeAmount: FeeAmount.LOWEST, tickSpacing: TICK_SPACINGS[FeeAmount.LOWEST] } },
[FeeAmount.LOWEST]: {
feeData: { feeAmount: FeeAmount.LOWEST, tickSpacing: TICK_SPACINGS[FeeAmount.LOWEST], isDynamic: false },
},
[FeeAmount.LOW_200]: {
feeData: { feeAmount: FeeAmount.LOW_200, tickSpacing: TICK_SPACINGS[FeeAmount.LOW_200] },
feeData: { feeAmount: FeeAmount.LOW_200, tickSpacing: TICK_SPACINGS[FeeAmount.LOW_200], isDynamic: false },
supportedChainIds: [UniverseChainId.Base],
},
[FeeAmount.LOW_300]: {
feeData: { feeAmount: FeeAmount.LOW_300, tickSpacing: TICK_SPACINGS[FeeAmount.LOW_300] },
feeData: { feeAmount: FeeAmount.LOW_300, tickSpacing: TICK_SPACINGS[FeeAmount.LOW_300], isDynamic: false },
supportedChainIds: [UniverseChainId.Base],
},
[FeeAmount.LOW_400]: {
feeData: { feeAmount: FeeAmount.LOW_400, tickSpacing: TICK_SPACINGS[FeeAmount.LOW_400] },
feeData: { feeAmount: FeeAmount.LOW_400, tickSpacing: TICK_SPACINGS[FeeAmount.LOW_400], isDynamic: false },
supportedChainIds: [UniverseChainId.Base],
},
[FeeAmount.LOW]: { feeData: { feeAmount: FeeAmount.LOW, tickSpacing: TICK_SPACINGS[FeeAmount.LOW] } },
[FeeAmount.MEDIUM]: { feeData: { feeAmount: FeeAmount.MEDIUM, tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM] } },
[FeeAmount.HIGH]: { feeData: { feeAmount: FeeAmount.HIGH, tickSpacing: TICK_SPACINGS[FeeAmount.HIGH] } },
[FeeAmount.LOW]: {
feeData: { feeAmount: FeeAmount.LOW, tickSpacing: TICK_SPACINGS[FeeAmount.LOW], isDynamic: false },
},
[FeeAmount.MEDIUM]: {
feeData: { feeAmount: FeeAmount.MEDIUM, tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM], isDynamic: false },
},
[FeeAmount.HIGH]: {
feeData: { feeAmount: FeeAmount.HIGH, tickSpacing: TICK_SPACINGS[FeeAmount.HIGH], isDynamic: false },
},
} as const
export const lpStatusConfig = {
......
......@@ -74,6 +74,7 @@ export function useAllFeeTierPoolData({
feeTierData[feeTier] = {
id: pool.poolId,
fee: {
isDynamic: pool.isDynamicFee,
feeAmount: pool.fee,
tickSpacing: pool.tickSpacing,
},
......
import { PositionStatus, ProtocolVersion } from '@uniswap/client-pools/dist/pools/v1/types_pb'
import { Currency, CurrencyAmount, Percent, Price, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { FeeAmount, Pool as V3Pool, Position as V3Position } from '@uniswap/v3-sdk'
import { Pool as V3Pool, Position as V3Position } from '@uniswap/v3-sdk'
import { Pool as V4Pool, Position as V4Position } from '@uniswap/v4-sdk'
import { FeeData } from 'pages/Pool/Positions/create/types'
import { Dispatch, ReactNode, SetStateAction } from 'react'
......@@ -73,7 +73,7 @@ export type V3PositionInfo = BasePositionInfo & {
version: ProtocolVersion.V3
tokenId: string
pool?: V3Pool
feeTier?: FeeAmount
feeTier?: FeeData
position?: V3Position
v4hook: undefined
owner: string
......@@ -84,7 +84,7 @@ type V4PositionInfo = BasePositionInfo & {
tokenId: string
pool?: V4Pool
position?: V4Position
feeTier?: string
feeTier?: FeeData
v4hook?: string
owner: string
totalApr?: number
......
......@@ -22,6 +22,7 @@ import { Flag } from 'ui/src/components/icons/Flag'
import { Pools } from 'ui/src/components/icons/Pools'
import { SwapCoin } from 'ui/src/components/icons/SwapCoin'
import { AppTFunction } from 'ui/src/i18n/types'
import { DEFAULT_TICK_SPACING } from 'uniswap/src/constants/pools'
import { nativeOnChain } from 'uniswap/src/constants/tokens'
import { ProtocolItems } from 'uniswap/src/data/tradingApi/__generated__'
import { UniverseChainId } from 'uniswap/src/features/chains/types'
......@@ -98,10 +99,12 @@ export function getPositionUrl(position: PositionInfo): string {
return `/positions/v4/${chainUrlParam}/${position.tokenId}`
}
function parseV3FeeTier(feeTier: string | undefined): FeeAmount | undefined {
function parseV3FeeTier(feeTier: string | undefined): FeeData | undefined {
const parsedFee = parseInt(feeTier || '')
return parsedFee in FeeAmount ? parsedFee : undefined
return parsedFee in FeeAmount
? { feeAmount: parsedFee, tickSpacing: DEFAULT_TICK_SPACING, isDynamic: false }
: undefined
}
export function getPoolFromRest({
......@@ -177,7 +180,7 @@ export function getPoolFromRest({
return new V3Pool(
token0 as Token,
token1 as Token,
feeTier,
feeTier.feeAmount,
pool.currentPrice,
pool.currentLiquidity,
parseInt(pool.currentTick),
......@@ -346,7 +349,11 @@ export function parseRestPosition(position?: RestPosition): PositionInfo | undef
const fee1Amount = CurrencyAmount.fromRawAmount(token1, v4Position.token1UncollectedFees)
return {
status: position.status,
feeTier: v4Position.feeTier,
feeTier: {
feeAmount: Number(v4Position.feeTier),
tickSpacing: Number(v4Position.tickSpacing),
isDynamic: v4Position.isDynamicFee,
},
version: ProtocolVersion.V4,
position: sdkPosition,
chainId: token0.chainId,
......@@ -528,7 +535,13 @@ export function mergeFeeTiers({
formattedDynamicFeeTier: string
}): Record<number, FeeTierData> {
const result: Record<number, FeeTierData> = {}
const hasDynamicFeeTier = Object.values(feeTiers).some((feeTier) => isDynamicFeeTier(feeTier.fee))
for (const feeTier of feeData) {
if (hasDynamicFeeTier && isDynamicFeeTier(feeTier)) {
continue
}
result[feeTier.feeAmount] = {
fee: feeTier,
formattedFee: isDynamicFeeTier(feeTier)
......@@ -547,7 +560,7 @@ export function mergeFeeTiers({
function getDefaultFeeTiersForChain(
chainId: UniverseChainId | undefined,
protocolVersion: ProtocolVersion,
): Record<FeeAmount, { feeAmount: FeeAmount; tickSpacing: number }> {
): Record<FeeAmount, { isDynamic: boolean; feeAmount: FeeAmount; tickSpacing: number }> {
const feeData = Object.values(defaultFeeTiers)
.filter((feeTier) => {
// Only filter by chain support if we're on V3
......@@ -563,7 +576,7 @@ function getDefaultFeeTiersForChain(
acc[fee.feeAmount] = fee
return acc
},
{} as Record<FeeAmount, { feeAmount: FeeAmount; tickSpacing: number }>,
{} as Record<FeeAmount, { isDynamic: boolean; feeAmount: FeeAmount; tickSpacing: number }>,
)
}
......@@ -663,22 +676,7 @@ export function getDefaultFeeTiersWithData({
}
export function isDynamicFeeTier(feeData: FeeData): feeData is DynamicFeeData {
return feeData.feeAmount === DYNAMIC_FEE_DATA.feeAmount
}
export function isDynamicFeeTierAmount(
feeAmount: string | number | undefined,
): feeAmount is DynamicFeeData['feeAmount'] {
if (!feeAmount) {
return false
}
const feeAmountNumber = Number(feeAmount)
if (isNaN(feeAmountNumber)) {
return false
}
return feeAmountNumber === DYNAMIC_FEE_DATA.feeAmount
return feeData.isDynamic || feeData.feeAmount === DYNAMIC_FEE_DATA.feeAmount
}
export function formatTokenAmount(amount: string, decimals: number): string {
......
......@@ -180,7 +180,7 @@ export default function ChartSection(props: ChartSectionProps) {
const selectedChartProps = {
...props,
feeTier: Number(props.poolData.feeTier),
feeTier: Number(props.poolData.feeTier?.feeAmount),
height: PDP_CHART_HEIGHT_PX,
timePeriod,
tokenA: currencyA,
......
......@@ -7,6 +7,7 @@ import { PoolDetailsBreadcrumb, PoolDetailsHeader } from 'components/Pools/PoolD
import store from 'state'
import { usdcWethPoolAddress, validBEPoolToken0, validBEPoolToken1 } from 'test-utils/pools/fixtures'
import { render, screen } from 'test-utils/render'
import { DEFAULT_TICK_SPACING } from 'uniswap/src/constants/pools'
import { dismissTokenWarning } from 'uniswap/src/features/tokens/slice/slice'
vi.mock('nft/components/iconExports', () => ({
......@@ -55,7 +56,7 @@ describe('PoolDetailsHeader', () => {
onChartTypeChange: vi.fn(),
priceChartType: PriceChartType.LINE,
onPriceChartTypeChange: vi.fn(),
feeTier: 500,
feeTier: { feeAmount: 500, tickSpacing: DEFAULT_TICK_SPACING, isDynamic: false },
toggleReversed: vi.fn(),
}
......
......@@ -15,6 +15,7 @@ import Row from 'components/deprecated/Row'
import { NATIVE_CHAIN_ID } from 'constants/tokens'
import styled, { useTheme } from 'lib/styled-components'
import { ReversedArrowsIcon } from 'nft/components/iconExports'
import { FeeData } from 'pages/Pool/Positions/create/types'
import React, { useMemo, useState } from 'react'
import { ChevronRight, ExternalLink as ExternalLinkIcon } from 'react-feather'
import { Trans, useTranslation } from 'react-i18next'
......@@ -85,7 +86,7 @@ const PoolDetailsTitle = ({
token0?: Token
token1?: Token
chainId?: UniverseChainId
feeTier?: number
feeTier?: FeeData
protocolVersion?: ProtocolVersion
toggleReversed: React.DispatchWithoutAction
hookAddress?: string
......@@ -267,7 +268,7 @@ interface PoolDetailsHeaderProps {
poolAddress?: string
token0?: Token
token1?: Token
feeTier?: number
feeTier?: FeeData
protocolVersion?: ProtocolVersion
toggleReversed: React.DispatchWithoutAction
loading?: boolean
......
......@@ -256,7 +256,7 @@ export function PoolDetailsStats({ poolData, isReversed, chainId, loading }: Poo
{poolData.volumeUSD24H !== undefined && poolData.feeTier !== undefined && (
<StatItem
title={<Trans i18nKey="stats.24fees" />}
value={poolData.volumeUSD24H * (poolData.feeTier / 1000000)}
value={poolData.volumeUSD24H * (poolData.feeTier.feeAmount / 1000000)}
/>
)}
</StatsWrapper>
......
......@@ -6,6 +6,7 @@ import { useExploreContextTopPools } from 'state/explore/topPools'
import { mocked } from 'test-utils/mocked'
import { validRestPoolToken0, validRestPoolToken1 } from 'test-utils/pools/fixtures'
import { render, screen } from 'test-utils/render'
import { DEFAULT_TICK_SPACING } from 'uniswap/src/constants/pools'
import { ProtocolVersion } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
vi.mock('state/explore/topPools')
......@@ -54,7 +55,11 @@ describe('PoolTable', () => {
chain: 'mainnet',
token0: validRestPoolToken0,
token1: validRestPoolToken1,
feeTier: 10000,
feeTier: {
feeAmount: 10000,
tickSpacing: DEFAULT_TICK_SPACING,
isDynamic: false,
},
hash: '0x123',
txCount: 200,
tvl: 300,
......
......@@ -7,7 +7,7 @@ import { PoolSortFields, TablePool } from 'appGraphql/data/pools/useTopPools'
import { OrderDirection, gqlToCurrency, supportedChainIdFromGQLChain, unwrapToken } from 'appGraphql/data/util'
import { PortfolioLogo } from 'components/AccountDrawer/MiniPortfolio/PortfolioLogo'
import LPIncentiveFeeStatTooltip from 'components/Liquidity/LPIncentiveFeeStatTooltip'
import { isDynamicFeeTierAmount } from 'components/Liquidity/utils'
import { isDynamicFeeTier } from 'components/Liquidity/utils'
import CurrencyLogo from 'components/Logo/CurrencyLogo'
import { Table } from 'components/Table'
import { Cell } from 'components/Table/Cell'
......@@ -26,6 +26,7 @@ import useSimplePagination from 'hooks/useSimplePagination'
import { useAtom } from 'jotai'
import { atomWithReset, useAtomValue, useResetAtom, useUpdateAtom } from 'jotai/utils'
import { exploreProtocolVersionFilterAtom } from 'pages/Explore/ProtocolFilter'
import { FeeData } from 'pages/Pool/Positions/create/types'
import { ReactElement, memo, useCallback, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { TABLE_PAGE_SIZE, giveExploreStatDefaultValue } from 'state/explore'
......@@ -64,7 +65,7 @@ interface PoolTableValues {
volOverTvl?: number
link: string
protocolVersion?: string
feeTier?: number
feeTier?: FeeData
rewardApr?: number
token0CurrencyId?: string
token1CurrencyId?: string
......@@ -376,7 +377,7 @@ export function PoolsTable({
<Cell loading={showLoadingSkeleton}>
<TableText>
{feeTier.getValue?.()
? `${isDynamicFeeTierAmount(feeTier.getValue()!) ? t('common.dynamic') : (feeTier.getValue()! / BIPS_BASE).toFixed(2) + '%'}`
? `${isDynamicFeeTier(feeTier.getValue()!) ? t('common.dynamic') : (feeTier.getValue()!.feeAmount / BIPS_BASE).toFixed(2) + '%'}`
: '-'}
</TableText>
</Cell>
......
......@@ -149,7 +149,7 @@ export function RemoveLiquidityReview({ onClose }: { onClose: () => void }) {
analytics: {
...getLPBaseAnalyticsProperties({
trace,
fee: feeTier,
fee: feeTier?.feeAmount,
tickSpacing,
tickLower,
tickUpper,
......
......@@ -116,7 +116,7 @@ export function useRemoveLiquidityTxAndGasInfo({ account }: { account?: string }
pool: {
token0: currency0.isNative ? ZERO_ADDRESS : currency0.address,
token1: currency1.isNative ? ZERO_ADDRESS : currency1.address,
fee: positionInfo.feeTier ? Number(positionInfo.feeTier) : undefined,
fee: positionInfo.feeTier?.feeAmount,
tickSpacing: positionInfo.tickSpacing ? Number(positionInfo.tickSpacing) : undefined,
hooks: positionInfo.v4hook,
},
......
......@@ -7,6 +7,7 @@ import { TokenDetailsPoolsTable } from 'components/Tokens/TokenDetails/tables/To
import { mocked } from 'test-utils/mocked'
import { validBEPoolToken0, validBEPoolToken1 } from 'test-utils/pools/fixtures'
import { render, screen } from 'test-utils/render'
import { DEFAULT_TICK_SPACING } from 'uniswap/src/constants/pools'
import { ProtocolVersion } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { UniverseChainId } from 'uniswap/src/features/chains/types'
......@@ -67,7 +68,11 @@ describe('TDPPoolTable', () => {
{
token0: validBEPoolToken0,
token1: validBEPoolToken1,
feeTier: 10000,
feeTier: {
feeAmount: 10000,
tickSpacing: DEFAULT_TICK_SPACING,
isDynamic: false,
},
hash: '0x123',
txCount: 200,
tvl: 300,
......
......@@ -134,7 +134,7 @@ export function MigrateV3PositionTxContextProvider({
token1: positionInfo.currency1Amount.currency.isNative
? ZERO_ADDRESS
: positionInfo.currency1Amount.currency.address,
fee: positionInfo.feeTier ? Number(positionInfo.feeTier) : undefined,
fee: positionInfo.feeTier?.feeAmount,
tickSpacing: positionInfo.tickSpacing ? Number(positionInfo.tickSpacing) : undefined,
},
tickLower: positionInfo.tickLower ? Number(positionInfo.tickLower) : undefined,
......
......@@ -229,7 +229,7 @@ function MigrateV3Inner({
analytics: {
...getLPBaseAnalyticsProperties({
trace,
fee: positionInfo.feeTier,
fee: positionInfo.feeTier?.feeAmount,
tickSpacing: positionInfo.tickSpacing,
tickLower: positionInfo.tickLower,
tickUpper: positionInfo.tickUpper,
......
......@@ -138,7 +138,7 @@ export function ClaimFeeModal() {
pool: {
token0: currency0.isNative ? ZERO_ADDRESS : currency0.address,
token1: currency1.isNative ? ZERO_ADDRESS : currency1.address,
fee: positionInfo.feeTier ? Number(positionInfo.feeTier) : undefined,
fee: positionInfo.feeTier?.feeAmount,
tickSpacing: positionInfo.tickSpacing ? Number(positionInfo.tickSpacing) : undefined,
hooks: positionInfo.v4hook,
},
......
......@@ -289,7 +289,7 @@ function PositionPage() {
pool_address: positionInfo.poolId,
label: [currency0Amount.currency.symbol, currency1Amount.currency.symbol].join('/'),
type: positionInfo.version,
fee_tier: typeof positionInfo.feeTier === 'string' ? parseInt(positionInfo.feeTier) : positionInfo.feeTier,
fee_tier: positionInfo.feeTier?.feeAmount,
baseCurrencyId: currencyIdToAddress(currencyId(currency0Amount.currency)),
quoteCurrencyId: currencyIdToAddress(currencyId(currency1Amount.currency)),
}}
......@@ -438,7 +438,7 @@ function PositionPage() {
poolAddressOrId={positionInfo.poolId}
chainId={positionInfo.chainId}
tickSpacing={positionInfo.tickSpacing}
feeTier={positionInfo.feeTier}
feeTier={positionInfo.feeTier?.feeAmount}
hook={positionInfo.v4hook}
positionStatus={status}
priceOrdering={
......
......@@ -55,7 +55,11 @@ const DEFAULT_DEPOSIT_STATE: DepositState = {
exactAmounts: {},
}
const DEFAULT_FEE_DATA = { feeAmount: FeeAmount.MEDIUM, tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM] }
const DEFAULT_FEE_DATA = {
feeAmount: FeeAmount.MEDIUM,
tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],
isDynamic: false,
}
const DEFAULT_POSITION_STATE: PositionState = {
fee: DEFAULT_FEE_DATA,
......
......@@ -233,12 +233,7 @@ export function CreatePositionModal({ isOpen, onClose }: { isOpen: boolean; onCl
<Text variant="heading3">{currencyAmounts?.TOKEN1?.currency.symbol}</Text>
</Flex>
<Flex row gap={2} alignItems="center">
<LiquidityPositionInfoBadges
size="small"
versionLabel={versionLabel}
v4hook={hook}
feeTier={fee.feeAmount}
/>
<LiquidityPositionInfoBadges size="small" versionLabel={versionLabel} v4hook={hook} feeTier={fee} />
</Flex>
</Flex>
<DoubleCurrencyLogo
......
......@@ -22,6 +22,7 @@ export const DynamicFeeTierSpeedbump = () => {
fee: {
feeAmount: dynamicFeeTierSpeedbumpData.wishFeeData.feeAmount,
tickSpacing: dynamicFeeTierSpeedbumpData.wishFeeData.tickSpacing,
isDynamic: dynamicFeeTierSpeedbumpData.wishFeeData.isDynamic,
},
}))
......
......@@ -74,12 +74,7 @@ export const EditSelectTokensStep = (props?: FlexProps) => {
<Text variant="subheading1">{TOKEN1?.symbol}</Text>
</Flex>
<Flex row gap={2} alignItems="center">
<LiquidityPositionInfoBadges
size="small"
versionLabel={versionLabel}
v4hook={hook}
feeTier={fee.feeAmount}
/>
<LiquidityPositionInfoBadges size="small" versionLabel={versionLabel} v4hook={hook} feeTier={fee} />
</Flex>
</Flex>
</Flex>
......
......@@ -4,7 +4,7 @@ import { Pair } from '@uniswap/v2-sdk'
import { FeeAmount, TICK_SPACINGS, Pool as V3Pool } from '@uniswap/v3-sdk'
import { Pool as V4Pool } from '@uniswap/v4-sdk'
import { DepositInfo, DepositState } from 'components/Liquidity/types'
import { getPoolFromRest } from 'components/Liquidity/utils'
import { getPoolFromRest, isDynamicFeeTier } from 'components/Liquidity/utils'
import { ZERO_ADDRESS } from 'constants/misc'
import { checkIsNative, useCurrency } from 'hooks/Tokens'
import { useAccount } from 'hooks/useAccount'
......@@ -569,7 +569,15 @@ function getParsedFeeTierParam(params: ParsedQs): FeeData | undefined {
return DEFAULT_FEE_DATA
}
return { feeAmount: feeTierNumber, tickSpacing }
return {
feeAmount: feeTierNumber,
tickSpacing,
isDynamic: isDynamicFeeTier({
feeAmount: feeTierNumber,
tickSpacing,
isDynamic: false,
}),
}
}
// Prefill currency inputs from URL search params ?currencyA=ETH&currencyB=0x123...&chain=base&feeTier=10000&hook=0x123...
......
......@@ -6,9 +6,12 @@ import { Pool as V4Pool } from '@uniswap/v4-sdk'
import { Dispatch, SetStateAction } from 'react'
import { PositionField } from 'types/position'
import { WarningSeverity } from 'uniswap/src/components/modals/WarningModal/types'
import { DEFAULT_TICK_SPACING } from 'uniswap/src/constants/pools'
import { UniverseChainId } from 'uniswap/src/features/chains/types'
import { TransactionStep } from 'uniswap/src/features/transactions/steps/types'
export type FeeData = {
isDynamic: boolean
feeAmount: number
tickSpacing: number
}
......@@ -20,8 +23,9 @@ export type DynamicFeeData = FeeData & {
}
export const DYNAMIC_FEE_DATA = {
isDynamic: true,
feeAmount: DYNAMIC_FEE_AMOUNT,
tickSpacing: 60,
tickSpacing: DEFAULT_TICK_SPACING,
} as const satisfies DynamicFeeData
export enum PositionFlowStep {
......@@ -43,7 +47,11 @@ export interface PositionState {
}
}
export const DEFAULT_FEE_DATA = { feeAmount: FeeAmount.MEDIUM, tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM] }
export const DEFAULT_FEE_DATA = {
feeAmount: FeeAmount.MEDIUM,
tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],
isDynamic: false,
}
export const DEFAULT_POSITION_STATE: PositionState = {
fee: DEFAULT_FEE_DATA,
......
......@@ -138,6 +138,7 @@ describe('getV3PriceRangeInfo', () => {
fee: {
feeAmount: FeeAmount.MEDIUM,
tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],
isDynamic: false,
},
}
......@@ -344,6 +345,7 @@ describe('getV3PriceRangeInfo', () => {
fee: {
feeAmount: FeeAmount.MEDIUM,
tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],
isDynamic: false,
},
}
......@@ -712,6 +714,7 @@ describe('getV4PriceRangeInfo', () => {
fee: {
feeAmount: FeeAmount.MEDIUM,
tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],
isDynamic: false,
},
}
......@@ -927,6 +930,7 @@ describe('getV4PriceRangeInfo', () => {
fee: {
feeAmount: FeeAmount.MEDIUM,
tickSpacing: TICK_SPACINGS[FeeAmount.MEDIUM],
isDynamic: false,
},
}
......
......@@ -115,7 +115,7 @@ export default function PoolDetailsPage() {
calculateApr({
volume24h: poolData?.volumeUSD24H,
tvl: poolData?.tvlUSD,
feeTier: poolData?.feeTier,
feeTier: poolData?.feeTier?.feeAmount,
}),
[poolData?.volumeUSD24H, poolData?.tvlUSD, poolData?.feeTier],
)
......@@ -234,7 +234,7 @@ export default function PoolDetailsPage() {
chainId={chainInfo.id}
token0={token0}
token1={token1}
feeTier={poolData?.feeTier}
feeTier={poolData?.feeTier?.feeAmount}
hookAddress={poolData?.hookAddress}
protocolVersion={poolData?.protocolVersion}
loading={loading}
......
......@@ -12,7 +12,7 @@ import { useAtomValue } from 'jotai/utils'
import { useContext, useMemo } from 'react'
import { ExploreContext, giveExploreStatDefaultValue } from 'state/explore'
import { PoolStat } from 'state/explore/types'
import { V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools'
import { DEFAULT_TICK_SPACING, V2_DEFAULT_FEE_TIER } from 'uniswap/src/constants/pools'
function useFilteredPools(pools?: PoolStat[]) {
const filterString = useAtomValue(exploreSearchStringAtom)
......@@ -86,7 +86,11 @@ function convertPoolStatsToPoolStat(poolStats: PoolStats): PoolStat {
feeTier: poolStats.feeTier ?? V2_DEFAULT_FEE_TIER,
}),
boostedApr: poolStats.boostedApr,
feeTier: poolStats.feeTier ?? V2_DEFAULT_FEE_TIER,
feeTier: {
feeAmount: poolStats.feeTier ?? V2_DEFAULT_FEE_TIER,
tickSpacing: DEFAULT_TICK_SPACING,
isDynamic: false, // TODO: add dynamic fee tier check when client-explore is updated
},
volOverTvl: calculate1DVolOverTvl(poolStats.volume1Day?.value, poolStats.totalLiquidity?.value),
hookAddress: poolStats.hook?.address,
}
......
import { Amount, PoolStats, TokenStats } from '@uniswap/client-explore/dist/uniswap/explore/v1/service_pb'
import { Percent } from '@uniswap/sdk-core'
import { FeeData as CreatePositionFeeData } from 'pages/Pool/Positions/create/types'
import { FeeData } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
type PricePoint = { timestamp: number; value: number }
......@@ -13,7 +14,16 @@ export interface TokenStat
type PoolStatWithoutMethods = Omit<
PoolStats,
'clone' | 'toBinary' | 'toJson' | 'equals' | 'fromBinary' | 'fromJson' | 'fromJsonString' | 'toJsonString' | 'getType'
| 'clone'
| 'toBinary'
| 'toJson'
| 'equals'
| 'fromBinary'
| 'fromJson'
| 'fromJsonString'
| 'toJsonString'
| 'getType'
| 'feeTier'
>
export interface PoolStat extends PoolStatWithoutMethods {
......@@ -21,4 +31,5 @@ export interface PoolStat extends PoolStatWithoutMethods {
boostedApr?: number
volOverTvl?: number
hookAddress?: string
feeTier?: CreatePositionFeeData
}
......@@ -3,6 +3,7 @@ import { Currency, WETH9 } from '@uniswap/sdk-core'
import { FeeAmount, Pool, Position } from '@uniswap/v3-sdk'
import { PoolData } from 'appGraphql/data/pools/usePoolData'
import { PoolStat } from 'state/explore/types'
import { DEFAULT_TICK_SPACING } from 'uniswap/src/constants/pools'
import { USDC_MAINNET } from 'uniswap/src/constants/tokens'
import { Token } from 'uniswap/src/data/graphql/uniswap-data-api/__generated__/types-and-hooks'
import { UniverseChainId } from 'uniswap/src/features/chains/types'
......@@ -119,7 +120,11 @@ export const usdcWethPoolAddress = '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640'
export const validPoolDataResponse = {
data: {
feeTier: 500,
feeTier: {
feeAmount: 500,
tickSpacing: DEFAULT_TICK_SPACING,
isDynamic: false,
},
token0: validBEPoolToken0,
token1: validBEPoolToken1,
token0Price: 1605.481,
......
export const V2_DEFAULT_FEE_TIER = 3000
export const DEFAULT_TICK_SPACING = 60
"""This directive allows results to be deferred during execution"""
directive @defer on FIELD
"""
Tells the service this field/object has access authorized by a Cognito User Pools token.
"""
directive @aws_cognito_user_pools(
"""List of cognito user pool groups which have access on this field"""
cognito_groups: [String]
) on OBJECT | FIELD_DEFINITION
"""
Tells the service this field/object has access authorized by a Lambda Authorizer.
"""
directive @aws_lambda on OBJECT | FIELD_DEFINITION
"""Directs the schema to enforce authorization on a field"""
directive @aws_auth(
"""List of cognito user pool groups which have access on this field"""
cognito_groups: [String]
) on FIELD_DEFINITION
"""
Tells the service this field/object has access authorized by sigv4 signing.
"""
......@@ -11,11 +30,6 @@ Tells the service this field/object has access authorized by an API key.
"""
directive @aws_api_key on OBJECT | FIELD_DEFINITION
"""
Tells the service this field/object has access authorized by an OIDC token.
"""
directive @aws_oidc on OBJECT | FIELD_DEFINITION
"""
Tells the service which subscriptions will be published to when this mutation is
called. This directive is deprecated use @aws_susbscribe directive instead.
......@@ -27,19 +41,6 @@ directive @aws_publish(
subscriptions: [String]
) on FIELD_DEFINITION
"""
Tells the service this field/object has access authorized by a Cognito User Pools token.
"""
directive @aws_cognito_user_pools(
"""List of cognito user pool groups which have access on this field"""
cognito_groups: [String]
) on OBJECT | FIELD_DEFINITION
"""
Tells the service this field/object has access authorized by a Lambda Authorizer.
"""
directive @aws_lambda on OBJECT | FIELD_DEFINITION
"""Tells the service which mutation triggers this subscription."""
directive @aws_subscribe(
"""
......@@ -48,11 +49,10 @@ directive @aws_subscribe(
mutations: [String]
) on FIELD_DEFINITION
"""Directs the schema to enforce authorization on a field"""
directive @aws_auth(
"""List of cognito user pool groups which have access on this field"""
cognito_groups: [String]
) on FIELD_DEFINITION
"""
Tells the service this field/object has access authorized by an OIDC token.
"""
directive @aws_oidc on OBJECT | FIELD_DEFINITION
"""
Types, unions, and inputs (alphabetized):
......@@ -203,6 +203,7 @@ enum Chain {
MONAD_TESTNET
ARBITRUM_SEPOLIA
BASE_SEPOLIA
SOLANA
UNKNOWN_CHAIN
}
......@@ -233,9 +234,11 @@ enum Currency {
MATIC
MXN
NGN
NZD
PKR
RUB
SGD
THB
TRY
UAH
USD
......@@ -1222,6 +1225,7 @@ type Token implements IContract {
v4Transactions(first: Int!, timestampCursor: Int): [PoolTransaction]
v3Transactions(first: Int!, timestampCursor: Int): [PoolTransaction]
v2Transactions(first: Int!, timestampCursor: Int): [PoolTransaction]
source: TokenSource
}
type TokenAmount {
......@@ -1337,9 +1341,14 @@ enum TokenSortableField {
POPULARITY
}
enum TokenSource {
TOKEN_FACTORY
}
enum TokenStandard {
NATIVE
ERC20
SPL
}
input TokenTradeInput {
......@@ -1534,6 +1543,7 @@ type V4Pool {
token1Supply: Float
txCount: Int
feeTier: Float
isDynamicFee: Boolean
hook: V4PoolHook
totalLiquidityPercentChange24h: Amount
cumulativeVolume(duration: HistoryDuration!): Amount
......
......@@ -61,6 +61,7 @@ query V4Pool($chain: Chain!, $poolId: String!) {
id
protocolVersion
feeTier
isDynamicFee
poolId
hook {
id
......
......@@ -3,6 +3,7 @@ query TopV4Pools($chain: Chain!, $first: Int!, $cursor: Float, $tokenAddress: St
id
protocolVersion
poolId
isDynamicFee
hook {
id
address
......
......@@ -56,6 +56,8 @@ const mapServerCurrencyToFiatCurrency: Record<Currency, FiatCurrency | undefined
[Currency.Vnd]: FiatCurrency.VietnameseDong,
[Currency.Eth]: undefined,
[Currency.Matic]: undefined,
[Currency.Nzd]: undefined,
[Currency.Thb]: undefined,
}
export const mapFiatCurrencyToServerCurrency: Record<FiatCurrency, SupportedServerCurrency> = {
[FiatCurrency.ArgentinePeso]: Currency.Ars,
......
......@@ -2,6 +2,7 @@ import { USDC, USDT } from 'uniswap/src/constants/tokens'
import { TransactionStepType } from 'uniswap/src/features/transactions/steps/types'
import { DEFAULT_TICK_SPACING } from 'uniswap/src/constants/pools'
import { IndependentToken, ProtocolItems } from 'uniswap/src/data/tradingApi/__generated__'
import { generateLPTransactionSteps } from 'uniswap/src/features/transactions/liquidity/steps/generateLPTransactionSteps'
import {
......@@ -52,7 +53,7 @@ describe('Liquidity', () => {
token0: USDC.address,
token1: USDT.address,
fee: 3000,
tickSpacing: 60,
tickSpacing: DEFAULT_TICK_SPACING,
},
},
},
......
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