ci(release): publish latest release

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