564 lines
16 KiB
TypeScript
564 lines
16 KiB
TypeScript
import ArrowForward from "@mui/icons-material/ArrowForward";
|
|
import { Box, Button, Grid, Typography } from "@mui/material";
|
|
import {
|
|
Link as RouterLink,
|
|
PageProps,
|
|
graphql,
|
|
useStaticQuery
|
|
} from "gatsby";
|
|
import React, { useEffect, useState } from "react";
|
|
import GridWithCards from "../components/GridWithCards";
|
|
import HeroText from "../components/HeroText";
|
|
import Layout from "../components/Layout";
|
|
import { SEO } from "../components/SEO";
|
|
import { portal as portalUrl } from "../utils/urls";
|
|
|
|
import { gsap } from "gsap";
|
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
|
|
|
import apps from "../images/index/apps2.png";
|
|
import blob from "../images/index/blob.svg";
|
|
import cross from "../images/index/cross.svg";
|
|
import cube from "../images/index/cube.svg";
|
|
import portal from "../images/index/portal.png";
|
|
import protocols from "../images/index/protocol_new.png";
|
|
import shape1 from "../images/index/shape1.svg";
|
|
import shape from "../images/shape.png";
|
|
import shape2 from "../images/shape2.png";
|
|
import { Totals, NotionalTvl } from "../components/ExplorerStats/ExplorerStats";
|
|
import { amountFormatter } from "../utils/explorer";
|
|
|
|
const featuredNumber = { fontSize: 42, fontFamily: "Suisse BP Intl", fontWeight: "bold" };
|
|
const statsBaseUrl = "https://europe-west3-wormhole-315720.cloudfunctions.net/mainnet-"
|
|
|
|
const IndexPage = ({ location }: PageProps) => {
|
|
const { site } = useStaticQuery<IndexQueryType>(IndexStaticQuery)
|
|
const [tvl, setTvl] = useState<number | undefined>(undefined)
|
|
const [messageTotal, setMessageTotal] = useState<number | undefined>(undefined)
|
|
|
|
let statsInterval: NodeJS.Timer | undefined = undefined
|
|
const controller = new AbortController()
|
|
const { signal } = controller
|
|
|
|
|
|
const logo = {
|
|
"@type": "ImageObject",
|
|
"url": `${site.siteMetadata.siteUrl}/logo-and-name-stacked.png`,
|
|
"height": "146",
|
|
"width": "146"
|
|
}
|
|
const structuredData = {
|
|
"@context": "https://schema.org",
|
|
"@type": "Organization",
|
|
"@id": "https://wormholenetwork.com#organization",
|
|
mainEntityOfPage: "https://wormholenetwork.com#organization",
|
|
url: "https://wormholenetwork.com",
|
|
name: "Wormhole",
|
|
sameAs: [
|
|
"https://github.com/certusone/wormhole",
|
|
"https://t.me/wormholecrypto",
|
|
"https://twitter.com/wormholecrypto",
|
|
"https://wormholebridge.com",
|
|
"https://wormholecrypto.medium.com",
|
|
"https://discord.gg/wormholecrypto",
|
|
],
|
|
alternateName: [
|
|
"wormhole network",
|
|
"wormhole protocol",
|
|
"wormhole bridge",
|
|
"wormhole crypto",
|
|
"certus one wormhole",
|
|
"solana wormhole",
|
|
"SOL wormhole",
|
|
"terra wormhole",
|
|
"LUNA wormhole",
|
|
"ethereum wormhole",
|
|
"ETH wormhole",
|
|
"binance wormhole",
|
|
"BSC wormhole",
|
|
"oasis wormhole",
|
|
"ROSE wormhole",
|
|
"avalanche wormhole",
|
|
"AVAX wormhole"
|
|
],
|
|
description: "A cross-chain messaging protocol.",
|
|
image: logo,
|
|
logo: logo
|
|
}
|
|
|
|
const headerImage = React.useRef<HTMLCanvasElement>(null);
|
|
const gradient1 = React.useRef<HTMLCanvasElement>(null);
|
|
const gradient2 = React.useRef<HTMLCanvasElement>(null);
|
|
|
|
|
|
function fetchStats() {
|
|
const tvlUrl = `${statsBaseUrl}notionaltvl`
|
|
const messagesUrl = `${statsBaseUrl}totals`
|
|
|
|
fetch(tvlUrl, { signal }).then((res) => {
|
|
if (res.ok) return res.json();
|
|
}).then((result: NotionalTvl) => {
|
|
setTvl(result.AllTime["*"]["*"].Notional);
|
|
}, (error) => {
|
|
if (error.name !== "AbortError") console.error("failed fetching notional TVL. error: ", error);
|
|
});
|
|
fetch(messagesUrl, { signal }).then((res) => {
|
|
if (res.ok) return res.json();
|
|
}).then((result: Totals) => {
|
|
setMessageTotal(result.TotalCount["*"]);
|
|
}, (error) => {
|
|
if (error.name !== "AbortError") console.error("failed fetching totals. error: ", error);
|
|
});
|
|
}
|
|
|
|
useEffect(() => {
|
|
statsInterval = setInterval(fetchStats, 30000)
|
|
|
|
gsap.registerPlugin(ScrollTrigger);
|
|
|
|
|
|
gsap.from(headerImage.current, {
|
|
scale: 1.1,
|
|
duration: 10,
|
|
delay: 1,
|
|
rotation: 3,
|
|
ease: "Power3.easeOut",
|
|
})
|
|
|
|
gsap.to(gradient1.current, {
|
|
scale: 1.2,
|
|
ease: "Power3.easeOut",
|
|
x: 300,
|
|
scrollTrigger: {
|
|
trigger: gradient1.current,
|
|
start: "-0% 0%",
|
|
end: "+=500",
|
|
scrub: 1,
|
|
},
|
|
})
|
|
|
|
gsap.from(gradient2.current, {
|
|
scale: 0.5,
|
|
ease: "Power3.easeOut",
|
|
scrollTrigger: {
|
|
trigger: gradient2.current,
|
|
start: "-50% 50%",
|
|
end: "+=1000",
|
|
scrub: 1,
|
|
},
|
|
})
|
|
|
|
return function cleanup() {
|
|
// clear any ongoing intervals
|
|
if (statsInterval) {
|
|
clearInterval(statsInterval);
|
|
}
|
|
// abort any in-flight requests
|
|
controller.abort();
|
|
}
|
|
}, [])
|
|
|
|
return (
|
|
<Layout>
|
|
<SEO
|
|
// use default title for index
|
|
description="The best of blockchains. Move information and value anywhere."
|
|
pathname={location.pathname}
|
|
>
|
|
<script type="application/ld+json">
|
|
{JSON.stringify(structuredData, undefined, 4)}
|
|
</script>
|
|
</SEO>
|
|
<Box sx={{ position: "relative", marginTop: 21 }}>
|
|
<Box
|
|
ref={headerImage}
|
|
sx={{
|
|
position: "absolute",
|
|
zIndex: -1,
|
|
transform: "translate(0px, -25%)",
|
|
background: `url(${shape1})`,
|
|
backgroundRepeat: "no-repeat",
|
|
backgroundPosition: "top -240px center",
|
|
backgroundSize: "2070px 1155px",
|
|
width: "100%",
|
|
height: 1155,
|
|
}}
|
|
/>
|
|
<HeroText
|
|
maxWidth={600}
|
|
heroSpans={["The best of", "blockchains"]}
|
|
subtitleText="Move information and value anywhere."
|
|
/>
|
|
<Box
|
|
sx={{
|
|
m: "auto",
|
|
maxWidth: { xs: 240, sm: 600 },
|
|
textAlign: "center",
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
width: "calc( 100% - 16px )",
|
|
mx: "auto",
|
|
mt: 15.5,
|
|
display: "flex",
|
|
flexWrap: "wrap",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
{tvl && <Box
|
|
sx={{
|
|
mt: 2,
|
|
mx: 1,
|
|
pt: { xs: 1, sm: 0 },
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-evenly",
|
|
flexBasis: { xs: "100%", sm: "calc(33.33333% - 16px)" },
|
|
borderTop: "1px solid white",
|
|
}}
|
|
>
|
|
<Typography sx={featuredNumber}>${amountFormatter(tvl)}</Typography>
|
|
<Typography variant="body2">in TVL</Typography>
|
|
</Box>}
|
|
<Box
|
|
sx={{
|
|
mt: 2,
|
|
mx: 1,
|
|
pt: { xs: 1, sm: 0 },
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-evenly",
|
|
flexBasis: { xs: "100%", sm: "calc(33.33333% - 16px)" },
|
|
borderTop: "1px solid white",
|
|
}}
|
|
>
|
|
<Typography sx={featuredNumber}>7</Typography>
|
|
<Typography variant="body2">chain integrations</Typography>
|
|
</Box>
|
|
{messageTotal && <Box
|
|
sx={{
|
|
mt: 2,
|
|
mx: 1,
|
|
pt: { xs: 1, sm: 0 },
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-evenly",
|
|
flexBasis: { xs: "100%", sm: "calc(33.33333% - 16px)" },
|
|
borderTop: "1px solid white",
|
|
}}
|
|
>
|
|
<Typography sx={featuredNumber}>
|
|
{amountFormatter(messageTotal, 0)}
|
|
</Typography>
|
|
<Typography variant="body2">txs</Typography>
|
|
</Box>}
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
|
|
|
|
<Box sx={{ position: 'relative' }}>
|
|
<Box
|
|
ref={gradient1}
|
|
sx={{
|
|
position: "absolute",
|
|
zIndex: -1,
|
|
top: '50%',
|
|
background: 'radial-gradient(closest-side at 50% 50%, #E72850 0%, #E7285000 100%)',
|
|
transform: 'matrix(0.96, 0.29, -0.29, 0.96, 0, 0)',
|
|
left: '60%',
|
|
width: 1645,
|
|
height: 903,
|
|
pointerEvents: 'none',
|
|
opacity: 0.7,
|
|
}}
|
|
/>
|
|
<Box
|
|
ref={gradient2}
|
|
sx={{
|
|
position: "absolute",
|
|
zIndex: -1,
|
|
top: '65%',
|
|
background: 'radial-gradient(closest-side at 50% 50%, #5189C8 0%, #5189C800 100%) ',
|
|
transform: 'matrix(0.67, 0.74, -0.74, 0.67, 0, 0)',
|
|
left: '5%',
|
|
width: 1136,
|
|
height: 1489,
|
|
pointerEvents: 'none',
|
|
opacity: 0.64,
|
|
}}
|
|
/>
|
|
<Box
|
|
sx={{
|
|
position: "absolute",
|
|
zIndex: -1,
|
|
background: `url(${shape})`,
|
|
backgroundSize: 'contain',
|
|
top: -100,
|
|
right: '70vw',
|
|
width: 1363,
|
|
height: 1130,
|
|
pointerEvents: 'none',
|
|
display: { xs: 'none', md: 'block' },
|
|
}}
|
|
/>
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
flexWrap: "wrap",
|
|
maxWidth: 1220,
|
|
px: 3.75,
|
|
mt: 35,
|
|
mx: "auto",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
|
|
<Box
|
|
sx={{
|
|
flexBasis: { xs: "100%", md: "40%" },
|
|
flexGrow: 1,
|
|
}}
|
|
>
|
|
<Box sx={{ px: { xs: 0, md: 4 } }}>
|
|
<Box sx={{ maxWidth: 348, mx: "auto" }}>
|
|
<Typography variant="h3">
|
|
<Box component="span" sx={{ color: "#FFCE00" }}>
|
|
Protocol:{" "}
|
|
</Box>
|
|
<Box component="span" sx={{ display: "inline-block" }}>
|
|
the core layer
|
|
</Box>
|
|
</Typography>
|
|
<Typography sx={{ mt: 2 }}>
|
|
The foundation that an ecosystem of apps is built on top of.
|
|
</Typography>
|
|
<Button
|
|
component={RouterLink}
|
|
to="/buidl/"
|
|
sx={{ mt: 3 }}
|
|
variant="outlined"
|
|
color="inherit"
|
|
endIcon={<ArrowForward />}
|
|
>
|
|
Learn More
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
<Box
|
|
sx={{
|
|
mt: { xs: 8, md: null },
|
|
flexBasis: { xs: "100%", md: "60%" },
|
|
textAlign: "center",
|
|
flexGrow: 1,
|
|
}}
|
|
>
|
|
<Box
|
|
component="img"
|
|
src={protocols}
|
|
alt=""
|
|
sx={{ maxWidth: "100%" }}
|
|
/>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
|
|
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
flexWrap: "wrap-reverse",
|
|
maxWidth: 1220,
|
|
px: 3.75,
|
|
mt: 15.5,
|
|
mx: "auto",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
mt: { xs: 8, md: null },
|
|
flexBasis: { xs: "100%", md: "60%" },
|
|
textAlign: "center",
|
|
flexGrow: 1,
|
|
}}
|
|
>
|
|
<Box component="img" src={apps} alt="" sx={{ maxWidth: "100%" }} />
|
|
</Box>
|
|
<Box sx={{ flexBasis: { xs: "100%", md: "40%" }, flexGrow: 1 }}>
|
|
<Box sx={{ px: { xs: 0, md: 4 } }}>
|
|
<Box sx={{ maxWidth: 348, mx: "auto" }}>
|
|
<Typography variant="h3">
|
|
<Box component="span" sx={{ color: "#FFCE00" }}>
|
|
Apps:{" "}
|
|
</Box>
|
|
<Box component="span">endless possibilities</Box>
|
|
</Typography>
|
|
<Typography sx={{ mt: 2 }}>
|
|
Apps can now live across chains at once and integrate the best
|
|
of each.
|
|
</Typography>
|
|
<Button
|
|
component={RouterLink}
|
|
to="/apps/"
|
|
sx={{ mt: 3 }}
|
|
variant="outlined"
|
|
color="inherit"
|
|
endIcon={<ArrowForward />}
|
|
>
|
|
Learn More
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
|
|
<Box sx={{ position: 'relative' }}>
|
|
<Box
|
|
sx={{
|
|
position: "absolute",
|
|
zIndex: -1,
|
|
background: `url(${shape2})`,
|
|
backgroundSize: 'contain',
|
|
top: '50%',
|
|
transform: 'translateY(-50%)',
|
|
left: '75vw',
|
|
width: 1612,
|
|
height: 1316,
|
|
pointerEvents: 'none',
|
|
display: { xs: 'none', md: 'block' },
|
|
}}
|
|
/>
|
|
<Box
|
|
sx={{
|
|
display: "flex",
|
|
flexWrap: "wrap",
|
|
maxWidth: 1220,
|
|
px: 3.75,
|
|
mt: 15.5,
|
|
mx: "auto",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
<Box
|
|
sx={{
|
|
flexBasis: { xs: "100%", md: "40%" },
|
|
flexGrow: 1,
|
|
}}
|
|
>
|
|
<Box sx={{ px: { xs: 0, md: 4 } }}>
|
|
<Box sx={{ maxWidth: 340, mx: "auto" }}>
|
|
<Typography variant="h3">
|
|
<Box component="span" sx={{ color: "#FFCE00" }}>
|
|
Portal:{" "}
|
|
</Box>
|
|
<Box component="span" sx={{ display: "inline-block" }}>
|
|
a token bridge
|
|
</Box>
|
|
</Typography>
|
|
<Typography sx={{ mt: 2 }}>
|
|
Never have to retrace your steps, with unlimited transfers
|
|
across chains for tokens and NFTs wrapped by Wormhole.
|
|
</Typography>
|
|
<Button
|
|
href={portalUrl}
|
|
sx={{ mt: 3 }}
|
|
variant="outlined"
|
|
color="inherit"
|
|
endIcon={<ArrowForward />}
|
|
>
|
|
Learn More
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
<Box
|
|
sx={{
|
|
mt: { xs: 8, md: null },
|
|
flexBasis: { xs: "100%", md: "60%" },
|
|
textAlign: "center",
|
|
flexGrow: 1,
|
|
}}
|
|
>
|
|
<Button variant="text" href={portalUrl} sx={{
|
|
'&:hover': {
|
|
background: 'transparent'
|
|
},
|
|
'span': {
|
|
display: 'none'
|
|
}
|
|
}}>
|
|
<Box component="img" src={portal} alt="" sx={{ maxWidth: "100%" }} />
|
|
</Button>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
|
|
<Box sx={{ textAlign: "center", mt: 12.5, px: 2 }}>
|
|
<Typography variant="h3">
|
|
<Box component="span" sx={{ color: "#FFCE00" }}>
|
|
Cross-chain
|
|
</Box>
|
|
<Box component="span"> everything</Box>
|
|
</Typography>
|
|
<Typography sx={{ mt: 2, maxWidth: 480, mx: "auto", fontWeight: 300 }}>
|
|
Each blockchain has a distinct strength. Wormhole lets you get the
|
|
best out of every blockchain without compromise.
|
|
</Typography>
|
|
</Box>
|
|
<Box sx={{ maxWidth: 1220, mx: "auto", mt: 12, px: 3.75 }}>
|
|
<GridWithCards
|
|
data={[
|
|
{
|
|
src: cross,
|
|
size: 220,
|
|
header: "Never stop expanding",
|
|
description:
|
|
"Chains, information, and users are growing everyday. Build on a protocol that is set up to scale, with no limits, right from the start.",
|
|
},
|
|
{
|
|
src: blob,
|
|
size: 220,
|
|
header: "Explore and experiment",
|
|
description:
|
|
"Now is the time to explore and experiment. The only limit to what you're able to build is your imagination.",
|
|
},
|
|
{
|
|
src: cube,
|
|
size: 220,
|
|
header: "Power your project",
|
|
description:
|
|
"Join the growing list of projects that are composing, raising, and succeeding with Wormhole core layer.",
|
|
},
|
|
]}
|
|
/>
|
|
</Box>
|
|
|
|
</Layout>
|
|
);
|
|
};
|
|
|
|
type IndexQueryType = {
|
|
site: {
|
|
siteMetadata: {
|
|
siteUrl: string
|
|
}
|
|
}
|
|
}
|
|
const IndexStaticQuery = graphql`
|
|
query Index {
|
|
site {
|
|
siteMetadata {
|
|
siteUrl
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
export default IndexPage;
|