diff --git a/explorer/src/App.tsx b/explorer/src/App.tsx
index be665a6be6..238d98ba3b 100644
--- a/explorer/src/App.tsx
+++ b/explorer/src/App.tsx
@@ -12,6 +12,7 @@ import { ClusterStatsPage } from "pages/ClusterStatsPage";
import { SupplyPage } from "pages/SupplyPage";
import { TransactionDetailsPage } from "pages/TransactionDetailsPage";
import { BlockDetailsPage } from "pages/BlockDetailsPage";
+import { UnlockAlert } from "components/UnlockAlert";
const ADDRESS_ALIASES = ["account", "accounts", "addresses"];
const TX_ALIASES = ["txs", "txn", "txns", "transaction", "transactions"];
@@ -20,6 +21,7 @@ function App() {
return (
<>
+
diff --git a/explorer/src/components/UnlockAlert.tsx b/explorer/src/components/UnlockAlert.tsx
new file mode 100644
index 0000000000..7099c5cd10
--- /dev/null
+++ b/explorer/src/components/UnlockAlert.tsx
@@ -0,0 +1,93 @@
+import React from "react";
+
+import { Connection } from "@solana/web3.js";
+import { useCluster, Cluster } from "providers/cluster";
+
+const CLUSTER_SYNC_INTERVAL = 30000;
+
+export function displayTimestamp(unixTimestamp: number): string {
+ const expireDate = new Date(unixTimestamp);
+ const dateString = new Intl.DateTimeFormat("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ }).format(expireDate);
+ const timeString = new Intl.DateTimeFormat("en-US", {
+ hour: "numeric",
+ minute: "numeric",
+ hour12: false,
+ timeZoneName: "long",
+ }).format(expireDate);
+ return `${dateString} at ${timeString}`;
+}
+
+export function UnlockAlert() {
+ const { cluster, url } = useCluster();
+ const [active, setActive] = React.useState(false);
+ const [blockTime, setBlockTime] = React.useState
(null);
+
+ React.useEffect(() => {
+ if (!active || !url) {
+ return;
+ }
+
+ const connection = new Connection(url);
+ const getBlockTime = async () => {
+ try {
+ const epochInfo = await connection.getEpochInfo();
+ const blockTime = await connection.getBlockTime(epochInfo.absoluteSlot);
+ if (blockTime !== null) {
+ setBlockTime(blockTime);
+ }
+ } catch (error) {}
+ };
+
+ getBlockTime();
+
+ const blockTimeInterval = setInterval(getBlockTime, CLUSTER_SYNC_INTERVAL);
+ const secondInterval = setInterval(() => {
+ setBlockTime((time) => (time !== null ? time + 1 : null));
+ }, 1000);
+
+ return () => {
+ clearInterval(blockTimeInterval);
+ clearInterval(secondInterval);
+ };
+ }, [active, url]);
+
+ React.useEffect(() => {
+ if (cluster !== Cluster.MainnetBeta) {
+ return;
+ }
+
+ setActive(true);
+ return () => {
+ setActive(false);
+ };
+ }, [setActive, cluster]);
+
+ if (cluster !== Cluster.MainnetBeta || blockTime === null) {
+ return null;
+ }
+
+ return (
+
+
An unlock event is timed for midnight January 7th UTC cluster time.
+
+ Cluster time is currently {displayTimestamp(blockTime * 1000)} and may
+ differ from actual UTC time.
+
+
+ More information can be found{" "}
+
+ here
+
+ .
+
+
+ );
+}
diff --git a/explorer/src/pages/ClusterStatsPage.tsx b/explorer/src/pages/ClusterStatsPage.tsx
index b110afe2de..856667a662 100644
--- a/explorer/src/pages/ClusterStatsPage.tsx
+++ b/explorer/src/pages/ClusterStatsPage.tsx
@@ -88,7 +88,7 @@ function StatsCardBody() {
)}
{blockTime && (
- Block time |
+ Cluster time |
{displayTimestamp(blockTime)}
|