From 3230673dc9570dab1f21a97e93a11424b7247855 Mon Sep 17 00:00:00 2001 From: Neven Date: Fri, 25 Oct 2024 20:42:44 +0200 Subject: [PATCH] CP-9383: Mobile - unable to stake on prod build (#2026 --- packages/core-mobile/app/hooks/time/useNow.ts | 7 +- .../EarnScreenStack/StakeSetupScreenStack.tsx | 9 ++- .../earn/Confirmation/Confirmation.tsx | 9 ++- .../Confirmation/useValidateStakingEndTime.ts | 25 ++++--- .../app/screens/earn/StakeDetails.tsx | 5 +- .../app/screens/earn/components/NodeCard.tsx | 3 +- .../app/screens/earn/components/StakeCard.tsx | 3 +- .../earn/durationScreen/DurationScreen.tsx | 56 +++++++------- .../components/CustomDurationOptionItem.tsx | 35 +++++---- .../app/services/earn/getStakeEndDate.test.ts | 73 +++++++++++++++++++ .../app/services/earn/getStakeEndDate.ts | 41 +++++++---- .../core-mobile/app/services/earn/types.ts | 1 + .../core-mobile/app/services/earn/utils.ts | 9 ++- .../app/utils/date/getReadableDateDuration.ts | 8 +- packages/core-mobile/package.json | 3 +- yarn.lock | 18 +++-- 16 files changed, 218 insertions(+), 87 deletions(-) create mode 100644 packages/core-mobile/app/services/earn/getStakeEndDate.test.ts diff --git a/packages/core-mobile/app/hooks/time/useNow.ts b/packages/core-mobile/app/hooks/time/useNow.ts index 6d8d73c63f..c9e681ce01 100644 --- a/packages/core-mobile/app/hooks/time/useNow.ts +++ b/packages/core-mobile/app/hooks/time/useNow.ts @@ -1,13 +1,14 @@ import { useEffect, useState } from 'react' import { Seconds } from 'types/siUnits' +import { UnixTimeMs } from 'services/earn/types' const tenSeconds = Seconds(10) -export const useNow = (refreshInterval: Seconds = tenSeconds) => { - const [now, setNow] = useState(new Date()) +export const useNow = (refreshInterval: Seconds = tenSeconds): UnixTimeMs => { + const [now, setNow] = useState(Date.now()) useEffect(() => { const intervalId = setInterval(() => { - setNow(new Date()) + setNow(Date.now()) }, Number(refreshInterval * 1000n)) return () => { diff --git a/packages/core-mobile/app/navigation/wallet/EarnScreenStack/StakeSetupScreenStack.tsx b/packages/core-mobile/app/navigation/wallet/EarnScreenStack/StakeSetupScreenStack.tsx index f2c7fb34cb..a138d475d9 100644 --- a/packages/core-mobile/app/navigation/wallet/EarnScreenStack/StakeSetupScreenStack.tsx +++ b/packages/core-mobile/app/navigation/wallet/EarnScreenStack/StakeSetupScreenStack.tsx @@ -16,30 +16,31 @@ import { BackButton } from 'components/BackButton' import { FundsStuckModal } from 'screens/earn/FundsStuckModal' import { handleStakeConfirmationGoBack } from 'utils/earn/handleStakeConfirmationGoBack' import AnalyticsService from 'services/analytics/AnalyticsService' +import { UTCDate } from '@date-fns/utc' export type StakeSetupStackParamList = { [AppNavigation.StakeSetup.GetStarted]: undefined [AppNavigation.StakeSetup.SmartStakeAmount]: undefined [AppNavigation.StakeSetup.StakingDuration]: { stakingAmount: Avax } [AppNavigation.StakeSetup.AdvancedStaking]: { - stakingEndTime: Date + stakingEndTime: UTCDate stakingAmount: Avax selectedDuration: string } [AppNavigation.StakeSetup.SelectNode]: { - stakingEndTime: Date + stakingEndTime: UTCDate stakingAmount: Avax minUpTime?: number maxFee?: number } [AppNavigation.StakeSetup.NodeSearch]: { - stakingEndTime: Date + stakingEndTime: UTCDate stakingAmount: Avax } [AppNavigation.StakeSetup.Confirmation]: { nodeId: string stakingAmount: Avax - stakingEndTime: Date + stakingEndTime: UTCDate onBack?: () => void } [AppNavigation.StakeSetup.Cancel]: undefined diff --git a/packages/core-mobile/app/screens/earn/Confirmation/Confirmation.tsx b/packages/core-mobile/app/screens/earn/Confirmation/Confirmation.tsx index a8a2b32a09..09196ae2e5 100644 --- a/packages/core-mobile/app/screens/earn/Confirmation/Confirmation.tsx +++ b/packages/core-mobile/app/screens/earn/Confirmation/Confirmation.tsx @@ -81,8 +81,15 @@ export const Confirmation = (): JSX.Element | null => { deductedStakingAmount = stakingAmount } + const validatorEndTimeUnix = useMemo(() => { + if (validator?.endTime) { + return Number(validator?.endTime) + } + return 0 + }, [validator?.endTime]) + const { minStartTime, validatedStakingEndTime, validatedStakingDuration } = - useValidateStakingEndTime(stakingEndTime, validator?.endTime ?? '') + useValidateStakingEndTime(stakingEndTime, validatorEndTimeUnix) const { data } = useEarnCalcEstimatedRewards({ amount: deductedStakingAmount, diff --git a/packages/core-mobile/app/screens/earn/Confirmation/useValidateStakingEndTime.ts b/packages/core-mobile/app/screens/earn/Confirmation/useValidateStakingEndTime.ts index 5b6af5571c..293f1fc758 100644 --- a/packages/core-mobile/app/screens/earn/Confirmation/useValidateStakingEndTime.ts +++ b/packages/core-mobile/app/screens/earn/Confirmation/useValidateStakingEndTime.ts @@ -5,24 +5,27 @@ import { useNow } from 'hooks/time/useNow' import { getMinimumStakeDurationMs } from 'services/earn/utils' import { selectIsDeveloperMode } from 'store/settings/advanced' import { MilliSeconds, Seconds, convertToSeconds } from 'types/siUnits' +import { UTCDate } from '@date-fns/utc' +import { UnixTime } from 'services/earn/types' +import { utc } from '@date-fns/utc/utc' export const useValidateStakingEndTime = ( - stakingEndTime: Date, - validatorEndTimeStr: string + stakingEndTime: UTCDate, + validatorEndTimeStr: UnixTime ): { - minStartTime: Date - validatedStakingEndTime: Date + minStartTime: UTCDate + validatedStakingEndTime: UTCDate validatedStakingDuration: Seconds } => { - const now = useNow() + const currentUnixMs = useNow() const isDeveloperMode = useSelector(selectIsDeveloperMode) const minStakeDurationMs = getMinimumStakeDurationMs(isDeveloperMode) // minStartTime - 1 minute after submitting const minStartTime = useMemo(() => { - return addMinutes(now, 1) - }, [now]) + return addMinutes(new UTCDate(currentUnixMs), 1) + }, [currentUnixMs]) const validatedStakingEndTime = useMemo(() => { // check if stake duration is less than minimum, and adjust if necessary @@ -31,12 +34,12 @@ export const useValidateStakingEndTime = ( stakingEndTime.getTime() - minStakeDurationMs < minStartTime.getTime() ) { - return new Date(minStartTime.getTime() + minStakeDurationMs) + return new UTCDate(minStartTime.getTime() + minStakeDurationMs) } // check if stake duration is more than validator's end time, // use validator's end time if it is - const validatorEndTime = fromUnixTime(Number(validatorEndTimeStr)) + const validatorEndTime = fromUnixTime(validatorEndTimeStr, { in: utc }) if (stakingEndTime > validatorEndTime) { return validatorEndTime @@ -49,10 +52,10 @@ export const useValidateStakingEndTime = ( () => convertToSeconds( BigInt( - validatedStakingEndTime.getTime() - now.getTime() + validatedStakingEndTime.getTime() - currentUnixMs ) as MilliSeconds ), - [now, validatedStakingEndTime] + [currentUnixMs, validatedStakingEndTime] ) return { minStartTime, validatedStakingEndTime, validatedStakingDuration } diff --git a/packages/core-mobile/app/screens/earn/StakeDetails.tsx b/packages/core-mobile/app/screens/earn/StakeDetails.tsx index 29a1140458..b146f0067c 100644 --- a/packages/core-mobile/app/screens/earn/StakeDetails.tsx +++ b/packages/core-mobile/app/screens/earn/StakeDetails.tsx @@ -22,6 +22,7 @@ import { RewardType } from '@avalabs/glacier-sdk' import { isOnGoing } from 'utils/earn/status' import { estimatesTooltipText } from 'consts/earn' import { Tooltip } from 'components/Tooltip' +import { UTCDate } from '@date-fns/utc' import { StatusChip } from './components/StatusChip' import { StakeProgress } from './components/StakeProgress' @@ -79,7 +80,9 @@ const StakeDetails = (): JSX.Element | null => { const renderActiveDetails = (): JSX.Element => { const formattedEndDate = format(endDate, 'LLLL d, yyyy, H:mm aa') - const remainingTime = humanize(getReadableDateDuration(endDate)) + const remainingTime = humanize( + getReadableDateDuration(new UTCDate(endDate.getTime())) + ) const [estimatedRewardInAvax, estimatedRewardInCurrency] = nAvaxFormatter( stake.estimatedReward, true diff --git a/packages/core-mobile/app/screens/earn/components/NodeCard.tsx b/packages/core-mobile/app/screens/earn/components/NodeCard.tsx index 68d83f567b..3f4142251a 100644 --- a/packages/core-mobile/app/screens/earn/components/NodeCard.tsx +++ b/packages/core-mobile/app/screens/earn/components/NodeCard.tsx @@ -23,6 +23,7 @@ import { Avax } from 'types/Avax' import { useSelector } from 'react-redux' import { selectIsDeveloperMode } from 'store/settings/advanced' import { usePeers } from 'hooks/earn/usePeers' +import { UTCDate } from '@date-fns/utc' import { PopableContentWithCaption } from './PopableContentWithCaption' type NavigationProp = StakeSetupScreenProps< @@ -36,7 +37,7 @@ export const NodeCard = ({ }: { data: NodeValidator stakingAmount: Avax - stakingEndTime: Date + stakingEndTime: UTCDate }): JSX.Element => { const { theme } = useApplicationContext() const [isCardExpanded, setIsCardExpanded] = useState(false) diff --git a/packages/core-mobile/app/screens/earn/components/StakeCard.tsx b/packages/core-mobile/app/screens/earn/components/StakeCard.tsx index d7ef07e0be..472a7ac968 100644 --- a/packages/core-mobile/app/screens/earn/components/StakeCard.tsx +++ b/packages/core-mobile/app/screens/earn/components/StakeCard.tsx @@ -16,6 +16,7 @@ import { TabsScreenProps } from 'navigation/types' import AppNavigation from 'navigation/AppNavigation' import { estimatesTooltipText } from 'consts/earn' import { Tooltip } from 'components/Tooltip' +import { UTCDate } from '@date-fns/utc' import { StatusChip } from './StatusChip' type BaseProps = { @@ -60,7 +61,7 @@ export const StakeCard = (props: Props): JSX.Element => { switch (status) { case StakeStatus.Ongoing: { const remainingTime = getReadableDateDuration( - fromUnixTime(props.endTimestamp || 0) + new UTCDate(props.endTimestamp || 0) ) return ( diff --git a/packages/core-mobile/app/screens/earn/durationScreen/DurationScreen.tsx b/packages/core-mobile/app/screens/earn/durationScreen/DurationScreen.tsx index 0bd13dfb39..0598cc9409 100644 --- a/packages/core-mobile/app/screens/earn/durationScreen/DurationScreen.tsx +++ b/packages/core-mobile/app/screens/earn/durationScreen/DurationScreen.tsx @@ -26,6 +26,9 @@ import { BackButton } from 'components/BackButton' import { Tooltip } from 'components/Tooltip' import InfoSVG from 'components/svg/InfoSVG' import AnalyticsService from 'services/analytics/AnalyticsService' +import { fromUnixTime, getUnixTime } from 'date-fns' +import { UTCDate } from '@date-fns/utc' +import { UnixTime } from 'services/earn/types' import { CustomDurationOptionItem } from './components/CustomDurationOptionItem' import { DurationOptionItem } from './components/DurationOptionItem' @@ -35,35 +38,36 @@ type ScreenProps = StakeSetupScreenProps< export const StakingDuration = (): JSX.Element => { const isDeveloperMode = useSelector(selectIsDeveloperMode) - const currentDate = useNow() + const currentUnix = useNow() / 1000 const minDelegationTime = isDeveloperMode ? ONE_DAY : TWO_WEEKS const [selectedDuration, setSelectedDuration] = useState(minDelegationTime) - const [stakeEndTime, setStakeEndTime] = useState( - getStakeEndDate( - currentDate, - minDelegationTime.stakeDurationFormat, - minDelegationTime.stakeDurationValue, - isDeveloperMode - ) + const [stakeEndTime, setStakeEndTime] = useState( + getStakeEndDate({ + startDateUnix: currentUnix, + stakeDurationFormat: minDelegationTime.stakeDurationFormat, + stakeDurationValue: minDelegationTime.stakeDurationValue, + isDeveloperMode: isDeveloperMode + }) ) const { theme } = useApplicationContext() const { navigate, setOptions, goBack } = useNavigation() const { stakingAmount } = useRoute().params const isNextDisabled = - stakeEndTime === undefined || (stakeEndTime && stakeEndTime < new Date()) + stakeEndTime === undefined || + (!!stakeEndTime && stakeEndTime < UTCDate.now() / 1000) const selectMinDuration = useCallback(() => { setSelectedDuration(minDelegationTime) - const calculatedStakeEndTime = getStakeEndDate( - currentDate, - minDelegationTime.stakeDurationFormat, - minDelegationTime.stakeDurationValue, - isDeveloperMode - ) + const calculatedStakeEndTime = getStakeEndDate({ + startDateUnix: currentUnix, + stakeDurationFormat: minDelegationTime.stakeDurationFormat, + stakeDurationValue: minDelegationTime.stakeDurationValue, + isDeveloperMode: isDeveloperMode + }) setStakeEndTime(calculatedStakeEndTime) - }, [currentDate, isDeveloperMode, minDelegationTime]) + }, [currentUnix, isDeveloperMode, minDelegationTime]) useLayoutEffect(() => { const isCustomSelected = @@ -88,18 +92,18 @@ export const StakingDuration = (): JSX.Element => { } setSelectedDuration(durationOption) - const calculatedStakeEndTime = getStakeEndDate( - currentDate, - durationOption.stakeDurationFormat, - durationOption.stakeDurationValue, - isDeveloperMode - ) + const calculatedStakeEndTime = getStakeEndDate({ + startDateUnix: currentUnix, + stakeDurationFormat: durationOption.stakeDurationFormat, + stakeDurationValue: durationOption.stakeDurationValue, + isDeveloperMode: isDeveloperMode + }) setStakeEndTime(calculatedStakeEndTime) } const handleDateConfirm = (dateInput: Date): void => { setSelectedDuration(CUSTOM) - setStakeEndTime(dateInput) + setStakeEndTime(getUnixTime(dateInput)) } const navigateToNodeSearch = (): void => { @@ -110,7 +114,7 @@ export const StakingDuration = (): JSX.Element => { }) navigate(AppNavigation.StakeSetup.NodeSearch, { stakingAmount, - stakingEndTime: stakeEndTime + stakingEndTime: new UTCDate(stakeEndTime * 1000) }) } } @@ -120,7 +124,7 @@ export const StakingDuration = (): JSX.Element => { AnalyticsService.capture('StakeSelectAdvancedStaking') navigate(AppNavigation.StakeSetup.AdvancedStaking, { stakingAmount, - stakingEndTime: stakeEndTime, + stakingEndTime: new UTCDate(stakeEndTime * 1000), selectedDuration: selectedDuration.title }) } @@ -154,7 +158,7 @@ export const StakingDuration = (): JSX.Element => { const renderCustomOption = (): JSX.Element => ( diff --git a/packages/core-mobile/app/screens/earn/durationScreen/components/CustomDurationOptionItem.tsx b/packages/core-mobile/app/screens/earn/durationScreen/components/CustomDurationOptionItem.tsx index beca6704ec..90f06a27cc 100644 --- a/packages/core-mobile/app/screens/earn/durationScreen/components/CustomDurationOptionItem.tsx +++ b/packages/core-mobile/app/screens/earn/durationScreen/components/CustomDurationOptionItem.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useMemo, useState } from 'react' import { useApplicationContext } from 'contexts/ApplicationContext' import { useEarnCalcEstimatedRewards } from 'hooks/earn/useEarnCalcEstimatedRewards' import { @@ -24,6 +24,7 @@ import { differenceInMilliseconds } from 'date-fns' import { Avax } from 'types/Avax' import { useNow } from 'hooks/time/useNow' import { useAvaxFormatter } from 'hooks/formatter/useAvaxFormatter' +import { UTCDate } from '@date-fns/utc' export const CustomDurationOptionItem = ({ stakeAmount, @@ -32,34 +33,38 @@ export const CustomDurationOptionItem = ({ handleDateConfirm }: { stakeAmount: Avax - stakeEndTime: Date + stakeEndTime: UTCDate onRadioSelect: (item: DurationOption) => void - handleDateConfirm: (dateInput: Date) => void -}) => { + handleDateConfirm: (dateInput: UTCDate) => void +}): JSX.Element => { const avaxFormatter = useAvaxFormatter() const isDeveloperMode = useSelector(selectIsDeveloperMode) const [isDatePickerVisible, setIsDatePickerVisible] = useState(false) - const currentDate = useNow() + const currentUnix = useNow() const { theme } = useApplicationContext() const minDelegationTime = isDeveloperMode ? ONE_DAY : TWO_WEEKS + const stakeEndTimeLocal = useMemo( + () => new Date(stakeEndTime.getTime()), + [stakeEndTime] + ) const minimumStakeEndDate = getMinimumStakeEndTime( isDeveloperMode, - currentDate + new UTCDate(currentUnix) ) const maximumStakeEndDate = getMaximumStakeEndDate() - const stakeDurationUnixMs = differenceInMilliseconds( + const stakeDurationMs = differenceInMilliseconds( stakeEndTime, - currentDate + new UTCDate(currentUnix) ) - const stakeDurationUnixSec = convertToSeconds( - BigInt(stakeDurationUnixMs) as MilliSeconds + const stakeDurationSec = convertToSeconds( + BigInt(stakeDurationMs) as MilliSeconds ) const { data } = useEarnCalcEstimatedRewards({ amount: stakeAmount, - duration: stakeDurationUnixSec, + duration: stakeDurationSec, delegationFee: 2 }) @@ -68,6 +73,10 @@ export const CustomDurationOptionItem = ({ true ) + function handleDateSelected(date: Date): void { + handleDateConfirm(new UTCDate(date.getTime())) + } + return ( ({ + getMinimumStakeEndTime: jest.fn() +})) + +describe('getStakeEndDate', () => { + const startDateUnix = 1729807200 // Thu Oct 24 2024 22:00:00 UTC + const currentDate = fromUnixTime(startDateUnix) // Date representation in UTC + + it('calculates end date in days', () => { + const result = getStakeEndDate({ + startDateUnix, + stakeDurationFormat: StakeDurationFormat.Day, + stakeDurationValue: 1, + isDeveloperMode: false + }) + expect(result).toBe(startDateUnix + 24 * 60 * 60) // Thu Oct 25 2024 22:00:00 UTC + }) + + it('calculates end date in weeks', () => { + const result = getStakeEndDate({ + startDateUnix, + stakeDurationFormat: StakeDurationFormat.Week, + stakeDurationValue: 2, + isDeveloperMode: false + }) + expect(result).toBe(startDateUnix + 2 * 7 * 24 * 60 * 60) //Thu Nov 07 2024 22:00:00 UTC + }) + + it('calculates end date in months', () => { + const result = getStakeEndDate({ + startDateUnix, + stakeDurationFormat: StakeDurationFormat.Month, + stakeDurationValue: 2, + isDeveloperMode: false + }) + expect(result).toBe(1735077600) // Tue, 24 Dec 2024 22:00:00 GMT + }) + + it('calculates end date in years', () => { + const result = getStakeEndDate({ + startDateUnix, + stakeDurationFormat: StakeDurationFormat.Year, + stakeDurationValue: 2, + isDeveloperMode: false + }) + expect(result).toBe(1792879200) //Sat Oct 24 2026 22:00:00 GMT+0000 + }) + + it('calculates end date for custom duration with developer mode', () => { + const mockCustomEndDate = getUnixTime(addDays(currentDate, 10)) // Custom example for mock return + ;(getMinimumStakeEndTime as jest.Mock).mockReturnValueOnce( + fromUnixTime(mockCustomEndDate) + ) + + const result = getStakeEndDate({ + startDateUnix, + stakeDurationFormat: StakeDurationFormat.Custom, + stakeDurationValue: 0, // Value may not be used in Custom format + isDeveloperMode: true + }) + + expect(result).toBe(mockCustomEndDate) + expect(getMinimumStakeEndTime).toHaveBeenCalledWith(true, currentDate) + }) +}) diff --git a/packages/core-mobile/app/services/earn/getStakeEndDate.ts b/packages/core-mobile/app/services/earn/getStakeEndDate.ts index 7154db8d6f..f6913409a5 100644 --- a/packages/core-mobile/app/services/earn/getStakeEndDate.ts +++ b/packages/core-mobile/app/services/earn/getStakeEndDate.ts @@ -1,24 +1,39 @@ -import { addDays, addMonths, addWeeks, addYears } from 'date-fns' -import { UnixTimeMs } from 'services/earn/types' +import { + addDays, + addMonths, + addWeeks, + addYears, + fromUnixTime, + getUnixTime +} from 'date-fns' +import { UnixTime, UnixTimeMs } from 'services/earn/types' +import { utc } from '@date-fns/utc/utc' +import { UTCDate } from '@date-fns/utc' import { getMinimumStakeEndTime } from './utils' -export const getStakeEndDate = ( - curentDate: Date, - stakeDurationFormat: StakeDurationFormat, - stakeDurationValue: number, +export const getStakeEndDate = ({ + startDateUnix, + stakeDurationFormat, + stakeDurationValue, + isDeveloperMode +}: { + startDateUnix: UnixTime + stakeDurationFormat: StakeDurationFormat + stakeDurationValue: number isDeveloperMode: boolean -) => { +}): UnixTimeMs => { + const currentDate = fromUnixTime(startDateUnix, { in: utc }) switch (stakeDurationFormat) { case StakeDurationFormat.Day: - return addDays(curentDate, stakeDurationValue) + return getUnixTime(addDays(currentDate, stakeDurationValue)) case StakeDurationFormat.Week: - return addWeeks(curentDate, stakeDurationValue) + return getUnixTime(addWeeks(currentDate, stakeDurationValue)) case StakeDurationFormat.Month: - return addMonths(curentDate, stakeDurationValue) + return getUnixTime(addMonths(currentDate, stakeDurationValue)) case StakeDurationFormat.Year: - return addYears(curentDate, stakeDurationValue) + return getUnixTime(addYears(currentDate, stakeDurationValue)) case StakeDurationFormat.Custom: - return getMinimumStakeEndTime(isDeveloperMode, curentDate) + return getUnixTime(getMinimumStakeEndTime(isDeveloperMode, currentDate)) } } export const getStakeDuration = ( @@ -26,7 +41,7 @@ export const getStakeDuration = ( stakeDurationValue: number, isDeveloperMode: boolean ): UnixTimeMs => { - const currentTime = new Date() + const currentTime = new UTCDate() const currentTimeUnix = currentTime.getTime() switch (stakeDurationFormat) { case StakeDurationFormat.Day: diff --git a/packages/core-mobile/app/services/earn/types.ts b/packages/core-mobile/app/services/earn/types.ts index 76801f7f92..4f89ca0167 100644 --- a/packages/core-mobile/app/services/earn/types.ts +++ b/packages/core-mobile/app/services/earn/types.ts @@ -15,6 +15,7 @@ export type AddDelegatorTransactionProps = { } export type UnixTimeMs = number +export type UnixTime = number export type CollectTokensForStakingParams = { cChainBalance: Avax diff --git a/packages/core-mobile/app/services/earn/utils.ts b/packages/core-mobile/app/services/earn/utils.ts index 62d1b04906..ee63203cf7 100644 --- a/packages/core-mobile/app/services/earn/utils.ts +++ b/packages/core-mobile/app/services/earn/utils.ts @@ -17,6 +17,7 @@ import Logger from 'utils/Logger' import { valid, compare } from 'semver' import { Peer } from '@avalabs/avalanchejs/dist/info/model' import { PChainTransaction } from '@avalabs/glacier-sdk' +import { UTCDate } from '@date-fns/utc' import EarnService from './EarnService' // the max num of times we should check transaction status @@ -44,15 +45,15 @@ export const getMinimumStakeDurationMs = (isDeveloperMode: boolean): number => { export const getMinimumStakeEndTime = ( isDeveloperMode: boolean, - stakeStartTime: Date -): Date => { + stakeStartTime: UTCDate +): UTCDate => { return isDeveloperMode ? add(stakeStartTime, { hours: 24 }) : add(stakeStartTime, { weeks: 2 }) } -export const getMaximumStakeEndDate = (): Date => { - return addYears(new Date(), 1) +export const getMaximumStakeEndDate = (): UTCDate => { + return addYears(new UTCDate(), 1) } /** diff --git a/packages/core-mobile/app/utils/date/getReadableDateDuration.ts b/packages/core-mobile/app/utils/date/getReadableDateDuration.ts index e16923f343..6adab5a137 100644 --- a/packages/core-mobile/app/utils/date/getReadableDateDuration.ts +++ b/packages/core-mobile/app/utils/date/getReadableDateDuration.ts @@ -4,6 +4,8 @@ import { formatDuration, intervalToDuration } from 'date-fns' +import { UTCDate } from '@date-fns/utc' +import type { DurationUnit } from 'date-fns/types' /** * @@ -11,10 +13,10 @@ import { * @returns duration in different format that is more readable * e.g. 2 months 3 days | 10 hours 50 minutes | 2 years 5 months */ -export const getReadableDateDuration = (date: Date) => { - const currentDate = new Date() +export const getReadableDateDuration = (date: UTCDate): string => { + const currentDate = new UTCDate() const duration = intervalToDuration({ start: currentDate, end: date }) - let format = ['months', 'days'] + let format: DurationUnit[] = ['months', 'days'] if (differenceInYears(date, currentDate) > 1) { format = ['years', 'months'] diff --git a/packages/core-mobile/package.json b/packages/core-mobile/package.json index 41007dd119..3810a719e2 100644 --- a/packages/core-mobile/package.json +++ b/packages/core-mobile/package.json @@ -41,6 +41,7 @@ "@cubist-labs/cubesigner-sdk-ethers-v6": "0.3.29", "@datadog/mobile-react-native": "2.4.3", "@datadog/mobile-react-navigation": "2.4.3", + "@date-fns/utc": "2.1.0", "@ethereumjs/common": "4.4.0", "@ethereumjs/tx": "5.4.0", "@gorhom/bottom-sheet": "4.6.4", @@ -96,7 +97,7 @@ "browserify-zlib": "0.2.0", "camelcase-keys": "9.1.3", "crypto-browserify": "3.12.0", - "date-fns": "2.28.0", + "date-fns": "4.1.0", "deprecated-react-native-prop-types": "5.0.0", "es6-promise": "4.2.8", "ethers": "6.8.1", diff --git a/yarn.lock b/yarn.lock index 3050f1ca9f..9e1f44a95c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -181,6 +181,7 @@ __metadata: "@cubist-labs/cubesigner-sdk-ethers-v6": 0.3.29 "@datadog/mobile-react-native": 2.4.3 "@datadog/mobile-react-navigation": 2.4.3 + "@date-fns/utc": 2.1.0 "@dlenroc/testrail": 1.9.1 "@ethereumjs/common": 4.4.0 "@ethereumjs/tx": 5.4.0 @@ -266,7 +267,7 @@ __metadata: browserify-zlib: 0.2.0 camelcase-keys: 9.1.3 crypto-browserify: 3.12.0 - date-fns: 2.28.0 + date-fns: 4.1.0 deprecated-react-native-prop-types: 5.0.0 detox: 20.27.0 es6-promise: 4.2.8 @@ -3351,6 +3352,13 @@ __metadata: languageName: node linkType: hard +"@date-fns/utc@npm:2.1.0": + version: 2.1.0 + resolution: "@date-fns/utc@npm:2.1.0" + checksum: 924ca45afa948a6f618ade184e490deb6d0cfdaab40ac833afd409cf5fc0b09cfab14e37c4503a790711178a5e27e2a68172f15208cd9e0542d635aa24ab4113 + languageName: node + linkType: hard + "@dlenroc/testrail@npm:1.9.1": version: 1.9.1 resolution: "@dlenroc/testrail@npm:1.9.1" @@ -14227,10 +14235,10 @@ __metadata: languageName: node linkType: hard -"date-fns@npm:2.28.0": - version: 2.28.0 - resolution: "date-fns@npm:2.28.0" - checksum: a0516b2e4f99b8bffc6cc5193349f185f195398385bdcaf07f17c2c4a24473c99d933eb0018be4142a86a6d46cb0b06be6440ad874f15e795acbedd6fd727a1f +"date-fns@npm:4.1.0": + version: 4.1.0 + resolution: "date-fns@npm:4.1.0" + checksum: fb681b242cccabed45494468f64282a7d375ea970e0adbcc5dcc92dcb7aba49b2081c2c9739d41bf71ce89ed68dd73bebfe06ca35129490704775d091895710b languageName: node linkType: hard