diff --git a/app/[locale]/roadmap/_components/ReleaseCarousel.tsx b/app/[locale]/roadmap/_components/ReleaseCarousel.tsx new file mode 100644 index 00000000000..c092d5eebe0 --- /dev/null +++ b/app/[locale]/roadmap/_components/ReleaseCarousel.tsx @@ -0,0 +1,264 @@ +"use client" + +import { useEffect, useState } from "react" + +import { Image } from "@/components/Image" +import { ButtonLink } from "@/components/ui/buttons/Button" +import { + Carousel, + type CarouselApi, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} from "@/components/ui/carousel" + +import { cn } from "@/lib/utils/cn" +import { formatDate } from "@/lib/utils/date" + +import { releasesData } from "@/data/roadmap/releases" + +const findLatestReleaseIndex = () => { + const today = new Date() + const twoMonthsFromNow = new Date() + twoMonthsFromNow.setMonth(today.getMonth() + 2) + + // First try to find a release within the next 2 months + const upcomingReleaseIndex = releasesData.findIndex((release) => { + const releaseDate = new Date(release.releaseDate) + return releaseDate > today && releaseDate <= twoMonthsFromNow + }) + + // If no upcoming release found, find the most recent release up to today + if (upcomingReleaseIndex === -1) { + const pastReleases = releasesData.filter( + (release) => new Date(release.releaseDate) <= today + ) + if (pastReleases.length > 0) { + const mostRecentRelease = pastReleases[pastReleases.length - 1] + return releasesData.findIndex( + (release) => release.releaseDate === mostRecentRelease.releaseDate + ) + } + } + + return upcomingReleaseIndex +} + +const ReleaseCarousel = () => { + const todayDate = new Date() + const twoMonthsFromNow = new Date() + twoMonthsFromNow.setMonth(todayDate.getMonth() + 2) + + const [api1, setApi1] = useState() + const [api2, setApi2] = useState() + const [currentIndex, setCurrentIndex] = useState(() => + findLatestReleaseIndex() + ) + + useEffect(() => { + if (!api1 || !api2) { + return + } + + api1.on("select", () => { + setCurrentIndex(api1.selectedScrollSnap()) + api2.scrollTo(api1.selectedScrollSnap()) + }) + + api2.on("select", () => { + setCurrentIndex(api2.selectedScrollSnap()) + api1.scrollTo(api2.selectedScrollSnap()) + }) + }, [api1, api2]) + + return ( +
+
+
+
+ {/* First Carousel */} + + + {releasesData.map((release, index) => { + const releaseDate = new Date(release.releaseDate) + const nextRelease = + releaseDate > todayDate && releaseDate <= twoMonthsFromNow + const labelType = + releaseDate < todayDate + ? 1 + : releaseDate < twoMonthsFromNow + ? 2 + : 3 + + return ( + +
+
+ {labelType === 1 && ( +
+

In production

+
+ )} + {labelType === 2 && ( +
+

+ Coming soon +

+
+ )} + {labelType === 3 && ( +
+

+ In development +

+
+ )} +
+
+
+
+
+
+
+

+ {release.releaseName} +

+

+ {formatDate(release.releaseDate)} +

+
+
+ + ) + })} + +
+ + +
+ + + {/* Second Carousel */} + + + {releasesData.map((release) => ( + +
+
+ {release.releaseName} +
+
+
+

+ {release.releaseName} +

+

+ {formatDate(release.releaseDate)} +

+
+ +
+

+ Main features +

+
+ {release.content} +
+
+ + Learn more + +
+
+
+ ))} +
+
+ + +
+
+
+
+
+
+ ) +} + +export default ReleaseCarousel diff --git a/app/[locale]/roadmap/_components/roadmap.tsx b/app/[locale]/roadmap/_components/roadmap.tsx new file mode 100644 index 00000000000..b373eb20747 --- /dev/null +++ b/app/[locale]/roadmap/_components/roadmap.tsx @@ -0,0 +1,377 @@ +import BannerNotification from "@/components/Banners/BannerNotification" +import ExpandableCard from "@/components/ExpandableCard" +import FeedbackCard from "@/components/FeedbackCard" +import { HubHero } from "@/components/Hero" +import type { HubHeroProps } from "@/components/Hero/HubHero" +import { + AccountAbstractionIcon, + BetterUserExperienceIcon, + CheaperTransactionsIcon, + DankshardingIcon, + ExtraSecurityIcon, + FutureProofingIcon, + ProposerBuilderSeparationIcon, + SecretLeaderElectionIcon, + SingleSlotFinalityIcon, + StatelessnessIcon, + VerkleTreesIcon, +} from "@/components/icons/roadmap" +import { Image } from "@/components/Image" +import MainArticle from "@/components/MainArticle" +import { ButtonLink } from "@/components/ui/buttons/Button" +import Link from "@/components/ui/Link" +import InlineLink from "@/components/ui/Link" +import { LinkBox, LinkOverlay } from "@/components/ui/link-box" + +import ReleaseCarousel from "./ReleaseCarousel" + +import { useTranslation } from "@/hooks/useTranslation" +import ethBlocksImage from "@/public/images/developers-eth-blocks.png" +import communityHeroImg from "@/public/images/heroes/community-hero.png" +import roadmapHeroImg from "@/public/images/heroes/roadmap-hub-hero.jpg" + +const RoadmapPage = () => { + const { t } = useTranslation("page-roadmap") + + const heroContent: HubHeroProps = { + title: "", + header: t("page-roadmap-meta-title"), + description: t("page-roadmap-meta-description"), + heroImg: roadmapHeroImg, + } + + const changesComingItems = [ + { + title: "Cheaper transactions", + icon: , + description: + "Rollups are too expensive and rely on centralized components, causing users to place too much trust in their operators. The roadmap includes fixes for both of these problems.", + button: { + label: "More on reducing fees", + href: "/roadmap/scaling", + }, + }, + { + title: "Extra security", + icon: , + description: + "Ethereum is already very secure but it can be made even stronger, ready to withstand all kinds of attack far into the future.", + button: { + label: "More on security", + href: "/roadmap/security", + }, + }, + { + title: "Better user experience", + icon: , + description: + "More support for smart contract wallets and light-weight nodes will make using Ethereum simpler and safer.", + button: { + label: "More on user experience", + href: "/roadmap/user-experience", + }, + }, + { + title: "Future-proofing", + icon: , + description: + "Ethereum researchers and developers are solving tomorrow's problems today, readying the network for future generations.", + button: { + label: "More on future-proofing", + href: "/roadmap/future-proofing", + }, + }, + ] + + const technicalUpgradesItems = [ + { + icon: , + title: "Danksharding", + description: + "Danksharding makes L2 rollups much cheaper for users by adding “blobs” of data to Ethereum blocks.", + href: "/roadmap/danksharding", + }, + { + icon: , + title: "Single slot finality", + description: + "Instead of waiting for fifteen minutes, blocks could get proposed and finalized in the same slot. This is more convenient for apps and difficult to attack.", + href: "/roadmap/single-slot-finality", + }, + { + icon: , + title: "Proposer-builder separation", + description: + "Splitting the block building and block proposal tasks across separate validators creates a fairer, more censorship resistant and efficient way for Ethereum to come to consensus.", + href: "/roadmap/pbs", + }, + { + icon: , + title: "Secret leader election", + description: + "Clever cryptography can be used to ensure that the identity of the current block proposer is not made public, protecting them from certain types of attack.", + href: "/roadmap/secret-leader-election", + }, + { + icon: , + title: "Account abstraction", + description: + "Account abstraction is a class of upgrades that support smart contract wallets natively on Ethereum, rather than having to use complex middleware.", + href: "/roadmap/account-abstraction", + }, + { + icon: , + title: "Verkle trees", + description: + "Verkle trees are a data structure that can be used to enable stateless clients on Ethereum. These clients will require a small amount of storage space but will still be able to verify new blocks.", + href: "/roadmap/verkle-trees", + }, + { + icon: , + title: "Statelessness", + description: + "Stateless clients will be able to verify new blocks without having to store large amounts of data. This will provide all the benefits of running a node with only a tiny fraction of today’s costs.", + href: "/roadmap/statelessness", + }, + ] + + // TODO: MATOMO EVENTS + return ( + + +

+ Ethereum's development is community-driven and subject to change. +

+
+
+ + +
+ +
+ +
+

What changes are coming to Ethereum?

+

+ Ethereum is already a powerful platform, but it is still being + improved. An ambitious set of improvements will upgrade Ethereum + from its current form into a fully scaled, maximally resilient + platform. +

+
+ {changesComingItems.map((item) => ( +
+
+

{item.title}

+
+ {item.icon} +
+
+

{item.description}

+ + {item.button.label} + +
+ ))} +
+
+ +
+
+

Why does Ethereum need a roadmap?

+

+ Ethereum gets regular upgrades that enhance its scalability, + security, or sustainability. One of Ethereum's core strengths + is adapting as new ideas emerge from research and development. + Adaptability gives Ethereum the flexibility to tackle emerging + challenges and keep up with the most advanced technological + breakthroughs. +

+

How the roadmap is defined

+

+ The roadmap is mostly the result of years of work by researchers + and developers - because the protocol is very technical - but any + motivated person can participate. +

+

+ Ideas usually start off as discussions on a forum such as{" "} + ethresear.ch,{" "} + + Ethereum Magicians + {" "} + or the Eth R&D discord server. They may be responses to new + vulnerabilities that are discovered, suggestions from + organizations working in the application layer (such as dapps and + exchanges) or from known frictions for end users (such as costs or + transaction speeds). +

+

+ When these ideas mature, they can be proposed as{" "} + + Ethereum Improvement Proposals + + . This is all done in public so that anyone from the community can + weigh in at any time. +

+ + More on Ethereum governance + +
+
+ Ethereum roadmap +
+
+ +
+

+ What technical upgrades are coming to Ethereum? +

+
+ {technicalUpgradesItems.map((item) => ( + +
+
{item.icon}
+

{item.title}

+
+

+ {item.description} +

+ + + + Learn more + + +
+ ))} +
+
+ +
+
+ Ethereum blocks +
+
+

What is the timeline for these upgrades?

+
+ +
+

+ Yes—almost definitely. The roadmap is the + current plan for upgrading Ethereum, covering both near-term + and future plans. We expect the roadmap to change as new + information and technology become available. +

+

+ Think of Ethereum's roadmap as a set of intentions for + improving Ethereum; it is the core researchers' and + developers' best hypothesis of Ethereum's most + optimal path forward. +

+
+
+ +
+

+ Some upgrades are lower priority and likely not to be + implemented for the next 5-10 years (e.g. quantum + resistance).{" "} + + Giving precise timing of each upgrade is complicated + {" "} + to predict as many roadmap items are worked on in parallel + and developed at different speeds. The urgency of an upgrade + can also change over time depending on external factors + (e.g. a sudden leap in the performance and availability of + quantum computers may make quantum-resistant cryptography + more urgent). +

+

+ One way to think about Ethereum development is by analogy to + biological evolution. A network that is able to adapt to new + challenges and maintain fitness is more likely to succeed + that one that is resistant to change, although as the + network becomes more and more performant, scalable and + secure fewer changes to the protocol will be required. +

+
+
+ +
+

+ Upgrades tend not to impact end-users except by providing + better user-experiences and a more secure protocol and + perhaps more options for how to interact with + Ethereum.{" "} + + Regular users are not required to actively participate in + an upgrade, nor are they required to do anything** to + secure their assets. + {" "} + Node operators will need + to update their clients to prepare for an upgrade. Some + upgrades may lead to changes for application developers. For + example, history expiry upgrades may lead application + developers to grab historical data from new sources. +

+
+
+ +
+

+ Sharding is splitting up the Ethereum blockchain so that + subsets of{" "} + validators are only + responsible for a fraction of the total data. This was + originally intended to be the way for Ethereum to scale. + However, layer 2{" "} + rollups have developed much faster than expected and have + provided a lot of scaling already, and will provide much + more after Proto-Danksharding is implemented. This means + "shard chains" are no longer needed and have been + dropped from the roadmap. +

+
+
+
+
+
+ + +
+
+ ) +} + +export default RoadmapPage diff --git a/app/[locale]/roadmap/page.tsx b/app/[locale]/roadmap/page.tsx new file mode 100644 index 00000000000..587c593c66d --- /dev/null +++ b/app/[locale]/roadmap/page.tsx @@ -0,0 +1,48 @@ +import pick from "lodash.pick" +import { getTranslations } from "next-intl/server" + +import { Lang } from "@/lib/types" + +import I18nProvider from "@/components/I18nProvider" + +import { getMetadata } from "@/lib/utils/metadata" +import { getRequiredNamespacesForPage } from "@/lib/utils/translations" + +import RoadmapPage from "./_components/roadmap" + +import { loadMessages } from "@/i18n/loadMessages" + +const Page = async ({ params }: { params: Promise<{ locale: Lang }> }) => { + const { locale } = await params + + // Get i18n messages + const allMessages = await loadMessages(locale) + const requiredNamespaces = getRequiredNamespacesForPage("/roadmap") + const messages = pick(allMessages, requiredNamespaces) + + return ( + + + + ) +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ locale: string }> +}) { + const { locale } = await params + + const t = await getTranslations({ locale, namespace: "page-roadmap" }) + + return await getMetadata({ + locale, + slug: ["roadmap"], + title: t("page-roadmap-meta-title"), + description: t("page-roadmap-meta-description"), + image: "/images/roadmap/roadmap-hub-hero.png", + }) +} + +export default Page diff --git a/package.json b/package.json index f9eec08d1c2..b41f801cc94 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "@radix-ui/react-radio-group": "^1.2.0", "@radix-ui/react-scroll-area": "^1.2.2", "@radix-ui/react-select": "^2.1.1", - "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-switch": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.2", @@ -56,6 +56,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", + "embla-carousel-react": "^8.6.0", "ethereum-blockies-base64": "^1.0.2", "framer-motion": "^10.13.0", "gray-matter": "^4.0.3", diff --git a/public/content/roadmap/index.md b/public/content/roadmap/index.md deleted file mode 100644 index 1de5fe6a33b..00000000000 --- a/public/content/roadmap/index.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: Ethereum roadmap -description: The path to more scalability, security and sustainability for Ethereum. -lang: en -template: roadmap -image: /images/heroes/roadmap-hub-hero.jpg -alt: "Ethereum roadmap" -summaryPoints: -buttons: - - content: Further upgrades - toId: what-changes-are-coming - - content: Past upgrades - href: /history/ - variant: outline ---- - -Ethereum is already a powerful platform for global coordination, but it is still being improved. An ambitious set of improvements will upgrade Ethereum from its current form into a fully scaled, maximally resilient platform. These upgrades are laid out in the Ethereum roadmap. - -**To learn about previous upgrades to Ethereum, please visit our [History](/history/) page** - -## What changes are coming to Ethereum? {#what-changes-are-coming} - -The Ethereum roadmap outlines the specific improvements that will be made to protocol in the future. Overall, the roadmap will bring the following benefits to Ethereum users: - - - - - - - - -## Why does Ethereum need a roadmap? {#why-does-ethereum-need-a-roadmap} - -Ethereum gets regular upgrades that enhance its scalability, security, or sustainability. One of Ethereum's core strengths is adapting as new ideas emerge from research and development. Adaptability gives Ethereum the flexibility to tackle emerging challenges and keep up with the most advanced technological breakthroughs. - - - -The roadmap is mostly the result of years of work by researchers and developers - because the protocol is very technical - but any motivated person can participate. Ideas usually start off as discussions on a forum such as [ethresear.ch](https://ethresear.ch/), [Ethereum Magicians](https://ethereum-magicians.org/) or the Eth R&D discord server. They may be responses to new vulnerabilities that are discovered, suggestions from organizations working in the application layer (such as [dapps](/glossary/#dapp) and exchanges) or from known frictions for end users (such as costs or transaction speeds). When these ideas mature, they can be proposed as [Ethereum Improvement Proposals](https://eips.ethereum.org/). This is all done in public so that anyone from the community can weigh in at any time. - -[More on Ethereum governance](/governance/) - - - - -

What was ETH2?

- -

The term 'Eth2' was commonly used to describe the future of Ethereum before the switch to proof-of-stake but it was phased out in favor of more precise terminology. It was originally used to differentiate the Ethereum network before the switch to proof-of-stake and the network after, or sometimes to refer to the different Ethereum clients (execution clients were sometimes referred to as ETH1 clients and consensus clients were sometimes referred to as ETH2 clients).

- -
- -## Will Ethereum's roadmap change over time? {#will-ethereums-roadmap-change-over-time} - -**Yes—almost definitely**. The roadmap is the current plan for upgrading Ethereum, covering both near-term and future plans. We expect the roadmap to change as new information and technology become available. - -Think of Ethereum's roadmap as a set of intentions for improving Ethereum; it is the core researchers' and developers' best hypothesis of Ethereum's most optimal path forward. - -## When will the roadmap be finished? {#when-will-the-roadmap-be-finished} - -Some upgrades are lower priority and likely not to be implemented for the next 5-10 years (e.g. quantum resistance). **Giving precise timing of each upgrade is complicated to predict** as many roadmap items are worked on in parallel and developed at different speeds. The urgency of an upgrade can also change over time depending on external factors (e.g. a sudden leap in the performance and availability of quantum computers may make quantum-resistant cryptography more urgent). - -One way to think about Ethereum development is by analogy to biological evolution. A network that is able to adapt to new challenges and maintain fitness is more likely to succeed that one that is resistant to change, although as the network becomes more and more performant, scalable and secure fewer changes to the protocol will be required. - -## Do I have to do anything when there is an upgrade? {#do-i-have-to-do-anything-when-there-is-an-upgrade} - -Upgrades tend not to impact end-users except by providing better user-experiences and a more secure protocol and perhaps more options for how to interact with Ethereum. **Regular users are not required to actively participate in an upgrade, nor are they required to do anything** to secure their assets. [Node](/glossary/#node) operators will need to update their clients to prepare for an upgrade. Some upgrades may lead to changes for application developers. For example, history expiry upgrades may lead application developers to grab historical data from new sources. - -## What about The Verge, The Splurge, etc? {#what-about-the-verge-splurge-etc} - -[Vitalik Buterin proposed a vision for the Ethereum roadmap](https://twitter.com/VitalikButerin/status/1741190491578810445) that was organized into several categories linked by their effects on Ethereum's architecture. It included: - -- **The Merge**: upgrades relating to the switch from [proof-of-work](/glossary/#pow) to [proof-of-stake](/glossary/#pos) -- **The Surge**: upgrades related to scalability by [rollups](/glossary/#rollups) and data sharding -- **The Scourge**: upgrades related to censorship resistance, decentralization and protocol risks from [MEV](/glossary/#mev) -- **The Verge**: upgrades related to verifying [blocks](/glossary/#block) more easily -- **The Purge**: upgrades related to reducing the computational costs of running nodes and simplifying the protocol -- **The Splurge**: other upgrades that don't fit well into the previous categories. - -We decided not to use this terminology because we wanted to use a simpler and more user-centric model. Although we use user-centric language, the vision remains the same as the one proposed by Vitalik. - -## What about sharding? {#what-about-sharding} - -Sharding is splitting up the Ethereum blockchain so that subsets of [validators](/glossary/#validator) are only responsible for a fraction of the total data. This was originally intended to be the way for Ethereum to scale. However, [layer 2](/glossary/#layer-2) rollups have developed much faster than expected and have provided a lot of scaling already, and will provide much more after Proto-Danksharding is implemented. This means "shard chains" are no longer needed and have been dropped from the roadmap. - -## Looking for specific technical upgrades? {#looking-for-specific-technical-upgrades} - -- [Pectra](/roadmap/pectra) - Prague/Electra hardfork that brings new approach to account abstraction, improves scalability and more. -- [Danksharding](/roadmap/danksharding) - Danksharding makes layer 2 rollups much cheaper for users by adding “blobs” of data to Ethereum blocks. -- [Staking withdrawals](/staking/withdrawals) - The Shanghai/Capella upgrade enabled staking withdrawals on Ethereum, allowing people to unlock their staked ETH. -- [Single slot finality](/roadmap/single-slot-finality) - Instead of waiting for fifteen minutes, blocks could get proposed and finalized in the same slot. This is more convenient for apps and much more difficult to attack. -- [Proposer-builder separation](/roadmap/pbs) - Splitting the block building and block proposal tasks across separate validators creates a fairer, more censorship resistant and efficient way for Ethereum to come to consensus. -- [Secret leader election](/roadmap/secret-leader-election) - Clever cryptography can be used to ensure that the identity of the current block proposer is not made public, protecting them from certain types of attack. -- [Account abstraction](/roadmap/account-abstraction) - Account abstraction is a class of upgrades that support smart contract wallets natively on Ethereum, rather than having to use complex middleware. -- [Verkle trees](/roadmap/verkle-trees) - Verkle trees are a data structure that can be used to enable stateless clients on Ethereum. These “stateless” clients will require a tiny amount of storage space but will still be able to verify new blocks. -- [Statelessness](/roadmap/statelessness) - Stateless clients will be able to verify new blocks without having to store large amounts of data. This will provide all the benefits of running a node with only a tiny fraction of today’s costs. diff --git a/public/images/roadmap/roadmap-pectra.png b/public/images/roadmap/roadmap-pectra.png new file mode 100644 index 00000000000..82a3d2a2c3f Binary files /dev/null and b/public/images/roadmap/roadmap-pectra.png differ diff --git a/src/components/ExpandableCard.tsx b/src/components/ExpandableCard.tsx index 65084d5ab75..aad9b420c7a 100644 --- a/src/components/ExpandableCard.tsx +++ b/src/components/ExpandableCard.tsx @@ -4,6 +4,7 @@ import React, { type ReactNode, useState } from "react" import { Flex, HStack, VStack } from "@/components/ui/flex" +import { cn } from "@/lib/utils/cn" import { trackCustomEvent } from "@/lib/utils/matomo" import type { IconBaseType } from "./icons/icon-base" @@ -25,6 +26,7 @@ export type ExpandableCardProps = { eventCategory?: string eventName?: string visible?: boolean + className?: string } const ExpandableCard = ({ @@ -36,6 +38,7 @@ const ExpandableCard = ({ eventCategory = "", eventName = "", visible = false, + className, }: ExpandableCardProps) => { const [isVisible, setIsVisible] = useState(visible) const { t } = useTranslation("common") @@ -62,7 +65,7 @@ const ExpandableCard = ({ + + + ), +}) diff --git a/src/components/icons/roadmap/BetterUserExperienceIcon.tsx b/src/components/icons/roadmap/BetterUserExperienceIcon.tsx new file mode 100644 index 00000000000..dde3eb9af99 --- /dev/null +++ b/src/components/icons/roadmap/BetterUserExperienceIcon.tsx @@ -0,0 +1,35 @@ +import { createIconBase } from "../icon-base" + +export const BetterUserExperienceIcon = createIconBase({ + displayName: "BetterUserExperienceIcon", + viewBox: "0 0 50 50", + className: "w-[50px] h-[50px]", + children: ( + <> + + + + + + + + ), +}) diff --git a/src/components/icons/roadmap/CheaperTransactionsIcon.tsx b/src/components/icons/roadmap/CheaperTransactionsIcon.tsx new file mode 100644 index 00000000000..8c3481c6d13 --- /dev/null +++ b/src/components/icons/roadmap/CheaperTransactionsIcon.tsx @@ -0,0 +1,27 @@ +import { createIconBase } from "../icon-base" + +export const CheaperTransactionsIcon = createIconBase({ + displayName: "CheaperTransactionsIcon", + viewBox: "0 0 54 40", + className: "w-[54px] h-[40px]", + children: ( + <> + + + + + + ), +}) diff --git a/src/components/icons/roadmap/DankshardingIcon.tsx b/src/components/icons/roadmap/DankshardingIcon.tsx new file mode 100644 index 00000000000..e01a7173c7d --- /dev/null +++ b/src/components/icons/roadmap/DankshardingIcon.tsx @@ -0,0 +1,15 @@ +import { createIconBase } from "../icon-base" + +export const DankshardingIcon = createIconBase({ + displayName: "DankshardingIcon", + viewBox: "0 0 28 28", + className: "w-[28px] h-[28px]", + children: ( + <> + + + ), +}) diff --git a/src/components/icons/roadmap/ExtraSecurityIcon.tsx b/src/components/icons/roadmap/ExtraSecurityIcon.tsx new file mode 100644 index 00000000000..7b304aeae4e --- /dev/null +++ b/src/components/icons/roadmap/ExtraSecurityIcon.tsx @@ -0,0 +1,23 @@ +import { createIconBase } from "../icon-base" + +export const ExtraSecurityIcon = createIconBase({ + displayName: "ExtraSecurityIcon", + viewBox: "0 0 44 53", + className: "w-[44px] h-[53px]", + children: ( + <> + + + + + ), +}) diff --git a/src/components/icons/roadmap/FutureProofingIcon.tsx b/src/components/icons/roadmap/FutureProofingIcon.tsx new file mode 100644 index 00000000000..cfbe18c7b30 --- /dev/null +++ b/src/components/icons/roadmap/FutureProofingIcon.tsx @@ -0,0 +1,31 @@ +import { createIconBase } from "../icon-base" + +export const FutureProofingIcon = createIconBase({ + displayName: "FutureProofingIcon", + viewBox: "0 0 56 56", + className: "w-[56px] h-[56px]", + children: ( + <> + + + + + + + ), +}) diff --git a/src/components/icons/roadmap/ProposerBuilderSeparationIcon.tsx b/src/components/icons/roadmap/ProposerBuilderSeparationIcon.tsx new file mode 100644 index 00000000000..4828f3918af --- /dev/null +++ b/src/components/icons/roadmap/ProposerBuilderSeparationIcon.tsx @@ -0,0 +1,15 @@ +import { createIconBase } from "../icon-base" + +export const ProposerBuilderSeparationIcon = createIconBase({ + displayName: "ProposerBuilderSeparationIcon", + viewBox: "0 0 28 28", + className: "w-[28px] h-[28px]", + children: ( + <> + + + ), +}) diff --git a/src/components/icons/roadmap/SecretLeaderElectionIcon.tsx b/src/components/icons/roadmap/SecretLeaderElectionIcon.tsx new file mode 100644 index 00000000000..63faec198db --- /dev/null +++ b/src/components/icons/roadmap/SecretLeaderElectionIcon.tsx @@ -0,0 +1,15 @@ +import { createIconBase } from "../icon-base" + +export const SecretLeaderElectionIcon = createIconBase({ + displayName: "SecretLeaderElectionIcon", + viewBox: "0 0 28 28", + className: "w-[28px] h-[28px]", + children: ( + <> + + + ), +}) diff --git a/src/components/icons/roadmap/SingleSlotFinalityIcon.tsx b/src/components/icons/roadmap/SingleSlotFinalityIcon.tsx new file mode 100644 index 00000000000..6b2f7653f6a --- /dev/null +++ b/src/components/icons/roadmap/SingleSlotFinalityIcon.tsx @@ -0,0 +1,15 @@ +import { createIconBase } from "../icon-base" + +export const SingleSlotFinalityIcon = createIconBase({ + displayName: "SingleSlotFinalityIcon", + viewBox: "0 0 28 28", + className: "w-[28px] h-[28px]", + children: ( + <> + + + ), +}) diff --git a/src/components/icons/roadmap/StakingWithdrawalsIcon.tsx b/src/components/icons/roadmap/StakingWithdrawalsIcon.tsx new file mode 100644 index 00000000000..2023033b85f --- /dev/null +++ b/src/components/icons/roadmap/StakingWithdrawalsIcon.tsx @@ -0,0 +1,15 @@ +import { createIconBase } from "../icon-base" + +export const StakingWithdrawalsIcon = createIconBase({ + displayName: "StakingWithdrawalsIcon", + viewBox: "0 0 28 28", + className: "w-[28px] h-[28px]", + children: ( + <> + + + ), +}) diff --git a/src/components/icons/roadmap/StatelessnessIcon.tsx b/src/components/icons/roadmap/StatelessnessIcon.tsx new file mode 100644 index 00000000000..c90ccc5d227 --- /dev/null +++ b/src/components/icons/roadmap/StatelessnessIcon.tsx @@ -0,0 +1,15 @@ +import { createIconBase } from "../icon-base" + +export const StatelessnessIcon = createIconBase({ + displayName: "StatelessnessIcon", + viewBox: "0 0 28 28", + className: "w-[28px] h-[28px]", + children: ( + <> + + + ), +}) diff --git a/src/components/icons/roadmap/VerkleTreesIcon.tsx b/src/components/icons/roadmap/VerkleTreesIcon.tsx new file mode 100644 index 00000000000..326f3c4c4e6 --- /dev/null +++ b/src/components/icons/roadmap/VerkleTreesIcon.tsx @@ -0,0 +1,15 @@ +import { createIconBase } from "../icon-base" + +export const VerkleTreesIcon = createIconBase({ + displayName: "VerkleTreesIcon", + viewBox: "0 0 28 28", + className: "w-[28px] h-[28px]", + children: ( + <> + + + ), +}) diff --git a/src/components/icons/roadmap/index.ts b/src/components/icons/roadmap/index.ts new file mode 100644 index 00000000000..5beac56b0b2 --- /dev/null +++ b/src/components/icons/roadmap/index.ts @@ -0,0 +1,27 @@ +import { AccountAbstractionIcon } from "./AccountAbstractionIcon" +import { BetterUserExperienceIcon } from "./BetterUserExperienceIcon" +import { CheaperTransactionsIcon } from "./CheaperTransactionsIcon" +import { DankshardingIcon } from "./DankshardingIcon" +import { ExtraSecurityIcon } from "./ExtraSecurityIcon" +import { FutureProofingIcon } from "./FutureProofingIcon" +import { ProposerBuilderSeparationIcon } from "./ProposerBuilderSeparationIcon" +import { SecretLeaderElectionIcon } from "./SecretLeaderElectionIcon" +import { SingleSlotFinalityIcon } from "./SingleSlotFinalityIcon" +import { StakingWithdrawalsIcon } from "./StakingWithdrawalsIcon" +import { StatelessnessIcon } from "./StatelessnessIcon" +import { VerkleTreesIcon } from "./VerkleTreesIcon" + +export { + AccountAbstractionIcon, + BetterUserExperienceIcon, + CheaperTransactionsIcon, + DankshardingIcon, + ExtraSecurityIcon, + FutureProofingIcon, + ProposerBuilderSeparationIcon, + SecretLeaderElectionIcon, + SingleSlotFinalityIcon, + StakingWithdrawalsIcon, + StatelessnessIcon, + VerkleTreesIcon, +} diff --git a/src/components/ui/carousel.tsx b/src/components/ui/carousel.tsx new file mode 100644 index 00000000000..32b67c80cfb --- /dev/null +++ b/src/components/ui/carousel.tsx @@ -0,0 +1,261 @@ +import * as React from "react" +import useEmblaCarousel, { + type UseEmblaCarouselType, +} from "embla-carousel-react" + +import { ChevronNext, ChevronPrev } from "@/components/Chevron" +import { Button } from "@/components/ui/buttons/Button" + +import { cn } from "@/lib/utils/cn" + +type CarouselApi = UseEmblaCarouselType[1] +type UseCarouselParameters = Parameters +type CarouselOptions = UseCarouselParameters[0] +type CarouselPlugin = UseCarouselParameters[1] + +type CarouselProps = { + opts?: CarouselOptions + plugins?: CarouselPlugin + orientation?: "horizontal" | "vertical" + setApi?: (api: CarouselApi) => void +} + +type CarouselContextProps = { + carouselRef: ReturnType[0] + api: ReturnType[1] + scrollPrev: () => void + scrollNext: () => void + canScrollPrev: boolean + canScrollNext: boolean +} & CarouselProps + +const CarouselContext = React.createContext(null) + +function useCarousel() { + const context = React.useContext(CarouselContext) + + if (!context) { + throw new Error("useCarousel must be used within a ") + } + + return context +} + +const Carousel = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & CarouselProps +>( + ( + { + orientation = "horizontal", + opts, + setApi, + plugins, + className, + children, + ...props + }, + ref + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === "horizontal" ? "x" : "y", + }, + plugins + ) + const [canScrollPrev, setCanScrollPrev] = React.useState(false) + const [canScrollNext, setCanScrollNext] = React.useState(false) + + const onSelect = React.useCallback((api: CarouselApi) => { + if (!api) { + return + } + + setCanScrollPrev(api.canScrollPrev()) + setCanScrollNext(api.canScrollNext()) + }, []) + + const scrollPrev = React.useCallback(() => { + api?.scrollPrev() + }, [api]) + + const scrollNext = React.useCallback(() => { + api?.scrollNext() + }, [api]) + + const handleKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "ArrowLeft") { + event.preventDefault() + scrollPrev() + } else if (event.key === "ArrowRight") { + event.preventDefault() + scrollNext() + } + }, + [scrollPrev, scrollNext] + ) + + React.useEffect(() => { + if (!api || !setApi) { + return + } + + setApi(api) + }, [api, setApi]) + + React.useEffect(() => { + if (!api) { + return + } + + onSelect(api) + api.on("reInit", onSelect) + api.on("select", onSelect) + + return () => { + api?.off("select", onSelect) + } + }, [api, onSelect]) + + return ( + +
+ {children} +
+
+ ) + } +) +Carousel.displayName = "Carousel" + +const CarouselContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { carouselRef, orientation } = useCarousel() + + return ( +
+
+
+ ) +}) +CarouselContent.displayName = "CarouselContent" + +const CarouselItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { orientation } = useCarousel() + + return ( +
+ ) +}) +CarouselItem.displayName = "CarouselItem" + +const CarouselPrevious = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "sm", ...props }, ref) => { + const { orientation, scrollPrev, canScrollPrev } = useCarousel() + + return ( + + ) +}) +CarouselPrevious.displayName = "CarouselPrevious" + +const CarouselNext = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "sm", ...props }, ref) => { + const { orientation, scrollNext, canScrollNext } = useCarousel() + + return ( + + ) +}) +CarouselNext.displayName = "CarouselNext" + +export { + Carousel, + type CarouselApi, + CarouselContent, + CarouselItem, + CarouselNext, + CarouselPrevious, +} diff --git a/src/data/roadmap/releases.tsx b/src/data/roadmap/releases.tsx new file mode 100644 index 00000000000..571ce583f48 --- /dev/null +++ b/src/data/roadmap/releases.tsx @@ -0,0 +1,186 @@ +import { StaticImageData } from "next/image" + +import CommunityHeroImage from "@/public/images/heroes/community-hero.png" +import DevelopersHubHeroImage from "@/public/images/heroes/developers-hub-hero.jpg" +import GuidesHubHeroImage from "@/public/images/heroes/guides-hub-hero.jpg" +import Layer2HubHeroImage from "@/public/images/heroes/layer-2-hub-hero.jpg" +import QuizzesHubHeroImage from "@/public/images/heroes/quizzes-hub-hero.png" +import PectraImage from "@/public/images/roadmap/roadmap-pectra.png" + +interface Release { + image: StaticImageData + releaseName: string + releaseDate: string + content: React.ReactNode + href: string +} + +export const releasesData: Release[] = [ + { + image: DevelopersHubHeroImage, + releaseName: "Paris (The Merge)", + releaseDate: "2022-09-15", + content: ( +
+

Transition to Proof of Stake

+
    +
  • Replaced energy-intensive mining with staking-based consensus
  • +
  • Reduced Ethereum's energy consumption by ~99.95%
  • +
+

Beacon Chain Integration

+
    +
  • Merged the Beacon Chain with the Ethereum mainnet
  • +
  • Enabled the full transition to PoS consensus mechanism
  • +
+

Difficulty Bomb Removal

+
    +
  • + Removed the difficulty bomb that was increasing mining difficulty +
  • +
  • Ensured smooth transition to the new consensus mechanism
  • +
+
+ ), + href: "/upgrades/merge", + }, + { + image: QuizzesHubHeroImage, + releaseName: "Shapella", + releaseDate: "2023-04-12", + content: ( +
+

Staking withdrawals

+
    +
  • Enabled validators to withdraw their staked ETH and rewards
  • +
  • Introduced partial and full withdrawal capabilities
  • +
+

EIP-4895: Beacon chain push withdrawals

+
    +
  • Added a new system-level operation for withdrawals
  • +
  • + Ensured secure and efficient processing of withdrawal requests +
  • +
+

EIP-3651: Warm COINBASE

+
    +
  • Reduced gas costs for accessing the COINBASE address
  • +
  • Improved efficiency of certain smart contract operations
  • +
+
+ ), + href: "/staking/withdrawals", + }, + { + image: Layer2HubHeroImage, + releaseName: "Dencun", + releaseDate: "2024-03-13", + content: ( +
+

Proto-danksharding (EIP-4844)

+
    +
  • + Introduced blob transactions to significantly reduce rollup + transaction costs +
  • +
  • + Added a new transaction type that stores data temporarily and + cheaply +
  • +
+

EIP-1153: Transient storage opcodes

+
    +
  • + Added TSTORE and TLOAD opcodes for temporary storage during + transaction execution +
  • +
  • + Enables more efficient smart contract patterns and reduces gas costs +
  • +
+

EIP-4788: Beacon block root in the EVM

+
    +
  • Exposes consensus layer information to smart contracts
  • +
  • + Enables new trust-minimized applications and cross-chain bridges +
  • +
+
+ ), + href: "/roadmap/dencun", + }, + { + image: PectraImage, + releaseName: "Pectra", + releaseDate: "2025-05-07", + content: ( +
+

Enhance EOA wallets with smart contract functionality

+
    +
  • + Users can set their address to be represented by a code of an + existing smart contract and gain benefits such as transaction batching, transaction fee sponsorship or better recovery mechanisms +
  • +
+

Increase the max effective balance

+
    +
  • + Stakers can now choose an arbitrary amount of ETH to stake and + receive rewards on every 1 ETH above the minimum +
  • +
+

Blob throughput increase

+
    +
  • + The blob count will be increased from 3 to 6 targets, with a maximum + of 9, resulting in cheaper fees in Ethereum rollups +
  • +
+
+ ), + href: "/roadmap/pectra", + }, + { + image: CommunityHeroImage, + releaseName: "Fusaka", + releaseDate: "2026", + content: ( +
+

+ PeerDAS (Peer-to-Peer Data Availability Sampling) +

+
    +
  • Enables more efficient data availability for rollups
  • +
  • + Makes running a node more accessible while maintaining + decentralization +
  • +
+

Potential Additional Features

+
    +
  • + EIP-7688: Enhanced smart contract access to network information +
  • +
  • Blob fee market improvementse
  • +
  • + Further improvements to validator efficiency and network performance +
  • +
+
+ ), + href: "/roadmap/fusaka", + }, + { + image: GuidesHubHeroImage, + releaseName: "Glamsterdam", + releaseDate: "2026", + content: ( +
+

Discussed for Glamsterdam

+
    +
  • Verkle trees
  • +
+
+ ), + href: "https://eips.ethereum.org/EIPS/eip-7773", + }, +] diff --git a/src/intl/en/page-roadmap.json b/src/intl/en/page-roadmap.json new file mode 100644 index 00000000000..3a9faa530bc --- /dev/null +++ b/src/intl/en/page-roadmap.json @@ -0,0 +1,4 @@ +{ + "page-roadmap-meta-title": "Ethereum roadmap", + "page-roadmap-meta-description": "The path to more scalability, security and sustainability for Ethereum." +} \ No newline at end of file diff --git a/src/lib/utils/date.ts b/src/lib/utils/date.ts index 4f8f4555f8f..1bea2c4553b 100644 --- a/src/lib/utils/date.ts +++ b/src/lib/utils/date.ts @@ -6,3 +6,14 @@ export const isValidDate = (dateString?: string | number): boolean => { const date = new Date(dateString) return !isNaN(date.getTime()) } + +export const formatDate = (date: string) => { + if (/^\d{4}$/.test(date)) { + return date + } + return new Date(date).toLocaleDateString("en-US", { + month: "long", + day: "numeric", + year: "numeric", + }) +} diff --git a/src/lib/utils/translations.ts b/src/lib/utils/translations.ts index 352a9d024f2..6825585afcd 100644 --- a/src/lib/utils/translations.ts +++ b/src/lib/utils/translations.ts @@ -187,6 +187,10 @@ const getRequiredNamespacesForPath = (relativePath: string) => { requiredNamespaces = [...requiredNamespaces, "table"] } + if (path.startsWith("/roadmap/")) { + primaryNamespace = "page-roadmap" + } + if (path.startsWith("/start/")) { requiredNamespaces = [...requiredNamespaces] } diff --git a/src/styles/semantic-tokens.css b/src/styles/semantic-tokens.css index 8f97e469fa4..b8fb9a8be9f 100644 --- a/src/styles/semantic-tokens.css +++ b/src/styles/semantic-tokens.css @@ -85,6 +85,10 @@ #000000 69.77% ); + --card-gradient: linear-gradient(123deg, rgba(255, 255, 255, 0.20) 58.99%, rgba(174, 110, 203, 0.13) 104.04%); + --card-gradient-secondary: linear-gradient(95deg, rgba(211, 145, 242, 0.12) 0%, rgba(159, 43, 212, 0.12) 102.78%); + --card-gradient-secondary-hover: linear-gradient(95deg, rgba(211, 145, 242, 0.2) 0%, rgba(159, 43, 212, 0.2) 102.78%); + /* Shadows */ --shadow-color-a: hsla(var(--purple-800), 0.02); --shadow-color-b: hsla(var(--red-800), 0.04); @@ -198,6 +202,8 @@ #8db4ff 69.77% ); + --roadmap-card-gradient: linear-gradient(123deg, rgba(34, 34, 34, 0.20) 40.53%, rgba(174, 110, 203, 0.13) 104.05%); + /* Shadows (dark mode adjustments) */ --shadow-color: hsla(var(--white), 0.04); --shadow-svg-button-link-1: -2px 2px 12px 1px var(--shadow-color); diff --git a/tailwind.config.ts b/tailwind.config.ts index e555b78700a..268a8425fa4 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -231,6 +231,9 @@ const config = { "radial-a": "var(--radial-a)", "linear-bug-bounty-title": "var(--linear-bug-bounty-title)", "gradient-staking": "var(--gradient-staking)", + "card-gradient": "var(--card-gradient)", + "card-gradient-secondary": "var(--card-gradient-secondary)", + "card-gradient-secondary-hover": "var(--card-gradient-secondary-hover)", }, boxShadow: { "table-box": "var(--table-box-shadow)", diff --git a/yarn.lock b/yarn.lock index b6979aff574..0a2535d4268 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3265,6 +3265,11 @@ resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz#6f766faa975f8738269ebb8a23bad4f5a8d2faec" integrity sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw== +"@radix-ui/react-compose-refs@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30" + integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== + "@radix-ui/react-context@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c" @@ -3643,7 +3648,7 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.1" -"@radix-ui/react-slot@1.1.0", "@radix-ui/react-slot@^1.1.0": +"@radix-ui/react-slot@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84" integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw== @@ -3657,6 +3662,13 @@ dependencies: "@radix-ui/react-compose-refs" "1.1.1" +"@radix-ui/react-slot@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.0.tgz#57727fc186ddb40724ccfbe294e1a351d92462ba" + integrity sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w== + dependencies: + "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-switch@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.1.0.tgz#fcf8e778500f1d60d4b2bec2fc3fad77a7c118e3" @@ -8363,6 +8375,24 @@ elliptic@^6.5.7: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +embla-carousel-react@^8.6.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/embla-carousel-react/-/embla-carousel-react-8.6.0.tgz#b737042a32761c38d6614593653b3ac619477bd1" + integrity sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA== + dependencies: + embla-carousel "8.6.0" + embla-carousel-reactive-utils "8.6.0" + +embla-carousel-reactive-utils@8.6.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.6.0.tgz#607f1d8ab9921c906a555c206251b2c6db687223" + integrity sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A== + +embla-carousel@8.6.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/embla-carousel/-/embla-carousel-8.6.0.tgz#abcedff2bff36992ea8ac27cd30080ca5b6a3f58" + integrity sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA== + emoji-regex@^10.3.0: version "10.3.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23"