diff --git a/app/(home)/l1-performance/page.tsx b/app/(home)/l1-performance/page.tsx new file mode 100644 index 00000000000..f15ca6da7e7 --- /dev/null +++ b/app/(home)/l1-performance/page.tsx @@ -0,0 +1,61 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { + AnimatedBackground, + HeroSection, + MetricsSection, + HorizontalScrollSection, + FAQSection, + CTASection +} from '@/components/l1-performance'; + +export default function L1PerformancePage() { + const [isInSlider, setIsInSlider] = useState(false); + + // Initialize custom animations + useEffect(() => { + addCustomAnimations(); + }, []); + + return ( +
+ +
+ + + + + +
+
+ ); +} + +// Add custom animations to global CSS +const addCustomAnimations = () => { + if (typeof document !== 'undefined') { + const style = document.createElement('style'); + style.textContent = ` + @keyframes circuit-vertical { + 0% { transform: translateY(-100%); opacity: 0; } + 50% { opacity: 0.5; } + 100% { transform: translateY(100%); opacity: 0; } + } + + @keyframes wave { + 0%, 100% { transform: translateY(0px); } + 50% { transform: translateY(-10px); } + } + + .animate-circuit-vertical { + animation: circuit-vertical 8s linear infinite; + } + + .animate-wave { + animation: wave 8s ease-in-out infinite; + } + `; + document.head.appendChild(style); + } +}; diff --git a/app/global.css b/app/global.css index f8f139fc176..dbff11e7199 100644 --- a/app/global.css +++ b/app/global.css @@ -570,11 +570,92 @@ html body[data-scroll-locked] { text-shadow: var(--shadow); } + /* Token Background Animations */ + @keyframes slide-up { + 0% { + transform: translateY(100%); + } + 100% { + transform: translateY(-100%); + } + } + + @keyframes slide-down { + 0% { + transform: translateY(-100%); + } + 100% { + transform: translateY(100%); + } + } + + @keyframes slide-up-seamless { + 0% { + transform: translateY(0%); + } + 100% { + transform: translateY(-50%); + } + } + + @keyframes slide-down-seamless { + 0% { + transform: translateY(0%); + } + 100% { + transform: translateY(50%); + } + } + + .animate-slide-up { + animation: slide-up 60s linear infinite; + } + + .animate-slide-down { + animation: slide-down 70s linear infinite; + } + + .animate-slide-up-seamless { + animation: slide-up-seamless 40s linear infinite; + } + + .animate-slide-down-seamless { + animation: slide-down-seamless 45s linear infinite; + } + + @keyframes infinite-scroll-up { + 0% { + transform: translateY(0); + } + 100% { + transform: translateY(-12.5%); + } + } + + @keyframes infinite-scroll-down { + 0% { + transform: translateY(-12.5%); + } + 100% { + transform: translateY(0); + } + } + + .animate-infinite-scroll-up { + animation: infinite-scroll-up 10s linear infinite; + } + + .animate-infinite-scroll-down { + animation: infinite-scroll-down 15s linear infinite; + } + .scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; } + .scrollbar-hide::-webkit-scrollbar { display: none; } + } diff --git a/components/charts/L1TPSChart.tsx b/components/charts/L1TPSChart.tsx new file mode 100644 index 00000000000..a9a775da80b --- /dev/null +++ b/components/charts/L1TPSChart.tsx @@ -0,0 +1,31 @@ +"use client"; + +import { ChartContainer } from '@/components/ui/chart'; +import { + ResponsiveContainer, + LineChart, + Line, + XAxis, + YAxis, + Tooltip as RechartsTooltip, +} from "recharts"; + +export default function L1TPSChart({ data }: { data: any[] }) { + return ( + +
+ + + + + + + + +
+
+ ); +} \ No newline at end of file diff --git a/components/l1-performance/AnimatedBackground.tsx b/components/l1-performance/AnimatedBackground.tsx new file mode 100644 index 00000000000..a6ee9daae8d --- /dev/null +++ b/components/l1-performance/AnimatedBackground.tsx @@ -0,0 +1,50 @@ +// Animated Background Components +export const AnimatedBackground = () => ( +
+ {/* Gradient Orbs */} +
+
+
+ + {/* Additional Gradient Orbs */} +
+
+
+
+
+
+ + {/* Network Grid Lines */} +
+ + + + + + + + +
+ + {/* Animated Circuit Lines */} +
+ {/* Vertical Circuit Lines */} + {[...Array(6)].map((_, i) => ( +
+ ))} +
+ + {/* Subtle Wave Effect */} +
+
+); \ No newline at end of file diff --git a/components/l1-performance/BlockAnimation.tsx b/components/l1-performance/BlockAnimation.tsx new file mode 100644 index 00000000000..7715c19d4c3 --- /dev/null +++ b/components/l1-performance/BlockAnimation.tsx @@ -0,0 +1,130 @@ +'use client'; + +import { useEffect, useState } from 'react'; + +interface BlockAnimationProps { + className?: string; +} + +interface Block { + id: number; + y: number; + hash: string; + height: number; + isActive: boolean; +} + +export const BlockAnimation: React.FC = ({ className = '' }) => { + const [blocks, setBlocks] = useState([]); + const [nextId, setNextId] = useState(0); + + // Generate random hash-like string + const generateHash = () => { + const chars = '0123456789abcdef'; + let hash = ''; + for (let i = 0; i < 8; i++) { + hash += chars[Math.floor(Math.random() * chars.length)]; + } + return hash; + }; + + // Generate random block height + const generateHeight = () => Math.floor(Math.random() * 900000) + 100000; + + // Simple system: One block at a time + useEffect(() => { + const interval = setInterval(() => { + setBlocks(prev => { + const minSpacing = 25; // Minimum distance + const maxSpacing = 40; // Maximum distance + + // Move all blocks down + let updatedBlocks = prev.map(block => ({ + ...block, + y: block.y + 1.5 + })).filter(block => block.y < 130); // Remove blocks that are off screen + + // Add new block if there's space + const lastBlock = updatedBlocks.length > 0 + ? updatedBlocks.reduce((highest, block) => block.y < highest.y ? block : highest) + : null; + + const shouldAddBlock = !lastBlock || lastBlock.y > minSpacing; + + if (shouldAddBlock) { + const randomSpacing = minSpacing + Math.random() * (maxSpacing - minSpacing); + const newBlockY = lastBlock ? lastBlock.y - randomSpacing : -20; + + updatedBlocks.push({ + id: nextId, + y: newBlockY, + hash: generateHash(), + height: generateHeight(), + isActive: false + }); + + setNextId(prev => prev + 1); + } + + // Update active state + return updatedBlocks.map(block => ({ + ...block, + isActive: block.y >= 40 && block.y <= 60 + })); + }); + }, 40); + + return () => clearInterval(interval); + }, [nextId]); + + return ( +
+ {/* Chain line */} +
+ + {/* Blocks */} + {blocks.map(block => ( +
+ {/* Block container */} +
+ {/* Block content */} +
+
+ #{block.height} +
+
+ {block.hash} +
+
+
+ + {/* Active block glow */} + {block.isActive && ( +
+ )} +
+ ))} + + {/* Gradient overlay */} +
+
+ ); +}; diff --git a/components/l1-performance/CTASection.tsx b/components/l1-performance/CTASection.tsx new file mode 100644 index 00000000000..1aa9317a9d7 --- /dev/null +++ b/components/l1-performance/CTASection.tsx @@ -0,0 +1,22 @@ +import Link from 'next/link'; + +export const CTASection = () => ( +
+
+
+

Ready to Build Your L1?

+

+ Start building your high-performance L1 blockchain with Avalanche's battle-tested infrastructure and tooling. +

+ +
+ + Get Started + +
+
+
+); \ No newline at end of file diff --git a/components/l1-performance/EconomicsSection.tsx b/components/l1-performance/EconomicsSection.tsx new file mode 100644 index 00000000000..c6e3174a99a --- /dev/null +++ b/components/l1-performance/EconomicsSection.tsx @@ -0,0 +1,107 @@ +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; +import { + CheckCircle, + Coins, + Shield, + Settings, + Target, + Users, + Flame +} from 'lucide-react'; + +export const EconomicsSection = () => ( +
+ {/* Economic Model Overview */} +
+
+ +
Gas Token
+
Custom ERC-20
+
+
+ +
Staking Token
+
Validator Control
+
+
+ +
Fee Models
+
Flexible Options
+
+
+ +
+ + + + + Gas Token + + + +
+ {[ + 'Support for custom gas token', + 'Can be stablecoin or any ERC-20 token', + 'Can be shared across multiple L1s' + ].map((feature, idx) => ( +
+ + {feature} +
+ ))} +
+
+
+ + + + + + Staking Token + + + +
+ {[ + 'Can be same as gas/native token or separate', + 'Full control over validator set determination', + 'Custom implementations like node licenses' + ].map((feature, idx) => ( +
+ + {feature} +
+ ))} +
+
+
+
+ + + + Transaction Fee Models + + Flexible fee distribution and reward mechanisms + + + +
+ {[ + { title: 'Reward Address', desc: 'Set specific address to receive all transaction fees from blocks', icon: Target }, + { title: 'Fee Recipients', desc: 'Allow block producers to claim fees with their own addresses', icon: Users }, + { title: 'Burn All Fees', desc: 'Dynamically switch to burning all collected transaction fees', icon: Flame } + ].map((model, idx) => ( +
+
+ +

{model.title}

+
+

{model.desc}

+
+ ))} +
+
+
+
+); \ No newline at end of file diff --git a/components/l1-performance/ElectricEffect.tsx b/components/l1-performance/ElectricEffect.tsx new file mode 100644 index 00000000000..d011bc46328 --- /dev/null +++ b/components/l1-performance/ElectricEffect.tsx @@ -0,0 +1,52 @@ +'use client'; + +interface ElectricEffectProps { + className?: string; +} + +export const ElectricEffect: React.FC = ({ className = '' }) => { + return ( +
+ {/* Electric bolts */} +
+ +
+ +
+ +
+ +
+ + {/* Electric sparks */} +
+ +
+ +
+ + {/* Electric glow */} +
+ + {/* Zap icon enhancement */} +
+ + + +
+ + {/* Gradient overlay */} +
+
+ ); +}; diff --git a/components/l1-performance/FAQSection.tsx b/components/l1-performance/FAQSection.tsx new file mode 100644 index 00000000000..07eeda42043 --- /dev/null +++ b/components/l1-performance/FAQSection.tsx @@ -0,0 +1,83 @@ +import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from '@/components/ui/accordion'; +import { + Shield, + Users, + Zap, + Activity, + ArrowRight, + Globe +} from 'lucide-react'; + +const faqData = [ + { + q: 'What types of validation models are available?', + a: 'Avalanche supports flexible validator set management with audited reference implementations for both permissioned (Proof-of-Authority) and permissionless (Proof-of-Stake with optional delegation) models. These implementations are fully customizable to meet specific requirements.', + icon: Shield + }, + { + q: 'How many validators can there be in a permissioned chain?', + a: 'Avalanche L1 chains utilize the same consensus mechanism as the public Primary Network, which has been running with over 1,000 validators. The architecture supports significantly higher theoretical maximums, constrained mainly by network latency and hardware.', + icon: Users + }, + { + q: 'Can the chain be gasless?', + a: 'Yes. Avalanche L1s can support gasless transactions through several mechanisms, including using a valueless token for gas or completely abstracting gas via relayers that sponsor transaction fees on behalf of users.', + icon: Zap + }, + { + q: 'What is the expected TPS, and is it affected by the number of validators?', + a: 'EVM L1 performance depends on multiple factors: state size, transaction type, validator count, and network latency. With a small (~10), co-located validator set and an in-memory state (<100GB), throughput can reach up to 8,400 TPS, equivalent to ~175 million gas/second (for simple transfers). With ~30 globally distributed validators, performance is around 4,000 TPS, or ~85 million gas/second—assuming the state fits in memory.', + icon: Activity + }, + { + q: 'How does the migration from PoA to PoS work?', + a: 'It is easy to migrate from PoA to PoS. The ownership of the validator manager contract is transferred to a staking manager. The ERC20 or native token Staking Manager contracts are deployed separately and are made the owner of the PoA Validator Manager contract.', + icon: ArrowRight + }, + { + q: 'How does interoperability work without trusted third-parties?', + a: 'Avalanche L1s provide native interoperability without trusted third-parties. The system uses a flat interoperability fee per validator with no sequencer revenue share, settlement fees, DA costs or message-based interoperability fees. This enables fast cross-chain communication with 2 block confirmations (3s end-to-end).', + icon: Globe + } +]; + +export const FAQSection = () => ( +
+
+

Frequently Asked Questions

+

+ Common questions about L1 performance and capabilities +

+
+ +
+
+ {faqData.map((faq, idx) => ( + + + +
+
+
+ +
+
+ + {faq.q} + +
+
+ +
+

+ {faq.a} +

+
+
+
+
+ ))} +
+
+
+); \ No newline at end of file diff --git a/components/l1-performance/HeroSection.tsx b/components/l1-performance/HeroSection.tsx new file mode 100644 index 00000000000..52e3be0f27b --- /dev/null +++ b/components/l1-performance/HeroSection.tsx @@ -0,0 +1,19 @@ +import { AvalancheLogo } from '@/components/navigation/avalanche-logo'; + +export const HeroSection = () => ( +
+
+ +
+

+ L1 Performance + + Fact Sheet + +

+

+ Discover the cutting-edge performance metrics and capabilities of our L1 blockchain solution, + designed for maximum throughput, minimal latency, and seamless interoperability. +

+
+); \ No newline at end of file diff --git a/components/l1-performance/HorizontalScrollSection.tsx b/components/l1-performance/HorizontalScrollSection.tsx new file mode 100644 index 00000000000..5fffafccaf6 --- /dev/null +++ b/components/l1-performance/HorizontalScrollSection.tsx @@ -0,0 +1,119 @@ +import { useState } from 'react'; +import { Button } from '@/components/ui/button'; +import { + ChevronLeft, + ChevronRight, + BarChart3, + TrendingUp, + Shield, + Coins +} from 'lucide-react'; +import { OverviewSection } from './OverviewSection'; +import { PerformanceSection } from './PerformanceSection'; +import { ValidationSection } from './ValidationSection'; +import { EconomicsSection } from './EconomicsSection'; + +const sections = [ + { + id: 'overview', + title: 'Overview', + subtitle: 'Key advantages and technical specifications', + icon: BarChart3, + content: + }, + { + id: 'performance', + title: 'Performance', + subtitle: 'Scaling and performance characteristics', + icon: TrendingUp, + content: + }, + { + id: 'validation', + title: 'Validation', + subtitle: 'Flexible validation mechanisms', + icon: Shield, + content: + }, + { + id: 'economics', + title: 'Economics', + subtitle: 'Tokenomics and fee structures', + icon: Coins, + content: + } +]; + +interface HorizontalScrollSectionProps { + isInSlider: boolean; + setIsInSlider: (value: boolean) => void; +} + +export const HorizontalScrollSection = ({ isInSlider, setIsInSlider }: HorizontalScrollSectionProps) => { + const [currentSection, setCurrentSection] = useState(0); + + return ( +
+ {/* Navigation Dots - Fixed Position */} +
+ {sections.map((_, index) => ( +
+ + {/* Current Section Display */} +
+

{sections[currentSection].title}

+

{sections[currentSection].subtitle}

+
+ + {/* Navigation Buttons */} +
+ + + +
+ + {/* Responsive Section Container - NO horizontal scroll */} +
+
+ {sections.map((section, index) => ( +
+
+ {section.content} +
+
+ ))} +
+
+
+ ); +}; \ No newline at end of file diff --git a/components/l1-performance/MetricsSection.tsx b/components/l1-performance/MetricsSection.tsx new file mode 100644 index 00000000000..3fd845062fc --- /dev/null +++ b/components/l1-performance/MetricsSection.tsx @@ -0,0 +1,118 @@ +import { Card, CardContent } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { + Zap, + Coins, + Network, + Target +} from 'lucide-react'; +import { DotsPattern, GridPattern, WavePattern, HexagonPattern } from './SvgPatterns'; +import { TokenBackground } from './TokenBackground'; +import { NumberCounter } from './NumberCounter'; +import { NetworkAnimation } from './NetworkAnimation'; +import { BlockAnimation } from './BlockAnimation'; +import { ElectricEffect } from './ElectricEffect'; +import { useSequentialAnimation } from '@/hooks/use-sequential-animation'; + +const metrics = [ + { + title: 'Gas Token', + value: 'Fully Customizable', + subtitle: 'ERC-20, stablecoin, ...', + icon: Coins, + pattern: DotsPattern, + }, + { + title: 'Throughput', + value: 'Up to 8.4k TPS', + subtitle: '(simple transfers / 85m gas per second)', + icon: Zap, + pattern: GridPattern, + }, + { + title: 'Block Time', + value: 'Up to 125ms', + subtitle: 'Configurable target rate', + icon: Target, + pattern: WavePattern, + }, + { + title: 'Interoperability', + value: 'Native', + subtitle: 'No third parties', + icon: Network, + pattern: HexagonPattern, + } +]; + +export const MetricsSection = () => { + const { containerRef, getItemStyle } = useSequentialAnimation({ + itemCount: metrics.length, + delay: 150, + duration: 500 + }); + + return ( +
+
+

Performance Metrics

+

+ Industry-leading performance benchmarks that set new standards for blockchain technology +

+
+ +
+ {metrics.map((metric, index) => ( +
+ + {/* Special Token Background for Gas Token card */} + {metric.title === 'Gas Token' && ( + + )} + + {/* Special Network Animation for Interoperability card */} + {metric.title === 'Interoperability' && ( + + )} + + {/* Special Block Animation for Block Time card */} + {metric.title === 'Block Time' && ( + + )} + + {/* Special Electric Effect for Throughput card */} + {metric.title === 'Throughput' && ( + + )} + + {/* Background Icon for other cards */} + {metric.title !== 'Gas Token' && metric.title !== 'Interoperability' && metric.title !== 'Block Time' && metric.title !== 'Throughput' && ( +
+ +
+ )} + + +
+ + {metric.title} + +
+
+ {metric.title === 'Throughput' ? ( +

+ Up to k TPS +

+ ) : ( +

{metric.value}

+ )} +

{metric.subtitle}

+
+
+
+
+ ))} +
+
+ ); +}; \ No newline at end of file diff --git a/components/l1-performance/NetworkAnimation.tsx b/components/l1-performance/NetworkAnimation.tsx new file mode 100644 index 00000000000..052da7c5e40 --- /dev/null +++ b/components/l1-performance/NetworkAnimation.tsx @@ -0,0 +1,252 @@ +'use client'; + +import { useEffect, useState } from 'react'; + +interface NetworkAnimationProps { + className?: string; +} + +interface Node { + id: string; + x: number; + y: number; + size: number; + isHub?: boolean; +} + +interface Connection { + from: string; + to: string; + isActive: boolean; + progress: number; + type: 'hub' | 'direct'; +} + +export const NetworkAnimation: React.FC = ({ className = '' }) => { + const [connections, setConnections] = useState([]); + + // Define nodes positions (relative to container) - shifted more right + const nodes: Node[] = [ + // Hub node (center-right) - further right + { id: 'hub', x: 75, y: 50, size: 8, isHub: true }, + + // Inner ring - connected nodes + { id: 'node1', x: 55, y: 30, size: 4 }, + { id: 'node2', x: 95, y: 30, size: 4 }, + { id: 'node3', x: 100, y: 55, size: 4 }, + { id: 'node4', x: 90, y: 75, size: 4 }, + { id: 'node5', x: 60, y: 75, size: 4 }, + { id: 'node6', x: 50, y: 55, size: 4 }, + + // Outer ring - some connected, some autonomous + { id: 'node7', x: 45, y: 15, size: 3 }, + { id: 'node8', x: 105, y: 15, size: 3 }, + { id: 'node9', x: 115, y: 45, size: 3 }, + { id: 'node10', x: 110, y: 85, size: 3 }, + { id: 'node11', x: 75, y: 95, size: 3 }, + { id: 'node12', x: 40, y: 85, size: 3 }, + { id: 'node13', x: 35, y: 45, size: 3 }, + + // Additional outer nodes + { id: 'node14', x: 85, y: 10, size: 3 }, + { id: 'node15', x: 25, y: 25, size: 3 }, + { id: 'node16', x: 120, y: 65, size: 3 }, + + // Autonomous nodes (not connected to hub) + { id: 'autonomous1', x: 37, y: 20, size: 2 }, + { id: 'autonomous2', x: 113, y: 25, size: 2 }, + { id: 'autonomous3', x: 110, y: 70, size: 2 }, + { id: 'autonomous4', x: 40, y: 70, size: 2 }, + { id: 'autonomous5', x: 22, y: 10, size: 2 }, + { id: 'autonomous6', x: 118, y: 90, size: 2 }, + ]; + + // Only connected nodes (exclude autonomous) + const connectedNodes = nodes.filter(node => !node.isHub && !node.id.startsWith('autonomous')); + + // Initialize connections + useEffect(() => { + const hubConnections: Connection[] = connectedNodes.map(node => ({ + from: 'hub', + to: node.id, + isActive: false, + progress: 0, + type: 'hub' as const + })); + + // Add some direct connections between inner ring nodes only + const directConnections: Connection[] = [ + { from: 'node1', to: 'node2', isActive: false, progress: 0, type: 'direct' }, + { from: 'node3', to: 'node4', isActive: false, progress: 0, type: 'direct' }, + { from: 'node5', to: 'node6', isActive: false, progress: 0, type: 'direct' }, + ]; + + setConnections([...hubConnections, ...directConnections]); + }, []); + + // Animation logic + useEffect(() => { + const interval = setInterval(() => { + setConnections(prev => { + const updated = [...prev]; + + // Randomly activate connections (slower activation) + const inactiveConnections = updated.filter(conn => !conn.isActive); + if (inactiveConnections.length > 0 && Math.random() > 0.85) { + const randomConn = inactiveConnections[Math.floor(Math.random() * inactiveConnections.length)]; + const index = updated.findIndex(conn => + conn.from === randomConn.from && conn.to === randomConn.to + ); + if (index !== -1) { + updated[index] = { ...updated[index], isActive: true, progress: 0 }; + } + } + + // Update progress for active connections (slower movement) + return updated.map(conn => { + if (conn.isActive) { + const newProgress = conn.progress + 0.02; + if (newProgress >= 1) { + return { ...conn, isActive: false, progress: 0 }; + } + return { ...conn, progress: newProgress }; + } + return conn; + }); + }); + }, 80); + + return () => clearInterval(interval); + }, []); + + const getNodeById = (id: string): Node | undefined => { + return nodes.find(node => node.id === id); + }; + + const renderConnection = (connection: Connection) => { + const fromNode = getNodeById(connection.from); + const toNode = getNodeById(connection.to); + + if (!fromNode || !toNode) return null; + + const x1 = fromNode.x; + const y1 = fromNode.y; + const x2 = toNode.x; + const y2 = toNode.y; + + // Calculate current position of the data packet + const currentX = x1 + (x2 - x1) * connection.progress; + const currentY = y1 + (y2 - y1) * connection.progress; + + return ( + + {/* Connection line */} + + + {/* Animated data packet */} + {connection.isActive && ( + + + + )} + + ); + }; + + return ( +
+ + {/* Render connections */} + {connections.map(renderConnection)} + + {/* Render nodes */} + {nodes.map(node => ( + + {/* Node glow effect */} + {node.isHub && ( + + + + )} + + {/* Main node */} + + + {/* Inner core for hub */} + {node.isHub && ( + + )} + + ))} + + + {/* Gradient overlay */} +
+
+ ); +}; diff --git a/components/l1-performance/NumberCounter.tsx b/components/l1-performance/NumberCounter.tsx new file mode 100644 index 00000000000..996dfd58337 --- /dev/null +++ b/components/l1-performance/NumberCounter.tsx @@ -0,0 +1,86 @@ +'use client'; + +import { useEffect, useState, useRef } from 'react'; + +interface NumberCounterProps { + end: number; + duration?: number; + suffix?: string; + prefix?: string; + decimals?: number; + className?: string; +} + +export const NumberCounter: React.FC = ({ + end, + duration = 2000, + suffix = '', + prefix = '', + decimals = 0, + className = '' +}) => { + const [count, setCount] = useState(0); + const [isVisible, setIsVisible] = useState(false); + const countRef = useRef(null); + + useEffect(() => { + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting && !isVisible) { + setIsVisible(true); + } + }, + { threshold: 0.1 } + ); + + if (countRef.current) { + observer.observe(countRef.current); + } + + return () => { + if (countRef.current) { + observer.unobserve(countRef.current); + } + }; + }, [isVisible]); + + useEffect(() => { + if (!isVisible) return; + + let startTime: number; + let animationId: number; + + const animate = (timestamp: number) => { + if (!startTime) startTime = timestamp; + const progress = Math.min((timestamp - startTime) / duration, 1); + + // Easing function for smooth animation + const easeOutQuart = 1 - Math.pow(1 - progress, 4); + const currentCount = end * easeOutQuart; + + setCount(currentCount); + + if (progress < 1) { + animationId = requestAnimationFrame(animate); + } + }; + + animationId = requestAnimationFrame(animate); + + return () => { + if (animationId) { + cancelAnimationFrame(animationId); + } + }; + }, [isVisible, end, duration]); + + const formatNumber = (num: number) => { + return num.toFixed(decimals); + }; + + return ( + + {prefix}{formatNumber(count)}{suffix} + + ); +}; diff --git a/components/l1-performance/OverviewSection.tsx b/components/l1-performance/OverviewSection.tsx new file mode 100644 index 00000000000..ff3acb671c4 --- /dev/null +++ b/components/l1-performance/OverviewSection.tsx @@ -0,0 +1,125 @@ +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { + BarChart3, + CheckCircle, + Gauge, + Network, + Shield, + Zap +} from 'lucide-react'; + +export const OverviewSection = () => ( +
+ {/* Hero Stats */} +
+
+
100%
+
EVM Compatible
+
+
+
+
Customizable
+
+
+
Native
+
Interoperability
+
+
+ +
+
+ + {/* Decorative Elements */} +
+
+ + +
+
+
+ +
+ {/* Floating Indicator */} +
+
+
+ + Key Advantages + +
+
+ + + +
+ {[ + 'Native interoperability without trusted third-parties', + 'Fully customizable gas tokens and transaction fees', + 'Flat interoperability fee per validator', + 'No sequencer revenue share or DA costs' + ].map((item, idx) => ( +
+
+
+ +
+ + {item} + +
+
+ ))} +
+
+ +
+ +
+ + {/* Decorative Elements */} +
+
+ + +
+
+
+ +
+ {/* Floating Indicator */} +
+
+
+ + Technical Specifications + +
+
+ + + +
+ {[ + { label: 'EVM Compatibility', value: 'Full', icon: CheckCircle }, + { label: 'RPC Compatibility', value: 'Geth/Besu', icon: Network }, + { label: 'Gasless Support', value: 'Yes', icon: Zap }, + { label: 'State Growth Control', value: 'Strict', icon: Shield } + ].map((spec, idx) => ( +
+
+
+ + {spec.label} +
+ {spec.value} +
+
+ ))} +
+
+ +
+
+
+); \ No newline at end of file diff --git a/components/l1-performance/PerformanceSection.tsx b/components/l1-performance/PerformanceSection.tsx new file mode 100644 index 00000000000..e8b3f10c5bb --- /dev/null +++ b/components/l1-performance/PerformanceSection.tsx @@ -0,0 +1,221 @@ +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { + CheckCircle, + Cpu, + Globe, + Info +} from 'lucide-react'; + +const validatorConfigs = [ + { + type: 'Small Co-located', + validators: 5, + location: 'All in the same data center (minimal latency)', + gasPerSecond: '85m', + features: ['Minimal latency', 'Strict state growth control'], + flag: '🏢', + testResults: { + blocksPerSecond: '7.14', + transactionsPerSecond: '8,408', + gasPerSecond: '176.5M', + blockTime: '80ms', + } + }, + { + type: 'Large Distributed', + validators: 30, + location: 'Globally distributed (higher decentralization)', + gasPerSecond: '85m', + features: ['Global distribution', 'Higher decentralization'], + flag: '🌍', + testResults: { + blocksPerSecond: '2.7', + transactionsPerSecond: '4,053', + gasPerSecond: '85.3M', + blockTime: '370ms', + } + } +]; + +export const PerformanceSection = () => ( +
+ +
+
+
+
+
+
+ + {validatorConfigs[0].type} +
+
+ {validatorConfigs[0].validators} validators +
+
+ {validatorConfigs[0].location} +
+
+
+
+
+
{validatorConfigs[0].gasPerSecond}
+
Gas/Second (Config)
+
+
+
{validatorConfigs[0].validators}
+
Validators
+
+
+
+
Key Features
+
    + {validatorConfigs[0].features.map((feature, idx) => ( +
  • + + {feature} +
  • + ))} +
+
+
+
+
+
+ +
+
+
+
+
+
+
+ + {validatorConfigs[1].type} +
+
+ {validatorConfigs[1].validators} validators +
+
+ {validatorConfigs[1].location} +
+
+
+
+
+
{validatorConfigs[1].gasPerSecond}
+
Gas/Second (Config)
+
+
+
{validatorConfigs[1].validators}
+
Validators
+
+
+
+
Key Features
+
    + {validatorConfigs[1].features.map((feature, idx) => ( +
  • + + {feature} +
  • + ))} +
+
+
+
+
+ +
+
+
Performance Comparison
+ {/* Responsive chart grid: 2x2 on mobile, 4x1 on desktop */} +
+ {/* Transactions per Second */} +
+
+
+
+
+
+
+
+
+
TPS
8,408 / 4,053
+
+ {/* Vertical HR - only on desktop */} +
+
+
+ {/* Gas per Second */} +
+
+
+
+
+
+
+
+
+
Gas/sec (M)
176.5 / 85.3
+
+ {/* Vertical HR - only on desktop */} +
+
+
+ {/* Blocks per Second */} +
+
+
+
+
+
+
+
+
+
Blocks/sec
7.14 / 2.7
+
+ {/* Vertical HR - only on desktop */} +
+
+
+ {/* Block Time (lower is better) */} +
+
+
+
+
+
+
+
+
+
Block Time (ms)
80 / 370
+
+
+
+
Small Co-located
+
Large Distributed
+
+
+
+ + + + + + Performance Explainer + + + +
+

+ TPS vs Gas vs Multi-dimensional fees: Any EVM that runs at {'>'}20m gas per second needs to enforce restrictions on resource usage. For example, if there was an EVM chain with 100m gas per second that allowed arbitrary transactions, you could use all the capacity to write to the state (on disk). The state is stored forever in the EVM (huge flaw) and this would result in rapid state growth that would slow the chain down over time. +

+

+ The beauty of an L1 is that you can control the usage of the capacity by restricting which smart contracts can be deployed. This way you can push performance much further by implementing restrictions on the application layer (for example not just writing data to disk). +

+
+
+
+
+); \ No newline at end of file diff --git a/components/l1-performance/SvgPatterns.tsx b/components/l1-performance/SvgPatterns.tsx new file mode 100644 index 00000000000..106fcd02224 --- /dev/null +++ b/components/l1-performance/SvgPatterns.tsx @@ -0,0 +1,44 @@ +// SVG Background Components +export const GridPattern = () => ( + + + + + + + + +); + +export const DotsPattern = () => ( + + + + + + + + +); + +export const WavePattern = () => ( + + + + + + + + +); + +export const HexagonPattern = () => ( + + + + + + + + +); \ No newline at end of file diff --git a/components/l1-performance/TokenBackground.tsx b/components/l1-performance/TokenBackground.tsx new file mode 100644 index 00000000000..0373cbd384c --- /dev/null +++ b/components/l1-performance/TokenBackground.tsx @@ -0,0 +1,75 @@ +import Image from 'next/image'; + +interface TokenBackgroundProps { + className?: string; +} + +const tokenLogos = [ + 'avax.png', + 'tether.png', + 'usdc.png', + 'bitcoin.png', + 'arena.png', + 'beam.png', + 'coq.png', + 'aave.png', + 'gun.png', + 'chainlink.png' +]; + +export const TokenBackground: React.FC = ({ className = '' }) => { + // Split tokens into two columns + const midPoint = Math.ceil(tokenLogos.length / 2); + const firstColumn = tokenLogos.slice(0, midPoint); + const secondColumn = tokenLogos.slice(midPoint); + + return ( +
+ {/* Rotated container for diagonal effect */} +
+ {/* First Column - Moving Up */} +
+
+ {/* Duplicate the list multiple times for seamless loop */} + {Array.from({ length: 8 }, (_, setIndex) => + firstColumn.map((logo, index) => ( +
+ +
+ )) + )} +
+
+ + {/* Second Column - Moving Down */} +
+
+ {/* Duplicate the list multiple times for seamless loop */} + {Array.from({ length: 8 }, (_, setIndex) => + secondColumn.map((logo, index) => ( +
+ +
+ )) + )} +
+
+
+ + {/* Gradient overlay to fade tokens */} +
+
+ ); +}; diff --git a/components/l1-performance/ValidationSection.tsx b/components/l1-performance/ValidationSection.tsx new file mode 100644 index 00000000000..e5c741c9cdb --- /dev/null +++ b/components/l1-performance/ValidationSection.tsx @@ -0,0 +1,123 @@ +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; +import { + CheckCircle, + Shield, + Coins, + Settings, + ArrowRight +} from 'lucide-react'; + +const validationTypes = [ + { + type: 'PoA (Proof of Authority)', + description: 'Permissioned validator set with ownable manager contract', + features: ['Ownable validator manager', 'Protected with onlyOwner modifier', 'Flexible ownership (EOA, multisig, contract)'], + icon: Shield, + }, + { + type: 'PoS (Proof of Stake)', + description: 'Permissionless staking with ERC20 or native token', + features: ['Separate staking manager contracts', 'Lock/unlock stake functionality', 'Easy migration from PoA'], + icon: Coins, + }, + { + type: 'Custom Validation', + description: 'Fully customizable validation rules and mechanisms', + features: ['Permissionless by default', 'Custom implementations possible', 'Hybrid models supported'], + icon: Settings, + } +]; + +export const ValidationSection = () => ( +
+ + {/* Interactive Validation Types */} +
+ {validationTypes.map((type, index) => ( +
+ + {/* Decorative Elements */} +
+
+ + +
+
+
+ +
+ {/* Floating Indicator */} +
+
+
+ + {type.type} + + + {type.description} + +
+
+ + + +
+ {type.features.map((feature, idx) => ( +
+
+
+ +
+ + {feature} + +
+
+ ))} +
+
+ +
+ ))} +
+ + {/* Creative Migration Path */} + + + Seamless Evolution + + Transform your validation model without disruption + + + + +
+ {[ + { label: 'PoA', desc: 'Permissioned', icon: Shield }, + { label: 'Transfer', desc: 'Validator Manager', icon: ArrowRight }, + { label: 'PoS', desc: 'Permissionless', icon: Coins } + ].map((step, idx) => ( +
+
+
+
+ +
+
+ {step.label} +
+

{step.desc}

+
+
+ {idx < 2 && ( +
+ +
+ )} +
+ ))} +
+
+
+
+); \ No newline at end of file diff --git a/components/l1-performance/index.ts b/components/l1-performance/index.ts new file mode 100644 index 00000000000..d3f21c7b179 --- /dev/null +++ b/components/l1-performance/index.ts @@ -0,0 +1,11 @@ +export { AnimatedBackground } from './AnimatedBackground'; +export { GridPattern, DotsPattern, WavePattern, HexagonPattern } from './SvgPatterns'; +export { HeroSection } from './HeroSection'; +export { MetricsSection } from './MetricsSection'; +export { OverviewSection } from './OverviewSection'; +export { PerformanceSection } from './PerformanceSection'; +export { ValidationSection } from './ValidationSection'; +export { EconomicsSection } from './EconomicsSection'; +export { FAQSection } from './FAQSection'; +export { CTASection } from './CTASection'; +export { HorizontalScrollSection } from './HorizontalScrollSection'; \ No newline at end of file diff --git a/hooks/use-sequential-animation.ts b/hooks/use-sequential-animation.ts new file mode 100644 index 00000000000..3a75b6e15d1 --- /dev/null +++ b/hooks/use-sequential-animation.ts @@ -0,0 +1,70 @@ +'use client'; + +import { useEffect, useState, useRef } from 'react'; + +interface UseSequentialAnimationProps { + itemCount: number; + delay?: number; + duration?: number; +} + +export const useSequentialAnimation = ({ + itemCount, + delay = 200, + duration = 600 +}: UseSequentialAnimationProps) => { + const [visibleItems, setVisibleItems] = useState( + new Array(itemCount).fill(false) + ); + const [hasStarted, setHasStarted] = useState(false); + const observerRef = useRef(null); + const containerRef = useRef(null); + + useEffect(() => { + if (typeof window === 'undefined') return; + + observerRef.current = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting && !hasStarted) { + setHasStarted(true); + // Start sequential animation + for (let i = 0; i < itemCount; i++) { + setTimeout(() => { + setVisibleItems(prev => { + const newState = [...prev]; + newState[i] = true; + return newState; + }); + }, i * delay); + } + } + }); + }, + { threshold: 0.2 } + ); + + if (containerRef.current) { + observerRef.current.observe(containerRef.current); + } + + return () => { + if (observerRef.current) { + observerRef.current.disconnect(); + } + }; + }, [itemCount, delay, hasStarted]); + + const getItemStyle = (index: number) => ({ + transform: visibleItems[index] ? 'scale(1) translateY(0)' : 'scale(0.8) translateY(20px)', + opacity: visibleItems[index] ? 1 : 0, + transition: `all ${duration}ms cubic-bezier(0.175, 0.885, 0.32, 1.275)`, + }); + + return { + containerRef, + getItemStyle, + hasStarted, + visibleItems + }; +}; diff --git a/public/images/arena.png b/public/images/arena.png new file mode 100644 index 00000000000..21dccb199e8 Binary files /dev/null and b/public/images/arena.png differ diff --git a/public/images/avax.png b/public/images/avax.png new file mode 100644 index 00000000000..2170e4080e7 Binary files /dev/null and b/public/images/avax.png differ diff --git a/public/images/beam.png b/public/images/beam.png new file mode 100644 index 00000000000..52fb3ca7e6c Binary files /dev/null and b/public/images/beam.png differ diff --git a/public/images/bitcoin.png b/public/images/bitcoin.png new file mode 100644 index 00000000000..76fe12ea144 Binary files /dev/null and b/public/images/bitcoin.png differ diff --git a/public/images/coq.png b/public/images/coq.png new file mode 100644 index 00000000000..17ce08561d9 Binary files /dev/null and b/public/images/coq.png differ diff --git a/public/images/gun.png b/public/images/gun.png new file mode 100644 index 00000000000..946ef964ab4 Binary files /dev/null and b/public/images/gun.png differ diff --git a/public/images/tether.png b/public/images/tether.png new file mode 100644 index 00000000000..8df41c3929f Binary files /dev/null and b/public/images/tether.png differ diff --git a/public/images/usdc.png b/public/images/usdc.png new file mode 100644 index 00000000000..51c7749a026 Binary files /dev/null and b/public/images/usdc.png differ