From d403366f60e68baa5228d2ce162fe90fe25d1334 Mon Sep 17 00:00:00 2001 From: George Zhang Date: Thu, 10 Jul 2025 19:03:12 -0700 Subject: [PATCH 1/5] feat: add MEV refund metrics widget component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new React component that fetches and displays real-time MEV and gas refund metrics from the Dune Analytics API. The widget shows loading states and falls back to mock data on errors. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/components/MevMetrics.module.css | 39 +++++++++++++++++ src/components/MevMetrics.tsx | 64 ++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/components/MevMetrics.module.css create mode 100644 src/components/MevMetrics.tsx diff --git a/src/components/MevMetrics.module.css b/src/components/MevMetrics.module.css new file mode 100644 index 00000000..fdab949e --- /dev/null +++ b/src/components/MevMetrics.module.css @@ -0,0 +1,39 @@ +.container { + display: flex; + align-items: center; + gap: 0.75rem; + font-size: 0.875rem; + color: var(--ifm-navbar-link-color); + margin-right: 0.75rem; +} + +.metric { + display: flex; + align-items: center; + gap: 0.25rem; +} + +.label { + font-weight: 400; +} + +.value { + font-family: monospace; + font-weight: 600; + transition: opacity 0.3s ease; +} + +.loading { + opacity: 0.5; +} + +.separator { + color: var(--ifm-navbar-link-color); + opacity: 0.3; +} + +@media (max-width: 996px) { + .container { + display: none !important; + } +} \ No newline at end of file diff --git a/src/components/MevMetrics.tsx b/src/components/MevMetrics.tsx new file mode 100644 index 00000000..813a34fd --- /dev/null +++ b/src/components/MevMetrics.tsx @@ -0,0 +1,64 @@ +import React, { useEffect, useState } from 'react'; +import styles from './MevMetrics.module.css'; + +interface MetricsResponse { + totalMevRefund: number; + totalGasRefund: number; + fetchedAt: string; + stale: boolean; +} + +export default function MevMetrics(): JSX.Element { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchMetrics = async () => { + try { + const response = await fetch('https://refund-metrics-dune-api.vercel.app/api/metrics'); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const metrics: MetricsResponse = await response.json(); + setData(metrics); + } catch (error) { + console.error('Error fetching MEV metrics:', error); + // Mock data as fallback + setData({ + totalMevRefund: 380.29, + totalGasRefund: 444.24, + fetchedAt: new Date().toISOString(), + stale: true + }); + } finally { + setLoading(false); + } + }; + + fetchMetrics(); + }, []); + + const formatValue = (value: number): string => { + return `${value.toFixed(2)} ETH`; + }; + + return ( +
+ Refund + | +
+ MEV: + + {loading ? '...' : data && formatValue(data.totalMevRefund)} + +
+ | +
+ Gas: + + {loading ? '...' : data && formatValue(data.totalGasRefund)} + +
+
+ ); +} \ No newline at end of file From 8ce8f6d7d4bd570263e44700b2bf882996e5d3d5 Mon Sep 17 00:00:00 2001 From: George Zhang Date: Thu, 10 Jul 2025 19:03:28 -0700 Subject: [PATCH 2/5] feat: integrate MEV metrics widget into navbar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add custom navbar item type to support the MEV metrics widget and configure it in the navbar. The widget displays real-time MEV and gas refund data fetched from the Dune Analytics API. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- docusaurus.config.js | 4 ++++ src/theme/NavbarItem/ComponentTypes.tsx | 27 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/theme/NavbarItem/ComponentTypes.tsx diff --git a/docusaurus.config.js b/docusaurus.config.js index 5a7a4048..65939bb6 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -69,6 +69,10 @@ module.exports = async function createConfigAsync() { sidebarId: 'api', position: 'left', }, + { + type: 'custom-mevMetrics', + position: 'right', + }, { href: 'https://github.com/flashbots/docs', label: 'GitHub', diff --git a/src/theme/NavbarItem/ComponentTypes.tsx b/src/theme/NavbarItem/ComponentTypes.tsx new file mode 100644 index 00000000..faff4de8 --- /dev/null +++ b/src/theme/NavbarItem/ComponentTypes.tsx @@ -0,0 +1,27 @@ +import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem'; +import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem'; +import LocaleDropdownNavbarItem from '@theme/NavbarItem/LocaleDropdownNavbarItem'; +import SearchNavbarItem from '@theme/NavbarItem/SearchNavbarItem'; +import HtmlNavbarItem from '@theme/NavbarItem/HtmlNavbarItem'; +import DocNavbarItem from '@theme/NavbarItem/DocNavbarItem'; +import DocSidebarNavbarItem from '@theme/NavbarItem/DocSidebarNavbarItem'; +import DocsVersionNavbarItem from '@theme/NavbarItem/DocsVersionNavbarItem'; +import DocsVersionDropdownNavbarItem from '@theme/NavbarItem/DocsVersionDropdownNavbarItem'; +import MevMetrics from '@site/src/components/MevMetrics'; + +import type {ComponentTypesObject} from '@theme/NavbarItem/ComponentTypes'; + +const ComponentTypes: ComponentTypesObject = { + default: DefaultNavbarItem, + localeDropdown: LocaleDropdownNavbarItem, + search: SearchNavbarItem, + dropdown: DropdownNavbarItem, + html: HtmlNavbarItem, + doc: DocNavbarItem, + docSidebar: DocSidebarNavbarItem, + docsVersion: DocsVersionNavbarItem, + docsVersionDropdown: DocsVersionDropdownNavbarItem, + 'custom-mevMetrics': MevMetrics, +}; + +export default ComponentTypes; From 66f70dc4e2a88328554b62ec52ab65bf3afb358f Mon Sep 17 00:00:00 2001 From: George Zhang Date: Wed, 23 Jul 2025 12:16:03 -0700 Subject: [PATCH 3/5] feat: add click-to-navigate functionality for MEV refunds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Make MEV refunds metric clickable, redirecting to protect.flashbots.net - Add hover and active states for better UX - Include keyboard accessibility (Enter/Space key support) - Implement as per requirement to navigate to Protect site from docs 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/components/MevMetrics.module.css | 13 +++++++++++++ src/components/MevMetrics.tsx | 21 +++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/components/MevMetrics.module.css b/src/components/MevMetrics.module.css index fdab949e..7cee49a6 100644 --- a/src/components/MevMetrics.module.css +++ b/src/components/MevMetrics.module.css @@ -32,6 +32,19 @@ opacity: 0.3; } +.clickable { + cursor: pointer; + transition: opacity 0.2s ease; +} + +.clickable:hover { + opacity: 0.8; +} + +.clickable:active { + opacity: 0.6; +} + @media (max-width: 996px) { .container { display: none !important; diff --git a/src/components/MevMetrics.tsx b/src/components/MevMetrics.tsx index 813a34fd..f55d817b 100644 --- a/src/components/MevMetrics.tsx +++ b/src/components/MevMetrics.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import styles from './MevMetrics.module.css'; interface MetricsResponse { @@ -9,13 +10,15 @@ interface MetricsResponse { } export default function MevMetrics(): JSX.Element { + const { siteConfig } = useDocusaurusContext(); const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchMetrics = async () => { try { - const response = await fetch('https://refund-metrics-dune-api.vercel.app/api/metrics'); + const apiUrl = siteConfig.customFields?.refundMetricsApiUrl as string; + const response = await fetch(`${apiUrl}/api/metrics`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } @@ -42,11 +45,25 @@ export default function MevMetrics(): JSX.Element { return `${value.toFixed(2)} ETH`; }; + const handleMevClick = () => { + window.open('https://protect.flashbots.net/', '_blank', 'noopener,noreferrer'); + }; + return (
Refund | -
+
{ + if (e.key === 'Enter' || e.key === ' ') { + handleMevClick(); + } + }} + > MEV: {loading ? '...' : data && formatValue(data.totalMevRefund)} From 24e2a6b0aedb836eb0d645ef4c4144da2b25fab5 Mon Sep 17 00:00:00 2001 From: George Zhang Date: Wed, 23 Jul 2025 12:16:46 -0700 Subject: [PATCH 4/5] refactor: make refund metrics API URL configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add refundMetricsApiUrl to Docusaurus customFields - Allows easy configuration without environment variables - Consistent with BuilderNet implementation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- docusaurus.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docusaurus.config.js b/docusaurus.config.js index 65939bb6..554db365 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -120,5 +120,8 @@ module.exports = async function createConfigAsync() { }, 'docusaurus-plugin-sass' ], + customFields: { + refundMetricsApiUrl: 'https://refund-metrics-dune-api.vercel.app', + }, } } From be29d75d4a5c4e90d8239f45b819de9169d21594 Mon Sep 17 00:00:00 2001 From: George Zhang Date: Wed, 23 Jul 2025 12:46:31 -0700 Subject: [PATCH 5/5] feat: make redirect URL configurable and document widget setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add refundMetricsRedirectUrl to customFields for configurable redirects - Update component to use configured redirect URL - Add comprehensive documentation to README about widget configuration - Document that Flashbots docs shows both MEV and gas refunds, MEV clicks redirect to Protect 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 22 ++++++++++++++++++++++ docusaurus.config.js | 1 + src/components/MevMetrics.tsx | 3 ++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index eb2d24b3..4d0a8eb0 100644 --- a/README.md +++ b/README.md @@ -48,3 +48,25 @@ This command generates static content into the `build` directory and can be serv Create a PR and once merged, Github actions automatically deploy it. The docs use Vercel for hosting, and deployment is done by Vercel on any merge into the master branch. + +## Refund Metrics Widget + +This site displays MEV and gas refund metrics in the navbar, fetched from the [Flashbots Refund Metrics API](https://github.com/flashbots/refund-metrics-dune-api). + +### Configuration + +To configure the widget, edit `docusaurus.config.js`: + +```js +customFields: { + refundMetricsApiUrl: 'https://refund-metrics-dune-api.vercel.app', + refundMetricsRedirectUrl: 'https://protect.flashbots.net/', +}, +``` + +- `refundMetricsApiUrl`: The API endpoint for fetching metrics +- `refundMetricsRedirectUrl`: Where to redirect when users click on MEV refunds + +The widget implementation is in `src/components/MevMetrics.tsx`. For Flashbots docs, it: +- Shows both MEV and gas refunds +- Clicking on MEV refunds redirects to the configured URL (default: [Flashbots Protect](https://protect.flashbots.net/)) diff --git a/docusaurus.config.js b/docusaurus.config.js index 554db365..523e9f5d 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -122,6 +122,7 @@ module.exports = async function createConfigAsync() { ], customFields: { refundMetricsApiUrl: 'https://refund-metrics-dune-api.vercel.app', + refundMetricsRedirectUrl: 'https://protect.flashbots.net/', }, } } diff --git a/src/components/MevMetrics.tsx b/src/components/MevMetrics.tsx index f55d817b..66852ed4 100644 --- a/src/components/MevMetrics.tsx +++ b/src/components/MevMetrics.tsx @@ -46,7 +46,8 @@ export default function MevMetrics(): JSX.Element { }; const handleMevClick = () => { - window.open('https://protect.flashbots.net/', '_blank', 'noopener,noreferrer'); + const redirectUrl = siteConfig.customFields?.refundMetricsRedirectUrl as string; + window.open(redirectUrl, '_blank', 'noopener,noreferrer'); }; return (