Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(synapse-interface): upgrade maintenance #2844

Merged
merged 33 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cb78b35
Fetch and store Maintenance data
bigboydiamonds Jul 3, 2024
ea38d0a
Construct maintenance lists from fetched data
bigboydiamonds Jul 3, 2024
46b4da6
Use state to construct Maintenance components
bigboydiamonds Jul 3, 2024
035b4b2
Maintenance components working except for countdown
bigboydiamonds Jul 3, 2024
f4265eb
useMaintenance exports bridge maintenance components
bigboydiamonds Jul 3, 2024
31afdaf
Add Swap maintenance components in useMaintenance
bigboydiamonds Jul 3, 2024
82011ef
Maintenance Banners exported from useMaintenance
bigboydiamonds Jul 3, 2024
a7889f5
Clean
bigboydiamonds Jul 3, 2024
b828354
Text size in Progress Bar
bigboydiamonds Jul 3, 2024
deb0979
Prevent active chain pause clashes between diff instances
bigboydiamonds Jul 3, 2024
d189123
Clean
bigboydiamonds Jul 3, 2024
10c42f9
Use fetched paused bridge module list in Bridge
bigboydiamonds Jul 3, 2024
22bd327
Clean
bigboydiamonds Jul 3, 2024
dc959c6
Organize
bigboydiamonds Jul 4, 2024
f9a72a3
Type Maintenance State
bigboydiamonds Jul 5, 2024
0ca2bf9
Lift isFetching status to store
bigboydiamonds Jul 5, 2024
290b597
Organize
bigboydiamonds Jul 5, 2024
1a994f9
Add tests for fetchJSONData, implement retries
bigboydiamonds Jul 5, 2024
a93298d
Use local data source as backup when fetch retries fail
bigboydiamonds Jul 5, 2024
62589b3
useMaintenanceListener
bigboydiamonds Jul 5, 2024
5ed91a9
Clean, fix current chain pause selection when only single pause exists
bigboydiamonds Jul 5, 2024
a693483
Clean, use jsdelivr urls
bigboydiamonds Jul 5, 2024
fb9968d
Revert package.json, remove jest command
bigboydiamonds Jul 6, 2024
69422be
Resolve file name
bigboydiamonds Jul 6, 2024
ad9f4e4
Add randomized query params to cache bust git rest api
bigboydiamonds Jul 8, 2024
3306ae7
Test URLs
bigboydiamonds Jul 8, 2024
7fbaefc
Revert changes
bigboydiamonds Jul 9, 2024
43c7ef6
No cache fetch request, update to gh pages cdn url
bigboydiamonds Jul 11, 2024
af40763
Merge branch 'master' into fe/maintenance-read-method
bigboydiamonds Jul 18, 2024
4a0dc8d
yarn
bigboydiamonds Jul 18, 2024
b48c8fc
useSynapsePauseData hook (#2921)
bigboydiamonds Jul 25, 2024
c842633
Merge branch 'master' into fe/maintenance-read-method
bigboydiamonds Jul 25, 2024
88b7a9a
Merge branch 'fe/maintenance-read-method' of https://github.com/synap…
bigboydiamonds Jul 25, 2024
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
306 changes: 158 additions & 148 deletions packages/synapse-interface/components/Maintenance/Maintenance.tsx
Original file line number Diff line number Diff line change
@@ -1,189 +1,199 @@
import { MaintenanceBanner } from './components/MaintenanceBanner'
import { MaintenanceWarningMessage } from './components/MaintenanceWarningMessage'
import { useMaintenanceCountdownProgress } from './components/useMaintenanceCountdownProgress'
import { useEventCountdownProgressBar } from './components/EventCountdownProgressBar'
import { useBridgeState } from '@/slices/bridge/hooks'
import { useSwapState } from '@/slices/swap/hooks'
import pausedChains from '@/public/pauses/v1/paused-chains.json'
import pausedBridgeModules from '@/public/pauses/v1/paused-bridge-modules.json'
import { useMaintenanceState } from '@/slices/maintenance/hooks'
import { isChainIncluded } from '@/utils/isChainIncluded'
import { isValidBridgeModule } from './functions/isValidBridgeModule'
import { getFurthestFutureDate } from './functions/getFurthestFutureDate'

/** Pause Chain Activity */
interface ChainPause {
id: string
pausedFromChains: number[]
pausedToChains: number[]
pauseBridge: boolean
pauseSwap: boolean
startTimePauseChain: Date
endTimePauseChain: Date | null // If null, pause indefinitely
endTimePauseChain: Date | null // If null, pause chain indefinitely.
startTimeBanner: Date
endTimeBanner: Date | null // If null, pause indefinitely
inputWarningMessage: JSX.Element
endTimeBanner: Date | null // If null, persist banner indefinitely.
inputWarningMessage: string
bannerMessage: JSX.Element
progressBarMessage: JSX.Element
progressBarMessage: string
bigboydiamonds marked this conversation as resolved.
Show resolved Hide resolved
disableBanner: boolean
disableWarning: boolean
disableCountdown: boolean
}

const PAUSED_CHAINS: ChainPause[] = pausedChains.map((pause) => {
interface BridgeModulePause {
chainId?: number // If undefined, pause bridge module for all chains.
bridgeModuleName: 'SynapseBridge' | 'SynapseRFQ' | 'SynapseCCTP' | 'ALL'
}

const useMaintenanceData = () => {
const { pausedChainsData, pausedModulesData } = useMaintenanceState()

const pausedChainsList: ChainPause[] = pausedChainsData
? pausedChainsData?.map((pause: ChainPause) => {
return {
...pause,
startTimeBanner: new Date(pause.startTimeBanner),
endTimeBanner: pause.endTimeBanner
? new Date(pause.endTimeBanner)
: null,
startTimePauseChain: new Date(pause.startTimePauseChain),
endTimePauseChain: pause.endTimePauseChain
? new Date(pause.endTimePauseChain)
: null,
bannerMessage: <p className="text-left">{pause.bannerMessage}</p>,
inputWarningMessage: pause.inputWarningMessage,
progressBarMessage: pause.progressBarMessage,
}
})
: []
bigboydiamonds marked this conversation as resolved.
Show resolved Hide resolved

const pausedModulesList: BridgeModulePause[] = pausedModulesData
? pausedModulesData?.map((route: BridgeModulePause) => {
if (!isValidBridgeModule(route.bridgeModuleName)) {
throw new Error(`Invalid module type: ${route.bridgeModuleName}`)
}

return {
...route,
bridgeModuleName: route.bridgeModuleName as
| 'SynapseBridge'
| 'SynapseRFQ'
| 'SynapseCCTP'
| 'ALL',
}
})
: []

return {
...pause,
startTimePauseChain: new Date(pause.startTimePauseChain),
endTimePauseChain: pause.endTimePauseChain
? new Date(pause.endTimePauseChain)
: null,
startTimeBanner: new Date(pause.startTimeBanner),
endTimeBanner: pause.endTimeBanner ? new Date(pause.endTimeBanner) : null,
inputWarningMessage: <p>{pause.inputWarningMessage}</p>,
bannerMessage: <p className="text-left">{pause.bannerMessage}</p>,
progressBarMessage: <p>{pause.progressBarMessage}</p>,
pausedChainsList,
pausedModulesList,
}
})

export const MaintenanceBanners = () => {
return (
<>
{PAUSED_CHAINS.map((event) => {
return (
<MaintenanceBanner
id={event.id}
bannerMessage={event.bannerMessage}
startDate={event.startTimeBanner}
endDate={event.endTimeBanner}
disabled={event.disableBanner}
/>
)
})}
</>
)
}

export const MaintenanceWarningMessages = ({
type,
}: {
type: 'Bridge' | 'Swap'
}) => {
export const MaintenanceBanners = () => {
const { pausedChainsList } = useMaintenanceData()
const { fromChainId: bridgeFromChainId, toChainId: bridgeToChainId } =
useBridgeState()
const { swapChainId } = useSwapState()

if (type === 'Bridge') {
return (
<>
{PAUSED_CHAINS.map((event) => {
return (
<MaintenanceWarningMessage
fromChainId={bridgeFromChainId}
toChainId={bridgeToChainId}
startDate={event.startTimePauseChain}
endDate={event.endTimePauseChain}
pausedFromChains={event.pausedFromChains}
pausedToChains={event.pausedToChains}
warningMessage={event.inputWarningMessage}
disabled={event.disableWarning || !event.pauseBridge}
/>
)
})}
</>
)
} else if (type === 'Swap') {
const activeBanner = pausedChainsList.find(
(pausedChain) =>
isChainIncluded(pausedChain?.pausedFromChains, [bridgeFromChainId]) ||
isChainIncluded(pausedChain?.pausedToChains, [bridgeToChainId]) ||
isChainIncluded(pausedChain?.pausedFromChains, [swapChainId]) ||
isChainIncluded(pausedChain?.pausedToChains, [swapChainId])
)

if (activeBanner) {
return (
<>
{PAUSED_CHAINS.map((event) => {
return (
<MaintenanceWarningMessage
fromChainId={swapChainId}
toChainId={null}
startDate={event.startTimePauseChain}
endDate={event.endTimePauseChain}
pausedFromChains={event.pausedFromChains}
pausedToChains={event.pausedToChains}
warningMessage={event.inputWarningMessage}
disabled={event.disableWarning || !event.pauseSwap}
/>
)
})}
</>
<MaintenanceBanner
id={activeBanner?.id}
bannerMessage={activeBanner?.bannerMessage}
startDate={activeBanner?.startTimeBanner}
endDate={activeBanner?.endTimeBanner}
disabled={activeBanner?.disableBanner}
/>
)
} else {
return null
}
}

bigboydiamonds marked this conversation as resolved.
Show resolved Hide resolved
/**
* Hook that maps through PAUSED_CHAINS to apply the single event countdown progress logic to each.
* @returns A list of objects containing maintenance status and components for each paused chain.
*/
export const useMaintenanceCountdownProgresses = ({
type,
}: {
type: 'Bridge' | 'Swap'
}) => {
export const useMaintenance = () => {
const { pausedChainsList, pausedModulesList } = useMaintenanceData()
const { fromChainId: bridgeFromChainId, toChainId: bridgeToChainId } =
useBridgeState()
const { swapChainId } = useSwapState()

if (type === 'Bridge') {
return PAUSED_CHAINS.map((event) => {
return useMaintenanceCountdownProgress({
fromChainId: bridgeFromChainId,
toChainId: bridgeToChainId,
startDate: event.startTimePauseChain,
endDate: event.endTimePauseChain,
pausedFromChains: event.pausedFromChains,
pausedToChains: event.pausedToChains,
progressBarMessage: event.progressBarMessage,
disabled: event.disableCountdown || !event.pauseBridge,
})
})
} else if (type === 'Swap') {
return PAUSED_CHAINS.map((event) => {
return useMaintenanceCountdownProgress({
fromChainId: swapChainId,
toChainId: null,
startDate: event.startTimePauseChain,
endDate: event.endTimePauseChain,
pausedFromChains: event.pausedFromChains,
pausedToChains: event.pausedToChains,
progressBarMessage: event.progressBarMessage,
disabled: event.disableCountdown || !event.pauseSwap,
})
})
}
}
const activeBridgePause = pausedChainsList
.filter(
(pausedChain) =>
isChainIncluded(pausedChain?.pausedFromChains, [bridgeFromChainId]) ||
isChainIncluded(pausedChain?.pausedToChains, [bridgeToChainId])
)
.reduce((furthestPauseChain, currentChain) => {
const furthestDate = furthestPauseChain?.endTimePauseChain ?? null
const currentDate = currentChain.endTimePauseChain ?? null
const furthestFutureDate = getFurthestFutureDate(
furthestDate,
currentDate
)

/** Pause Bridge Modules */
interface BridgeModulePause {
chainId?: number // Will pause for all chains if undefined
bridgeModuleName: 'SynapseBridge' | 'SynapseRFQ' | 'SynapseCCTP' | 'ALL'
}
if (!furthestDate) return currentChain
return furthestFutureDate === furthestDate
? furthestPauseChain
: currentChain
}, null)

function isValidBridgeModule(
module: any
): module is 'SynapseBridge' | 'SynapseRFQ' | 'SynapseCCTP' | 'ALL' {
return ['SynapseBridge', 'SynapseRFQ', 'SynapseCCTP', 'ALL'].includes(module)
}
const activeSwapPause = pausedChainsList.find(
(pausedChain) =>
isChainIncluded(pausedChain?.pausedFromChains, [swapChainId]) ||
isChainIncluded(pausedChain?.pausedToChains, [swapChainId])
)

export function getBridgeModuleNames(module) {
if (module.bridgeModuleName === 'ALL') {
return ['SynapseRFQ', 'SynapseCCTP', 'SynapseBridge']
}
return [module.bridgeModuleName]
}
const {
isPending: isBridgePaused,
EventCountdownProgressBar: BridgeEventCountdownProgressBar,
} = useEventCountdownProgressBar(
activeBridgePause?.progressBarMessage,
activeBridgePause?.startTimePauseChain,
activeBridgePause?.endTimePauseChain,
activeBridgePause?.disableCountdown
)

const {
isPending: isSwapPaused,
EventCountdownProgressBar: SwapEventCountdownProgressBar,
} = useEventCountdownProgressBar(
activeSwapPause?.progressBarMessage,
activeSwapPause?.startTimePauseChain,
activeSwapPause?.endTimePauseChain,
activeSwapPause?.disableCountdown
)

const BridgeMaintenanceProgressBar = () => BridgeEventCountdownProgressBar
const SwapMaintenanceProgressBar = () => SwapEventCountdownProgressBar

const BridgeMaintenanceWarningMessage = () => (
<MaintenanceWarningMessage
fromChainId={bridgeFromChainId}
toChainId={bridgeToChainId}
startDate={activeBridgePause?.startTimePauseChain}
endDate={activeBridgePause?.endTimePauseChain}
pausedFromChains={activeBridgePause?.pausedFromChains}
pausedToChains={activeBridgePause?.pausedToChains}
warningMessage={activeBridgePause?.inputWarningMessage}
disabled={
activeBridgePause?.disableWarning || !activeBridgePause?.pauseBridge
}
/>
)

const SwapMaintenanceWarningMessage = () => (
<MaintenanceWarningMessage
fromChainId={swapChainId}
toChainId={null}
startDate={activeSwapPause?.startTimePauseChain}
endDate={activeSwapPause?.endTimePauseChain}
pausedFromChains={activeSwapPause?.pausedFromChains}
pausedToChains={activeSwapPause?.pausedToChains}
warningMessage={activeSwapPause?.inputWarningMessage}
disabled={activeSwapPause?.disableWarning || !activeSwapPause?.pauseSwap}
/>
)

export const PAUSED_MODULES: BridgeModulePause[] = pausedBridgeModules.map(
(route) => {
if (!isValidBridgeModule(route.bridgeModuleName)) {
throw new Error(`Invalid module type: ${route.bridgeModuleName}`)
}

return {
...route,
bridgeModuleName: route.bridgeModuleName as
| 'SynapseBridge'
| 'SynapseRFQ'
| 'SynapseCCTP'
| 'ALL',
}
return {
isBridgePaused,
isSwapPaused,
pausedChainsList,
pausedModulesList,
BridgeMaintenanceProgressBar,
BridgeMaintenanceWarningMessage,
SwapMaintenanceProgressBar,
SwapMaintenanceWarningMessage,
}
)
}
bigboydiamonds marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading