From 8ac3ae26235a2598f17f24d9ba4d1c1ec32b1358 Mon Sep 17 00:00:00 2001
From: Paul Wackerow <54227730+wackerow@users.noreply.github.com>
Date: Mon, 29 Sep 2025 13:21:21 -0700
Subject: [PATCH 1/3] refactor: bug-bounty page to ssr
---
.../bug-bounty/_components/bug-bounty.tsx | 801 ------------------
app/[locale]/bug-bounty/page.tsx | 633 +++++++++++++-
src/intl/en/page-bug-bounty.json | 2 +-
3 files changed, 612 insertions(+), 824 deletions(-)
delete mode 100644 app/[locale]/bug-bounty/_components/bug-bounty.tsx
diff --git a/app/[locale]/bug-bounty/_components/bug-bounty.tsx b/app/[locale]/bug-bounty/_components/bug-bounty.tsx
deleted file mode 100644
index c7711b64673..00000000000
--- a/app/[locale]/bug-bounty/_components/bug-bounty.tsx
+++ /dev/null
@@ -1,801 +0,0 @@
-"use client"
-
-import { HTMLAttributes } from "react"
-
-import type { ChildOnlyProp, PageWithContributorsProps } from "@/lib/types"
-
-/* Uncomment for Bug Bounty Banner: */
-import BugBountyBanner from "@/components/Banners/BugBountyBanner"
-import Breadcrumbs from "@/components/Breadcrumbs"
-import BugBountyCards from "@/components/BugBountyCards"
-import Card from "@/components/Card"
-import CardList from "@/components/CardList"
-import Emoji from "@/components/Emoji"
-import ExpandableCard from "@/components/ExpandableCard"
-import FeedbackCard from "@/components/FeedbackCard"
-import FileContributors from "@/components/FileContributors"
-import { Image, type ImageProps } from "@/components/Image"
-import Leaderboard from "@/components/Leaderboard"
-import MainArticle from "@/components/MainArticle"
-import Translation from "@/components/Translation"
-import { ButtonLink } from "@/components/ui/buttons/Button"
-import { Divider } from "@/components/ui/divider"
-import { Center, Flex, VStack } from "@/components/ui/flex"
-import InlineLink from "@/components/ui/Link"
-import { ListItem, UnorderedList } from "@/components/ui/list"
-
-import { cn } from "@/lib/utils/cn"
-
-import consensusData from "@/data/consensus-bounty-hunters.json"
-import executionData from "@/data/execution-bounty-hunters.json"
-
-import useColorModeValue from "@/hooks/useColorModeValue"
-import { useTranslation } from "@/hooks/useTranslation"
-import { usePathname } from "@/i18n/routing"
-import besu from "@/public/images/upgrades/besu.png"
-import erigon from "@/public/images/upgrades/erigon.png"
-import geth from "@/public/images/upgrades/geth.png"
-import grandine from "@/public/images/upgrades/grandine.png"
-import lighthouseDark from "@/public/images/upgrades/lighthouse-dark.png"
-import lighthouseLight from "@/public/images/upgrades/lighthouse-light.png"
-import lodestar from "@/public/images/upgrades/lodestar.png"
-import nethermind from "@/public/images/upgrades/nethermind.png"
-import nimbus from "@/public/images/upgrades/nimbus-cloud.png"
-import prysm from "@/public/images/upgrades/prysm.png"
-import reth from "@/public/images/upgrades/reth.png"
-import solidityDark from "@/public/images/upgrades/solidity-dark.png"
-import solidityLight from "@/public/images/upgrades/solidity-light.png"
-import tekuDark from "@/public/images/upgrades/teku-dark.png"
-import tekuLight from "@/public/images/upgrades/teku-light.png"
-import vyper from "@/public/images/upgrades/vyper.png"
-
-const Page = (props: ChildOnlyProp) => (
-
-)
-
-const Content = (props: ChildOnlyProp) => (
-
-)
-
-const Title = (props: ChildOnlyProp) => (
-
-)
-
-const H2 = (props: HTMLAttributes) => (
-
-)
-
-const H4 = (props: ChildOnlyProp) => (
-
-)
-
-const Subtitle = (props: ChildOnlyProp) => (
-
-)
-
-const Text = ({ className, ...props }: HTMLAttributes) => (
-
-)
-
-const SloganGradient = ({ children }: ChildOnlyProp) => (
-
-
{children}
-
-)
-
-const Rules = (props: ChildOnlyProp) => (
-
-)
-
-const SubmitInstructions = (props: ChildOnlyProp) => (
-
-)
-
-const GradientContainer = (props: ChildOnlyProp) => (
-
-)
-
-const LeaderboardContainer = (props: ChildOnlyProp) => (
-
-)
-
-const FullLeaderboardContainer = (props: ChildOnlyProp) => (
-
-)
-
-const On = () =>
-
-const Contact = (props: ChildOnlyProp) => (
-
-)
-
-const ButtonRow = (props: ChildOnlyProp) => (
-
-)
-
-const StyledButton = ({ children, ...props }) => (
-
- {children}
-
-)
-
-const ClientIntro = (props: ChildOnlyProp) => (
-
-)
-
-const ClientRow = (props: ChildOnlyProp) => (
-
-)
-
-const Client = (props: ChildOnlyProp) => (
-
-)
-
-const HeroCard = (props: ChildOnlyProp) => (
-
-)
-
-const HeroContainer = (props: ChildOnlyProp) => (
-
-)
-
-const Row = (props: ChildOnlyProp) => (
-
-)
-
-const StyledCardContainer = (props: ChildOnlyProp) => (
-
-)
-
-const StyledCard = ({ children, ...props }) => (
-
- {children}
-
-)
-
-const StyledGrayContainer = ({ children, ...props }) => (
-
- {children}
-
-)
-
-const Faq = (props: ChildOnlyProp) => (
-
-)
-
-const LeftColumn = (props: ChildOnlyProp) => (
-
-)
-
-const RightColumn = (props: ChildOnlyProp) => (
-
-)
-
-type BountyHuntersArg = { score?: number }
-
-type Node = {
- readonly name: string
- readonly username: string
- readonly score: number
-}
-
-type Client = {
- title: string
- link: string
- image: ImageProps["src"]
-}
-
-type Spec = {
- title: string
- link: string
-}
-
-type Language = {
- title: string
- link: string
- image: ImageProps["src"]
-}
-
-const sortBountyHuntersFn = (a: BountyHuntersArg, b: BountyHuntersArg) => {
- if (!a.score || !b.score) return 0
- return b.score - a.score
-}
-
-const BugBountiesPage = ({
- contributors,
- lastEditLocaleTimestamp,
-}: PageWithContributorsProps) => {
- const pathname = usePathname()
- const { t } = useTranslation("page-bug-bounty")
-
- const consensusBountyHunters: Node[] = consensusData.sort(sortBountyHuntersFn)
- const executionBountyHunters: Node[] = executionData.sort(sortBountyHuntersFn)
-
- const bountyHuntersArrayToObject: Record = [
- ...consensusData,
- ...executionData,
- ].reduce((acc, next) => {
- const name = next.name
- if (!name) return acc
-
- if (acc[name]) {
- return {
- ...acc,
- [name]: {
- ...next,
- score: acc[name].score + next.score,
- },
- }
- }
-
- return {
- ...acc,
- [name]: next,
- }
- }, {})
-
- // total all counts using name as identifier, then sort
- const allBounterHunters = Object.values(bountyHuntersArrayToObject).sort(
- (a, b) => b.score - a.score
- )
-
- const clients: Client[] = [
- {
- title: "Besu",
- link: "https://besu.hyperledger.org/en/stable/",
- image: besu,
- },
- {
- title: "Erigon",
- link: "https://github.com/ledgerwatch/erigon",
- image: erigon,
- },
- {
- title: "Geth",
- link: "https://geth.ethereum.org/",
- image: geth,
- },
- {
- title: "Lighthouse",
- link: "https://lighthouse-book.sigmaprime.io/",
- image: useColorModeValue(lighthouseLight, lighthouseDark),
- },
- {
- title: "Lodestar",
- link: "https://chainsafe.github.io/lodestar/",
- image: lodestar,
- },
- {
- title: "Nimbus",
- link: "https://our.status.im/tag/nimbus/",
- image: nimbus,
- },
- {
- title: "Nethermind",
- link: "https://docs.nethermind.io/",
- image: nethermind,
- },
- {
- title: "Prysm",
- link: "https://prylabs.net/",
- image: prysm,
- },
- {
- title: "Reth",
- link: "https://reth.rs/",
- image: reth,
- },
- {
- title: "Teku",
- link: "https://pegasys.tech/teku",
- image: useColorModeValue(tekuDark, tekuLight),
- },
- {
- title: "Grandine",
- link: "https://grandine.io/",
- image: grandine,
- },
- ]
-
- const specs: Spec[] = [
- {
- title: t("page-upgrades-bug-bounty-title-1"),
- link: "https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md",
- },
- {
- title: t("page-upgrades-bug-bounty-title-2"),
- link: "https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/fork-choice.md",
- },
- {
- title: t("page-upgrades-bug-bounty-title-3"),
- link: "https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/deposit-contract.md",
- },
- {
- title: t("page-upgrades-bug-bounty-title-4"),
- link: "https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md",
- },
- ]
-
- const languages: Language[] = [
- {
- title: "Solidity",
- link: "https://soliditylang.org/",
- image: useColorModeValue(solidityLight, solidityDark),
- },
- {
- title: "Vyper",
- link: "https://vyperlang.org/",
- image: vyper,
- },
- ]
-
- const iconImageProps = {
- width: 60,
- }
- return (
-
- {/* Uncomment for Bug Bounty Banner: */}
-
-
-
-
-
-
-
- {t("page-upgrades-bug-bounty-title")}
-
-
- {t("page-upgrades-bug-bounty-slogan")}
-
- {t("page-upgrades-bug-bounty-subtitle")}
-
-
- {t("page-upgrades-bug-bounty-submit")}
-
-
- {t("page-upgrades-bug-bounty-rules")}
-
-
-
-
-
-
- {t("page-upgrades-bug-bounty-leaderboard")}
-
-
-
-
- {t("page-upgrades-bug-bounty-clients")}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {t("page-upgrades-bug-bounty-validity")}
-
-
-
-
-
-
-
- {t("page-upgrades-bug-bounty-specs")}
-
-
-
- {t("page-upgrades-bug-bounty-execution-specs")}
-
-
-
- {t("page-upgrades-bug-bounty-annotations")}
-
-
-
- Ben Edgington's{" "}
- {t("page-upgrades-bug-bounty-annotated-specs")}
-
-
-
-
- Vitalik Buterin's{" "}
- {t("page-upgrades-bug-bounty-annotated-specs")}
-
-
-
-
-
-
{t("page-upgrades-bug-bounty-types")}
-
- {t("page-upgrades-bug-bounty-type-1")}
- {t("page-upgrades-bug-bounty-type-2")}
- {t("page-upgrades-bug-bounty-type-3")}
- {t("page-upgrades-bug-bounty-type-4")}
-
-
-
-
{t("page-upgrades-bug-bounty-specs-docs")}
-
-
-
-
-
-
-
-
-
{t("page-upgrades-bug-bounty-types")}
-
-
- {t("page-upgrades-bug-bounty-clients-type-1")}
-
-
- {t("page-upgrades-bug-bounty-clients-type-2")}
-
-
- {t("page-upgrades-bug-bounty-clients-type-3")}
-
-
-
-
-
{t("page-upgrades-bug-bounty-help-links")}
-
-
-
-
-
- {t("page-upgrades-bug-bounty-misc-bugs-desc-2")}
-
-
-
{t("page-upgrades-bug-bounty-help-links")}
-
-
-
-
-
-
{t("page-upgrades-bug-bounty-help-links")}
-
- Deposit Contract Specifications
-
-
-
- Deposit Contract Source Code
-
-
-
-
-
-
{t("page-upgrades-bug-bounty-help-links")}
-
- C-KZG-4844
-
-
-
- Go-KZG-4844
-
-
-
-
- {t("page-upgrades-bug-bounty-not-included")}
- {t("page-upgrades-bug-bounty-not-included-desc")}
-
-
-
-
-
- {t("page-upgrades-bug-bounty-submit")}
-
- {t("page-upgrades-bug-bounty-submit-desc")}{" "}
-
- {t("page-upgrades-bug-bounty-owasp")}
-
-
- {t("page-upgrades-bug-bounty-points")}
-
- {t("page-upgrades-bug-bounty-quality")}
- {t("page-upgrades-bug-bounty-quality-desc")}
-
-
- {t("page-upgrades-bug-bounty-quality-repro")}
- {t("page-upgrades-bug-bounty-quality-repro-desc")}
-
-
-
-
-
-
-
-
-
-
- {t("page-upgrades-bug-bounty-hunting")}
-
- {t("page-upgrades-bug-bounty-hunting-desc")}
-
-
- {t("page-upgrades-bug-bounty-hunting-li-1")}
- {t("page-upgrades-bug-bounty-hunting-li-2")}
- {t("page-upgrades-bug-bounty-hunting-li-3")}
-
- {t("page-upgrades-bug-bounty-hunting-li-4")}
-
-
-
-
-
-
- {t("page-upgrades-bug-bounty-hunting-execution-leaderboard")}
-
- {t(
- "page-upgrades-bug-bounty-hunting-execution-leaderboard-subtitle"
- )}
-
-
-
-
- {t("page-upgrades-bug-bounty-hunting-leaderboard")}
-
- {t("page-upgrades-bug-bounty-hunting-leaderboard-subtitle")}
-
-
-
-
-
-
-
- {t("page-upgrades-question-title")}
-
-
-
-
- }
- contentPreview={
-
- }
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- contentPreview={
-
- }
- >
-
- {
-
- }
-
-
-
- }
- contentPreview={
-
- }
- >
-
- {
-
- }
-
-
-
- }
- contentPreview={
-
- }
- >
-
- {
-
- }
-
-
-
-
-
- }
- contentPreview={
-
- }
- >
-
- {
-
- }
-
-
-
- }
- contentPreview={
-
- }
- >
-
- {
-
- }
-
-
- {
-
- }
-
-
-
- }
- contentPreview={
-
- }
- >
-
- {
-
- }
-
-
-
- }
- contentPreview={
-
- }
- >
-
- {
-
- }
-
-
- { }
-
-
-
-
-
-
-
-
-
-
- {t("page-upgrades-bug-bounty-questions")}
-
-
- {t("page-upgrades-bug-bounty-email-us")}{" "}
-
- bounty@ethereum.org
-
-
-
-
-
-
-
- )
-}
-
-export default BugBountiesPage
diff --git a/app/[locale]/bug-bounty/page.tsx b/app/[locale]/bug-bounty/page.tsx
index 18ad244c64d..655d4ccb709 100644
--- a/app/[locale]/bug-bounty/page.tsx
+++ b/app/[locale]/bug-bounty/page.tsx
@@ -1,30 +1,113 @@
-import { pick } from "lodash"
-import {
- getMessages,
- getTranslations,
- setRequestLocale,
-} from "next-intl/server"
+import { getTranslations } from "next-intl/server"
+import type { ComponentProps } from "react"
-import type { CommitHistory, Lang, Params } from "@/lib/types"
+import type { ChildOnlyProp, CommitHistory, Lang, Params } from "@/lib/types"
-import I18nProvider from "@/components/I18nProvider"
+/* Uncomment for Bug Bounty Banner: */
+import BugBountyBanner from "@/components/Banners/BugBountyBanner"
+import Breadcrumbs from "@/components/Breadcrumbs"
+import BugBountyCards from "@/components/BugBountyCards"
+import Card from "@/components/Card"
+import CardList, { CardProps } from "@/components/CardList"
+import Emoji from "@/components/Emoji"
+import ExpandableCard from "@/components/ExpandableCard"
+import FeedbackCard from "@/components/FeedbackCard"
+import FileContributors from "@/components/FileContributors"
+import { Image } from "@/components/Image"
+import Leaderboard from "@/components/Leaderboard"
+import MainArticle from "@/components/MainArticle"
+import { ButtonLink } from "@/components/ui/buttons/Button"
+import { Divider } from "@/components/ui/divider"
+import { Center, Flex, VStack } from "@/components/ui/flex"
+import InlineLink from "@/components/ui/Link"
+import { ListItem, UnorderedList } from "@/components/ui/list"
+import { cn } from "@/lib/utils/cn"
import { getAppPageContributorInfo } from "@/lib/utils/contributors"
import { getMetadata } from "@/lib/utils/metadata"
-import { getRequiredNamespacesForPage } from "@/lib/utils/translations"
-import BugBountiesPage from "./_components/bug-bounty"
+import consensusData from "@/data/consensus-bounty-hunters.json"
+import executionData from "@/data/execution-bounty-hunters.json"
+
import BugBountyJsonLD from "./page-jsonld"
+import besu from "@/public/images/upgrades/besu.png"
+import erigon from "@/public/images/upgrades/erigon.png"
+import geth from "@/public/images/upgrades/geth.png"
+import grandine from "@/public/images/upgrades/grandine.png"
+import lighthouseLight from "@/public/images/upgrades/lighthouse-light.png"
+import lodestar from "@/public/images/upgrades/lodestar.png"
+import nethermind from "@/public/images/upgrades/nethermind.png"
+import nimbus from "@/public/images/upgrades/nimbus-cloud.png"
+import prysm from "@/public/images/upgrades/prysm.png"
+import reth from "@/public/images/upgrades/reth.png"
+import solidityLight from "@/public/images/upgrades/solidity-light.png"
+import tekuLight from "@/public/images/upgrades/teku-dark.png"
+import vyper from "@/public/images/upgrades/vyper.png"
+
+const Content = (props: ChildOnlyProp) => (
+
+)
+
+const H2 = ({ className, ...props }: ComponentProps<"h2">) => (
+
+)
+
+const H4 = (props: ChildOnlyProp) => (
+
+)
+
+const Text = ({ className, ...props }: ComponentProps<"p">) => (
+
+)
+
+const FullLeaderboardContainer = (props: ChildOnlyProp) => (
+
+)
+
+const ClientRow = (props: ChildOnlyProp) => (
+
+)
+
+const Client = (props: ChildOnlyProp) => (
+
+)
+
+const Row = (props: ChildOnlyProp) => (
+
+)
+
+const StyledCard = ({ children, ...props }) => (
+
+ {children}
+
+)
+
+type CardDetails = Required> &
+ Pick
+
+type Node = {
+ readonly name: string
+ readonly username: string
+ readonly score: number
+}
+
+type Spec = {
+ title: string
+ link: string
+}
+
+type BountyHuntersArg = { score?: number }
+
+const sortBountyHuntersFn = (a: BountyHuntersArg, b: BountyHuntersArg) => {
+ if (!a.score || !b.score) return 0
+ return b.score - a.score
+}
+
export default async function Page({ params }: { params: Promise }) {
const { locale } = await params
- setRequestLocale(locale)
-
- // Get i18n messages
- const allMessages = await getMessages({ locale })
- const requiredNamespaces = getRequiredNamespacesForPage("/bug-bounty")
- const messages = pick(allMessages, requiredNamespaces)
+ const t = await getTranslations({ namespace: "page-bug-bounty" })
const commitHistoryCache: CommitHistory = {}
const { contributors, lastEditLocaleTimestamp } =
@@ -34,15 +117,521 @@ export default async function Page({ params }: { params: Promise }) {
commitHistoryCache
)
+ const consensusBountyHunters: Node[] = consensusData.sort(sortBountyHuntersFn)
+ const executionBountyHunters: Node[] = executionData.sort(sortBountyHuntersFn)
+
+ const bountyHuntersArrayToObject: Record = [
+ ...consensusData,
+ ...executionData,
+ ].reduce((acc, next) => {
+ const name = next.name
+ if (!name) return acc
+
+ if (acc[name]) {
+ return {
+ ...acc,
+ [name]: {
+ ...next,
+ score: acc[name].score + next.score,
+ },
+ }
+ }
+
+ return {
+ ...acc,
+ [name]: next,
+ }
+ }, {})
+
+ // total all counts using name as identifier, then sort
+ const allBounterHunters = Object.values(bountyHuntersArrayToObject).sort(
+ (a, b) => b.score - a.score
+ )
+
+ const clients: CardDetails[] = [
+ {
+ title: "Besu",
+ link: "https://besu.hyperledger.org/en/stable/",
+ image: besu,
+ },
+ {
+ title: "Erigon",
+ link: "https://github.com/ledgerwatch/erigon",
+ image: erigon,
+ },
+ {
+ title: "Geth",
+ link: "https://geth.ethereum.org/",
+ image: geth,
+ },
+ {
+ title: "Lighthouse",
+ link: "https://lighthouse-book.sigmaprime.io/",
+ image: lighthouseLight,
+ className: "[&_img]:dark:invert",
+ },
+ {
+ title: "Lodestar",
+ link: "https://chainsafe.github.io/lodestar/",
+ image: lodestar,
+ },
+ {
+ title: "Nimbus",
+ link: "https://our.status.im/tag/nimbus/",
+ image: nimbus,
+ },
+ {
+ title: "Nethermind",
+ link: "https://docs.nethermind.io/",
+ image: nethermind,
+ },
+ {
+ title: "Prysm",
+ link: "https://prylabs.net/",
+ image: prysm,
+ },
+ {
+ title: "Reth",
+ link: "https://reth.rs/",
+ image: reth,
+ },
+ {
+ title: "Teku",
+ link: "https://pegasys.tech/teku",
+ image: tekuLight,
+ className: "[&_img]:dark:invert",
+ },
+ {
+ title: "Grandine",
+ link: "https://grandine.io/",
+ image: grandine,
+ },
+ ]
+
+ const specs: Spec[] = [
+ {
+ title: t("page-upgrades-bug-bounty-title-1"),
+ link: "https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md",
+ },
+ {
+ title: t("page-upgrades-bug-bounty-title-2"),
+ link: "https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/fork-choice.md",
+ },
+ {
+ title: t("page-upgrades-bug-bounty-title-3"),
+ link: "https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/deposit-contract.md",
+ },
+ {
+ title: t("page-upgrades-bug-bounty-title-4"),
+ link: "https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/p2p-interface.md",
+ },
+ ]
+
+ const languages: CardDetails[] = [
+ {
+ title: "Solidity",
+ link: "https://soliditylang.org/",
+ image: solidityLight,
+ className: "[&_img]:dark:invert",
+ },
+ {
+ title: "Vyper",
+ link: "https://vyperlang.org/",
+ image: vyper,
+ },
+ ]
+
+ const iconImageProps = (duotone?: boolean) => ({
+ className: cn("w-[60px]", duotone && "dark:invert"),
+ sizes: "60px",
+ })
+
return (
<>
-
-
-
+
+ {/* Uncomment for Bug Bounty Banner: */}
+
+
+
+
+
+
+
{" "}
+
+ {t("page-upgrades-bug-bounty-title")}
+
+
+
+
+ {t("page-upgrades-bug-bounty-slogan")}
+
+
+
+
+ {t("page-upgrades-bug-bounty-subtitle")}
+
+
+
+ {t("page-upgrades-bug-bounty-submit")}
+
+
+ {t("page-upgrades-bug-bounty-rules")}
+
+
+
+
+
+
+ {t("page-upgrades-bug-bounty-leaderboard")}
+
+
+
+
+
+ {t("page-upgrades-bug-bounty-clients")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{t("page-upgrades-bug-bounty-validity")}
+ {t("page-upgrades-bug-bounty-validity-desc")}
+
+
+
+
+ {t("page-upgrades-bug-bounty-specs")}
+
+
+
+ {t("page-upgrades-bug-bounty-execution-specs")}
+
+
+
+ {t("page-upgrades-bug-bounty-annotations")}
+
+
+
+ Ben Edgington's{" "}
+ {t("page-upgrades-bug-bounty-annotated-specs")}
+
+
+
+
+ Vitalik Buterin's{" "}
+ {t("page-upgrades-bug-bounty-annotated-specs")}
+
+
+
+
+
+
{t("page-upgrades-bug-bounty-types")}
+
+ {t("page-upgrades-bug-bounty-type-1")}
+ {t("page-upgrades-bug-bounty-type-2")}
+ {t("page-upgrades-bug-bounty-type-3")}
+ {t("page-upgrades-bug-bounty-type-4")}
+
+
+
+
{t("page-upgrades-bug-bounty-specs-docs")}
+
+
+
+
+
+
+ {t("page-upgrades-bug-bounty-client-bugs-desc-2")}
+
+
{t("page-upgrades-bug-bounty-types")}
+
+
+ {t("page-upgrades-bug-bounty-clients-type-1")}
+
+
+ {t("page-upgrades-bug-bounty-clients-type-2")}
+
+
+ {t("page-upgrades-bug-bounty-clients-type-3")}
+
+
+
+
+
{t("page-upgrades-bug-bounty-help-links")}
+
+
+
+
+
+ {t("page-upgrades-bug-bounty-misc-bugs-desc-2")}
+
+
+
{t("page-upgrades-bug-bounty-help-links")}
+
+
+
+
+
+
{t("page-upgrades-bug-bounty-help-links")}
+
+ Deposit Contract Specifications
+
+
+
+ Deposit Contract Source Code
+
+
+
+
+
+
{t("page-upgrades-bug-bounty-help-links")}
+
+ C-KZG-4844
+
+
+
+ Go-KZG-4844
+
+
+
+
+
+
+ {t("page-upgrades-bug-bounty-not-included")}
+
+
+ {t.rich("page-upgrades-bug-bounty-not-included-desc", {
+ a: (chunks) => (
+ {chunks}
+ ),
+ })}
+
+
+
+
+
+
+
+
{t("page-upgrades-bug-bounty-submit")}
+
+ {t("page-upgrades-bug-bounty-submit-desc")}{" "}
+
+ {t("page-upgrades-bug-bounty-owasp")}
+
+
+ {t("page-upgrades-bug-bounty-points")}
+
+
+ {t("page-upgrades-bug-bounty-quality")}
+
+ {t("page-upgrades-bug-bounty-quality-desc")}
+
+
+
+ {t("page-upgrades-bug-bounty-quality-repro")}
+
+ {t("page-upgrades-bug-bounty-quality-repro-desc")}
+
+ {t("page-upgrades-bug-bounty-quality-fix")}
+
+
+
+
+
+
+
{t("page-upgrades-bug-bounty-hunting")}
+
+ {t("page-upgrades-bug-bounty-hunting-desc")}
+
+
+ {t("page-upgrades-bug-bounty-hunting-li-1")}
+ {t("page-upgrades-bug-bounty-hunting-li-2")}
+ {t("page-upgrades-bug-bounty-hunting-li-3")}
+
+ {t("page-upgrades-bug-bounty-hunting-li-4")}
+
+
+
+
+
+
+
+ {t("page-upgrades-bug-bounty-hunting-execution-leaderboard")}
+
+
+ {t(
+ "page-upgrades-bug-bounty-hunting-execution-leaderboard-subtitle"
+ )}
+
+
+
+
+
+ {t("page-upgrades-bug-bounty-hunting-leaderboard")}
+
+
+ {t("page-upgrades-bug-bounty-hunting-leaderboard-subtitle")}
+
+
+
+
+
+
+
+
+ {t("page-upgrades-question-title")}
+
+
+
+
+
+ {t("bug-bounty-faq-q1-content-1")}
+ {t("bug-bounty-faq-q1-content-2")}
+ {t("bug-bounty-faq-q1-content-3")}
+ {t("bug-bounty-faq-q1-content-4")}
+ {t("bug-bounty-faq-q1-content-5")}
+ {t("bug-bounty-faq-q1-content-6")}
+ {t("bug-bounty-faq-q1-content-7")}
+
+
+ {t("bug-bounty-faq-q2-content-1")}
+
+
+ {t("bug-bounty-faq-q3-content-1")}
+
+
+ {t("bug-bounty-faq-q4-content-1")}
+
+
+
+
+ {t("bug-bounty-faq-q5-content-1")}
+
+
+ {t("bug-bounty-faq-q6-content-1")}
+ {t("bug-bounty-faq-q6-content-2")}
+
+
+ {t("bug-bounty-faq-q7-content-1")}
+
+
+ {t("bug-bounty-faq-q8-content-1")}
+
+ {t("bug-bounty-faq-q8-PGP-key")}
+
+
+
+
+
+
+
+
+
+
+ {t("page-upgrades-bug-bounty-questions")}
+
+
+ {t("page-upgrades-bug-bounty-email-us")}{" "}
+
+ bounty@ethereum.org
+
+
+
+
+
+
+
>
)
}
diff --git a/src/intl/en/page-bug-bounty.json b/src/intl/en/page-bug-bounty.json
index 0719e9adbb9..9ee50f1a83a 100644
--- a/src/intl/en/page-bug-bounty.json
+++ b/src/intl/en/page-bug-bounty.json
@@ -36,7 +36,7 @@
"page-upgrades-bug-bounty-meta-description": "An overview of the Ethereum bug bounty program: how to get involved and reward information.",
"page-upgrades-bug-bounty-meta-title": "Ethereum Bug Bounty Program",
"page-upgrades-bug-bounty-not-included": "Out of scope",
- "page-upgrades-bug-bounty-not-included-desc": "Only the targets listed under in-scope are part of the Bug Bounty Program. This means that for example our infrastructure; such as webpages, dns, email etc, are not part of the bounty-scope. ERC20 contract bugs are typically not included in the bounty scope. However, we can help reach out to affected parties, such as authors or exchanges in such cases. ENS is maintained by the ENS foundation, and is not part of the bounty scope. Vulnerabilities requiring the user to have publicly exposed an API, such as JSON-RPC or the Beacon API, is out of scope of the bug bounty program.",
+ "page-upgrades-bug-bounty-not-included-desc": "Only the targets listed under in-scope are part of the Bug Bounty Program. This means that for example our infrastructure; such as webpages, dns, email etc, are not part of the bounty-scope. ERC-20 contract bugs are typically not included in the bounty scope. However, we can help reach out to affected parties, such as authors or exchanges in such cases. ENS is maintained by the ENS foundation, and is not part of the bounty scope. Vulnerabilities requiring the user to have publicly exposed an API, such as JSON-RPC or the Beacon API, is out of scope of the bug bounty program.",
"page-upgrades-bug-bounty-owasp": "View OWASP method",
"page-upgrades-bug-bounty-points": "The EF will also provide rewards based on:",
"page-upgrades-bug-bounty-points-error": "Error loading data... please refresh.",
From d68bbdbdd6ca89b124ee64ec188ad2ec362a0374 Mon Sep 17 00:00:00 2001
From: Paul Wackerow <54227730+wackerow@users.noreply.github.com>
Date: Mon, 29 Sep 2025 14:04:27 -0700
Subject: [PATCH 2/3] refactor: cards to grid; fix spacing, semantic naming
---
src/components/BugBountyCards.tsx | 174 ++++++++++++------------------
1 file changed, 66 insertions(+), 108 deletions(-)
diff --git a/src/components/BugBountyCards.tsx b/src/components/BugBountyCards.tsx
index 7ab763870fd..2db59f97921 100644
--- a/src/components/BugBountyCards.tsx
+++ b/src/components/BugBountyCards.tsx
@@ -1,49 +1,16 @@
import { BaseHTMLAttributes } from "react"
-import type { ChildOnlyProp, TranslationKey } from "@/lib/types"
+import type { TranslationKey } from "@/lib/types"
import { cn } from "@/lib/utils/cn"
-import { ButtonLink, ButtonLinkProps } from "./ui/buttons/Button"
-import { Center, Flex, Stack } from "./ui/flex"
+import { ButtonLink } from "./ui/buttons/Button"
+import { Center } from "./ui/flex"
import { useTranslation } from "@/hooks/useTranslation"
type FlexProps = BaseHTMLAttributes
-const CardRow = ({ children }: ChildOnlyProp) => (
- {children}
-)
-
-const SubmitBugBountyButton = ({
- children,
- ...props
-}: Omit) => (
-
- {children}
-
-)
-
-const Card = ({ children, ...props }: FlexProps) => {
- return (
-
- {children}
-
- )
-}
-
type LabelVariant = "low" | "medium" | "high" | "critical"
type LabelProps = FlexProps & {
@@ -62,10 +29,7 @@ const Label = ({ children, variant = "medium", ...props }: LabelProps) => {
return (
{children}
@@ -73,36 +37,6 @@ const Label = ({ children, variant = "medium", ...props }: LabelProps) => {
)
}
-const H2 = ({ children, ...props }) => (
-
- {children}
-
-)
-
-const Description = ({ children, ...props }) => (
-
- {children}
-
-)
-
-const SubHeader = ({ children, ...props }) => (
-
- {children}
-
-)
-
-const TextBox = ({
- children,
- ...props
-}: BaseHTMLAttributes) => (
-
- {children}
-
-)
-
export interface BugBountyCardInfo {
lowLabelTranslationId?: TranslationKey
mediumLabelTranslationId?: TranslationKey
@@ -177,48 +111,72 @@ const bugBountyCardsInfo: BugBountyCardInfo[] = [
const BugBountyCards = () => {
const { t } = useTranslation("page-bug-bounty")
+
+ const Banner = ({ card }: { card: BugBountyCardInfo }) => {
+ if (card.lowLabelTranslationId)
+ return {t(card.lowLabelTranslationId)}
+ if (card.mediumLabelTranslationId)
+ return {t(card.mediumLabelTranslationId)}
+ if (card.highLabelTranslationId)
+ return {t(card.highLabelTranslationId)}
+ if (card.criticalLabelTranslationId)
+ return (
+ {t(card.criticalLabelTranslationId)}
+ )
+ return <>>
+ }
return (
-
+
{bugBountyCardsInfo.map((card, idx) => (
-
- {card.lowLabelTranslationId && (
- {t(card.lowLabelTranslationId)}
- )}
- {card.mediumLabelTranslationId && (
- {t(card.mediumLabelTranslationId)}
- )}
- {card.highLabelTranslationId && (
- {t(card.highLabelTranslationId)}
- )}
- {card.criticalLabelTranslationId && (
-
- {t(card.criticalLabelTranslationId)}
-
+ {t(card.h2TranslationId)}
-
{t(card.descriptionTranslationId)}
-
{t(card.subDescriptionTranslationId)}
-
-
{t(card.subHeader1TranslationId)}
-
-
- {card.severityList.map((listItemId) => (
-
- {t(listItemId)}
- {/* */}
-
- ))}
-
-
-
-
{t(card.subHeader2TranslationId)}
-
{t(card.textTranslationId)}
-
- {t(card.styledButtonTranslationId)}
-
-
+ >
+
+
+
+
+
+ {t(card.h2TranslationId)}
+
+
+ {t(card.descriptionTranslationId)}
+
+
+
+
+ {t(card.subDescriptionTranslationId)}
+
+
+
+
+ {t(card.subHeader1TranslationId)}
+
+
+ {card.severityList.map((listItemId) => (
+ {t(listItemId)}
+ ))}
+
+
+
+
+
+ {t(card.subHeader2TranslationId)}
+
+
{t(card.textTranslationId)}
+
+
+
+ {t(card.styledButtonTranslationId)}
+
+
+
))}
-
+
)
}
From e1c506f2e53590823784b07cdd3ae2aaf8c3dc66 Mon Sep 17 00:00:00 2001
From: Paul Wackerow <54227730+wackerow@users.noreply.github.com>
Date: Mon, 29 Sep 2025 14:10:16 -0700
Subject: [PATCH 3/3] fix: nested links in intl strings
---
app/[locale]/bug-bounty/page.tsx | 25 ++++++++++++++++++++++---
src/intl/en/page-bug-bounty.json | 4 ++--
2 files changed, 24 insertions(+), 5 deletions(-)
diff --git a/app/[locale]/bug-bounty/page.tsx b/app/[locale]/bug-bounty/page.tsx
index 655d4ccb709..fa3b1f2803b 100644
--- a/app/[locale]/bug-bounty/page.tsx
+++ b/app/[locale]/bug-bounty/page.tsx
@@ -14,12 +14,14 @@ import ExpandableCard from "@/components/ExpandableCard"
import FeedbackCard from "@/components/FeedbackCard"
import FileContributors from "@/components/FileContributors"
import { Image } from "@/components/Image"
+import { Strong } from "@/components/IntlStringElements"
import Leaderboard from "@/components/Leaderboard"
import MainArticle from "@/components/MainArticle"
import { ButtonLink } from "@/components/ui/buttons/Button"
import { Divider } from "@/components/ui/divider"
import { Center, Flex, VStack } from "@/components/ui/flex"
import InlineLink from "@/components/ui/Link"
+import Link from "@/components/ui/Link"
import { ListItem, UnorderedList } from "@/components/ui/list"
import { cn } from "@/lib/utils/cn"
@@ -338,7 +340,18 @@ export default async function Page({ params }: { params: Promise }) {
{t("page-upgrades-bug-bounty-validity")}
- {t("page-upgrades-bug-bounty-validity-desc")}
+
+ {t.rich("page-upgrades-bug-bounty-validity-desc", {
+ mailto: (chunks) => (
+ {chunks}
+ ),
+ a: (chunks) => (
+
+ {chunks}
+
+ ),
+ })}
+
}) {
{t("page-upgrades-bug-bounty-submit")}
- {t("page-upgrades-bug-bounty-submit-desc")}{" "}
+ {t.rich("page-upgrades-bug-bounty-submit-desc", {
+ strong: Strong,
+ })}{" "}
{t("page-upgrades-bug-bounty-owasp")}
@@ -495,7 +510,11 @@ export default async function Page({ params }: { params: Promise }) {
{t("page-upgrades-bug-bounty-quality-repro-desc")}
-
{t("page-upgrades-bug-bounty-quality-fix")}
+
+ {t.rich("page-upgrades-bug-bounty-quality-fix", {
+ strong: Strong,
+ })}
+
diff --git a/src/intl/en/page-bug-bounty.json b/src/intl/en/page-bug-bounty.json
index 9ee50f1a83a..1cb8a47e157 100644
--- a/src/intl/en/page-bug-bounty.json
+++ b/src/intl/en/page-bug-bounty.json
@@ -58,7 +58,7 @@
"page-upgrades-bug-bounty-execution-specs": "Execution Layer Specifications",
"page-upgrades-bug-bounty-specs-docs": "Specification documents",
"page-upgrades-bug-bounty-submit": "Submit a bug",
- "page-upgrades-bug-bounty-submit-desc": "For each valid bug you find you’ll earn rewards. The quantity of rewards awarded will vary depending on Severity. The severity is calculated according to the OWASP risk rating model based on Impact on the Ethereum Network and Likelihood.",
+ "page-upgrades-bug-bounty-submit-desc": "For each valid bug you find you’ll earn rewards. The quantity of rewards awarded will vary depending on severity . The severity is calculated according to the OWASP risk rating model based on Impact on the Ethereum Network and Likelihood.",
"page-upgrades-bug-bounty-subtitle": "Earn up to 250,000 USD and a place on the leaderboard by finding protocol, client and language compiler bugs affecting the Ethereum network.",
"page-upgrades-bug-bounty-title": "Open for submissions",
"page-upgrades-bug-bounty-title-1": "Beacon Chain",
@@ -71,7 +71,7 @@
"page-upgrades-bug-bounty-type-4": "Calculation or parameter inconsistencies",
"page-upgrades-bug-bounty-types": "Types of bugs",
"page-upgrades-bug-bounty-validity": "In Scope",
- "page-upgrades-bug-bounty-validity-desc": "Our bug bounty program spans end-to-end: from soundness of protocols (such as the blockchain consensus model, the wire and p2p protocols, proof of stake, etc.) and protocol/implementation compliance to network security and consensus integrity. Classical client security as well as security of cryptographic primitives are also part of the program. When in doubt, send an email to bounty@ethereum.org and ask us. You may also submit a disclosure/vulnerability directly to bounty@ethereum.org , in which case we ask that you encrypt the message using our PGP Key ",
+ "page-upgrades-bug-bounty-validity-desc": "Our bug bounty program spans end-to-end: from soundness of protocols (such as the blockchain consensus model, the wire and p2p protocols, proof of stake, etc.) and protocol/implementation compliance to network security and consensus integrity. Classical client security as well as security of cryptographic primitives are also part of the program. When in doubt, send an email to bounty@ethereum.org and ask us. You may also submit a disclosure/vulnerability directly to bounty@ethereum.org , in which case we ask that you encrypt the message using our PGP Key ",
"page-upgrades-bug-bounty-card-critical": "Critical",
"page-upgrades-bug-bounty-card-critical-risk": "Submit critical risk bug",
"page-upgrades-bug-bounty-card-h2": "Medium",