Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions apps/main/src/dex/components/PoolAlertBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import { createPortal } from 'react-dom'
import { useIsDesktop } from '@ui-kit/hooks/useBreakpoints'
import { useDismissBanner } from '@ui-kit/hooks/useLocalStorage'
import { Banner, BannerProps } from '@ui-kit/shared/ui/Banner'
import { Duration } from '@ui-kit/themes/design/0_primitives'
import { AlertType, PoolAlert } from '../types/main.types'

const ONE_DAY_MS = 24 * 60 * 60 * 1000 // 24 hours in milliseconds

/** Maps AlertType to BannerSeverity */
const alertTypeToBannerSeverity: Record<AlertType, BannerProps['severity']> = {
error: 'alert',
Expand All @@ -25,7 +24,7 @@ export const PoolAlertBanner = ({
poolAlertBannerKey: string
alertType: AlertType
}) => {
const { shouldShowBanner, dismissBanner } = useDismissBanner(poolAlertBannerKey, ONE_DAY_MS)
const { shouldShowBanner, dismissBanner } = useDismissBanner(poolAlertBannerKey, Duration.Banner.Daily)
const isDesktop = useIsDesktop()
// eslint-disable-next-line react-hooks/exhaustive-deps -- isDesktop triggers re-query when header changes (desktop ↔ mobile)
const portalTarget = useMemo(() => document.getElementsByTagName('header')[0], [isDesktop])
Expand Down
3 changes: 2 additions & 1 deletion apps/main/src/dex/store/createPoolsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { convertToLocaleTimestamp } from '@ui-kit/features/candle-chart/utils'
import { requireLib } from '@ui-kit/features/connect-wallet'
import { log } from '@ui-kit/lib/logging'
import { fetchTokenUsdRate, getTokenUsdRateQueryData } from '@ui-kit/lib/model/entities/token-usd-rate'
import { TIME_FRAMES } from '@ui-kit/lib/model/time'
import { fetchNetworks } from '../entities/networks'
import { getPools } from '../lib/pools'

Expand Down Expand Up @@ -526,7 +527,7 @@ const createPoolsSlice = (set: StoreApi<State>['setState'], get: StoreApi<State>
fetchPricesPoolSnapshots: async (chainId: ChainId, poolAddress: string) => {
const networks = await fetchNetworks()
if (networks[chainId].pricesApi) {
const startTime = Math.floor((Date.now() - 24 * 60 * 60 * 1000) / 1000)
const startTime = Math.floor((Date.now() - TIME_FRAMES.DAY_MS) / 1000)
const endTime = Math.floor(Date.now() / 1000)
const network = networks[chainId].id.toLowerCase()

Expand Down
9 changes: 5 additions & 4 deletions apps/main/src/loan/entities/scrvusd-yield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ import { getYield } from '@curvefi/prices-api/savings'
import type { Yield } from '@curvefi/prices-api/savings/models'
import { queryFactory } from '@ui-kit/lib/model/query'
import { timeOptionValidationSuite } from '@ui-kit/lib/model/query/time-option-validation'
import { TIME_FRAMES } from '@ui-kit/lib/model/time'
import type { TimeOption } from '@ui-kit/lib/types/scrvusd'

export type ScrvUsdYieldWithAverages = Yield & { proj_apy_7d_avg: number; proj_apy_total_avg: number }

export const _getScrvUsdYield = async (params: { timeOption: TimeOption }) => {
// calcs starting timestamp
const timeOptionCalc: Record<TimeOption, number> = {
'1M': 30 * 24 * 60 * 60 * 1000, // 30 days
'6M': 180 * 24 * 60 * 60 * 1000, // 180 days
'1Y': 365 * 24 * 60 * 60 * 1000, // 365 days
'1M': 30 * TIME_FRAMES.DAY_MS, // 30 days
'6M': 180 * TIME_FRAMES.DAY_MS, // 180 days
'1Y': 365 * TIME_FRAMES.DAY_MS, // 365 days
}
// sets number of aggregations
const aggNumbers: Record<TimeOption, number> = { '1M': 4, '6M': 16, '1Y': 32 }
Expand All @@ -31,7 +32,7 @@ export const _getScrvUsdYield = async (params: { timeOption: TimeOption }) => {
const totalAverage = array.reduce((sum, curr) => sum + curr.apyProjected, 0) / array.length

// Calculate 7-day moving average
const SEVEN_DAYS_IN_MILLISECONDS = 7 * 24 * 60 * 60 * 1000
const SEVEN_DAYS_IN_MILLISECONDS = 7 * TIME_FRAMES.DAY_MS
const currentTimestamp = item.timestamp.getTime()
const sevenDaysAgoTimestamp = currentTimestamp - SEVEN_DAYS_IN_MILLISECONDS

Expand Down
3 changes: 3 additions & 0 deletions packages/curve-ui-kit/src/lib/model/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ export const REFRESH_INTERVAL = {
} as const

export const TIME_FRAMES = {
DAY_MS: 24 * 60 * 60 * 1000,
WEEK: 7 * 24 * 60 * 60,
MONTH: 30 * 24 * 60 * 60,
MONTH_MS: 30 * 24 * 60 * 60 * 1000,
YEAR_MS: 365 * 24 * 60 * 60 * 1000,
} as const
26 changes: 24 additions & 2 deletions packages/curve-ui-kit/src/shared/ui/GlobalBanner.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useChainId, useConnection, useSwitchChain } from 'wagmi'
import Box from '@mui/material/Box'
import { isFailure, useCurve, type WagmiChainId } from '@ui-kit/features/connect-wallet'
import { useReleaseChannel } from '@ui-kit/hooks/useLocalStorage'
import { usePathname } from '@ui-kit/hooks/router'
import { useDismissBanner, useReleaseChannel } from '@ui-kit/hooks/useLocalStorage'
import { t } from '@ui-kit/lib/i18n'
import { getCurrentApp } from '@ui-kit/shared/routes'
import { Banner } from '@ui-kit/shared/ui/Banner'
import { Duration } from '@ui-kit/themes/design/0_primitives'
import { isCypress, ReleaseChannel } from '@ui-kit/utils'
import { Chain } from '@ui-kit/utils/network'
import { PhishingWarningBanner } from '@ui-kit/widgets/Header/PhishingWarningBanner'

export type GlobalBannerProps = {
Expand All @@ -21,11 +25,18 @@ export const GlobalBanner = ({ networkId, chainId }: GlobalBannerProps) => {
const { switchChain } = useSwitchChain()
const { connectState } = useCurve()
const walletChainId = useChainId()
const pathname = usePathname()
const currentApp = getCurrentApp(pathname)

const showSwitchNetworkMessage = isConnected && chainId && walletChainId != chainId
const showConnectApiErrorMessage = !showSwitchNetworkMessage && isFailure(connectState)

const { shouldShowBanner: showAaveBanner, dismissBanner: dismissAaveBanner } = useDismissBanner(
'aave-v2-frozen-avalanche-polygon',
Duration.Banner.Monthly,
)
return (
<Box>
<PhishingWarningBanner />
{releaseChannel !== ReleaseChannel.Stable && !isCypress && (
<Banner
icon="llama"
Expand All @@ -35,6 +46,7 @@ export const GlobalBanner = ({ networkId, chainId }: GlobalBannerProps) => {
{t`${releaseChannel} Mode Enabled`}
</Banner>
)}
<PhishingWarningBanner />
{maintenanceMessage && <Banner severity="warning">{maintenanceMessage}</Banner>}
{showSwitchNetworkMessage && (
<Banner
Expand All @@ -51,6 +63,16 @@ export const GlobalBanner = ({ networkId, chainId }: GlobalBannerProps) => {
{t`There is an issue connecting to the API. Please try to switch your RPC in your wallet settings.`}
</Banner>
)}
{showAaveBanner && currentApp === 'dex' && [Chain.Polygon, Chain.Avalanche].includes(chainId) && (
<Banner
severity="info"
subtitle={t`Aave is deprecating its V2 markets on Polygon and Avalanche. Deposits and swaps are not supported`}
onClick={dismissAaveBanner}
learnMoreUrl="https://governance.aave.com/t/direct-to-aip-aave-v2-non-ethereum-pools-next-deprecation-steps/22445"
>
{t`Aave V2 Frozen aTokens`}
</Banner>
)}
</Box>
)
}
6 changes: 6 additions & 0 deletions packages/curve-ui-kit/src/themes/design/0_primitives.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { TIME_FRAMES } from '@ui-kit/lib/model/time'

export const Grays = {
'10': '#fdfcfc',
'25': '#fafafa',
Expand Down Expand Up @@ -141,6 +143,10 @@ export const Duration = {
Snackbar: 6000,
Tooltip: { Enter: 500, Exit: 500 },
Transition: 256,
Banner: {
Daily: TIME_FRAMES.DAY_MS,
Monthly: TIME_FRAMES.MONTH_MS,
},
}

export const TransitionFunction = `ease-out ${Duration.Transition}ms`
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { useDismissBanner } from '@ui-kit/hooks/useLocalStorage'
import { t } from '@ui-kit/lib/i18n'
import { Banner } from '@ui-kit/shared/ui/Banner'
import { Duration } from '@ui-kit/themes/design/0_primitives'

const URL = 'https://www.curve.finance'
const ONE_MONTH_MS = 30 * 24 * 60 * 60 * 1000 // 30 days in milliseconds

/**
* Displays a banner warning users about phishing risks and encourages them to verify they are on the official Curve domains.
* The banner will reappear after one month if dismissed.
*/
export const PhishingWarningBanner = () => {
const { shouldShowBanner, dismissBanner } = useDismissBanner('phishing-warning-dismissed', ONE_MONTH_MS)
const { shouldShowBanner, dismissBanner } = useDismissBanner('phishing-warning-dismissed', Duration.Banner.Monthly)

return (
shouldShowBanner && (
Expand Down
7 changes: 3 additions & 4 deletions tests/cypress/e2e/all/header.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
SCROLL_WIDTH,
TABLET_BREAKPOINT,
} from '@cy/support/ui'
import { TIME_FRAMES } from '@ui-kit/lib/model/time'

const expectedMainNavHeight = 56
const expectedSubNavHeight = 42 // 40 + 2px border
Expand All @@ -18,8 +19,6 @@ const expectedFooterXMargin = { mobile: 32, tablet: 48, desktop: 48 }
const expectedFooterMinWidth = 273
const expectedFooterMaxWidth = 1536

const DAY_IN_MS = 24 * 60 * 60 * 1000

describe('Header', () => {
let viewport: readonly [number, number]

Expand Down Expand Up @@ -191,14 +190,14 @@ describe('Header', () => {

it('should reappear after one month', () => {
// Set dismissal date to 31 days ago (more than one month)
const oneMonthAgo = Date.now() - 31 * DAY_IN_MS
const oneMonthAgo = Date.now() - 31 * TIME_FRAMES.DAY_MS
visitWithDismissedBanner(oneMonthAgo)
cy.get("[data-testid='phishing-warning-banner']", LOAD_TIMEOUT).should('be.visible')
})

it('should remain hidden within one month', () => {
// Set dismissal date to 15 days ago (less than one month)
const fifteenDaysAgo = Date.now() - 15 * DAY_IN_MS
const fifteenDaysAgo = Date.now() - 15 * TIME_FRAMES.DAY_MS
visitWithDismissedBanner(fifteenDaysAgo)
cy.get("[data-testid='phishing-warning-banner']", LOAD_TIMEOUT).should('not.exist')
})
Expand Down
3 changes: 2 additions & 1 deletion tests/cypress/support/generators.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Address } from '@curvefi/prices-api'
import { range, recordValues } from '@curvefi/prices-api/objects.util'
import { TIME_FRAMES } from '@ui-kit/lib/model/time'

export const MAX_USD_VALUE = 400_000_000

Expand Down Expand Up @@ -36,7 +37,7 @@ export const oneTokenType = () => oneOf('collateral', 'borrowed')
export type TokenType = ReturnType<typeof oneTokenType>

export const oneDate = ({
minDate = new Date(Date.now() - 365 * 24 * 60 * 60 * 1000), // 1 year ago
minDate = new Date(Date.now() - TIME_FRAMES.YEAR_MS), // 1 year ago
maxDate = new Date(Date.now()),
}: {
minDate?: Date
Expand Down
Loading