index.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. import ArrowForward from "@mui/icons-material/ArrowForward";
  2. import { Box, Button, Grid, Typography } from "@mui/material";
  3. import {
  4. Link as RouterLink,
  5. PageProps,
  6. graphql,
  7. useStaticQuery
  8. } from "gatsby";
  9. import React, { useEffect, useState } from "react";
  10. import GridWithCards from "../components/GridWithCards";
  11. import HeroText from "../components/HeroText";
  12. import Layout from "../components/Layout";
  13. import { SEO } from "../components/SEO";
  14. import { portal as portalUrl } from "../utils/urls";
  15. import { gsap } from "gsap";
  16. import { ScrollTrigger } from "gsap/ScrollTrigger";
  17. import apps from "../images/index/apps2.png";
  18. import blob from "../images/index/blob.svg";
  19. import cross from "../images/index/cross.svg";
  20. import cube from "../images/index/cube.svg";
  21. import portal from "../images/index/portal.png";
  22. import protocols from "../images/index/protocol_new.png";
  23. import shape1 from "../images/index/shape1.svg";
  24. import shape from "../images/shape.png";
  25. import shape2 from "../images/shape2.png";
  26. import { Totals, NotionalTvl } from "../components/ExplorerStats/ExplorerStats";
  27. import { amountFormatter } from "../utils/explorer";
  28. const featuredNumber = { fontSize: 42, fontFamily: "Suisse BP Intl", fontWeight: "bold" };
  29. const statsBaseUrl = "https://europe-west3-wormhole-315720.cloudfunctions.net/mainnet-"
  30. const IndexPage = ({ location }: PageProps) => {
  31. const { site } = useStaticQuery<IndexQueryType>(IndexStaticQuery)
  32. const [tvl, setTvl] = useState<number | undefined>(undefined)
  33. const [messageTotal, setMessageTotal] = useState<number | undefined>(undefined)
  34. let statsInterval: NodeJS.Timer | undefined = undefined
  35. const controller = new AbortController()
  36. const { signal } = controller
  37. const logo = {
  38. "@type": "ImageObject",
  39. "url": `${site.siteMetadata.siteUrl}/logo-and-name-stacked.png`,
  40. "height": "146",
  41. "width": "146"
  42. }
  43. const structuredData = {
  44. "@context": "https://schema.org",
  45. "@type": "Organization",
  46. "@id": "https://wormholenetwork.com#organization",
  47. mainEntityOfPage: "https://wormholenetwork.com#organization",
  48. url: "https://wormholenetwork.com",
  49. name: "Wormhole",
  50. sameAs: [
  51. "https://github.com/certusone/wormhole",
  52. "https://t.me/wormholecrypto",
  53. "https://twitter.com/wormholecrypto",
  54. "https://wormholebridge.com",
  55. "https://wormholecrypto.medium.com",
  56. "https://discord.gg/wormholecrypto",
  57. ],
  58. alternateName: [
  59. "wormhole network",
  60. "wormhole protocol",
  61. "wormhole bridge",
  62. "wormhole crypto",
  63. "certus one wormhole",
  64. "solana wormhole",
  65. "SOL wormhole",
  66. "terra wormhole",
  67. "LUNA wormhole",
  68. "ethereum wormhole",
  69. "ETH wormhole",
  70. "binance wormhole",
  71. "BSC wormhole",
  72. "oasis wormhole",
  73. "ROSE wormhole",
  74. "avalanche wormhole",
  75. "AVAX wormhole"
  76. ],
  77. description: "A cross-chain messaging protocol.",
  78. image: logo,
  79. logo: logo
  80. }
  81. const headerImage = React.useRef<HTMLCanvasElement>(null);
  82. const gradient1 = React.useRef<HTMLCanvasElement>(null);
  83. const gradient2 = React.useRef<HTMLCanvasElement>(null);
  84. function fetchStats() {
  85. const tvlUrl = `${statsBaseUrl}notionaltvl`
  86. const messagesUrl = `${statsBaseUrl}totals`
  87. fetch(tvlUrl, { signal }).then((res) => {
  88. if (res.ok) return res.json();
  89. }).then((result: NotionalTvl) => {
  90. setTvl(result.AllTime["*"]["*"].Notional);
  91. }, (error) => {
  92. if (error.name !== "AbortError") console.error("failed fetching notional TVL. error: ", error);
  93. });
  94. fetch(messagesUrl, { signal }).then((res) => {
  95. if (res.ok) return res.json();
  96. }).then((result: Totals) => {
  97. setMessageTotal(result.TotalCount["*"]);
  98. }, (error) => {
  99. if (error.name !== "AbortError") console.error("failed fetching totals. error: ", error);
  100. });
  101. }
  102. useEffect(() => {
  103. statsInterval = setInterval(fetchStats, 30000)
  104. gsap.registerPlugin(ScrollTrigger);
  105. gsap.from(headerImage.current, {
  106. scale: 1.1,
  107. duration: 10,
  108. delay: 1,
  109. rotation: 3,
  110. ease: "Power3.easeOut",
  111. })
  112. gsap.to(gradient1.current, {
  113. scale: 1.2,
  114. ease: "Power3.easeOut",
  115. x: 300,
  116. scrollTrigger: {
  117. trigger: gradient1.current,
  118. start: "-0% 0%",
  119. end: "+=500",
  120. scrub: 1,
  121. },
  122. })
  123. gsap.from(gradient2.current, {
  124. scale: 0.5,
  125. ease: "Power3.easeOut",
  126. scrollTrigger: {
  127. trigger: gradient2.current,
  128. start: "-50% 50%",
  129. end: "+=1000",
  130. scrub: 1,
  131. },
  132. })
  133. return function cleanup() {
  134. // clear any ongoing intervals
  135. if (statsInterval) {
  136. clearInterval(statsInterval);
  137. }
  138. // abort any in-flight requests
  139. controller.abort();
  140. }
  141. }, [])
  142. return (
  143. <Layout>
  144. <SEO
  145. // use default title for index
  146. description="The best of blockchains. Move information and value anywhere."
  147. pathname={location.pathname}
  148. >
  149. <script type="application/ld+json">
  150. {JSON.stringify(structuredData, undefined, 4)}
  151. </script>
  152. </SEO>
  153. <Box sx={{ position: "relative", marginTop: 21 }}>
  154. <Box
  155. ref={headerImage}
  156. sx={{
  157. position: "absolute",
  158. zIndex: -1,
  159. transform: "translate(0px, -25%)",
  160. background: `url(${shape1})`,
  161. backgroundRepeat: "no-repeat",
  162. backgroundPosition: "top -240px center",
  163. backgroundSize: "2070px 1155px",
  164. width: "100%",
  165. height: 1155,
  166. }}
  167. />
  168. <HeroText
  169. maxWidth={600}
  170. heroSpans={["The best of", "blockchains"]}
  171. subtitleText="Move information and value anywhere."
  172. />
  173. <Box
  174. sx={{
  175. m: "auto",
  176. maxWidth: { xs: 240, sm: 600 },
  177. textAlign: "center",
  178. }}
  179. >
  180. <Box
  181. sx={{
  182. width: "calc( 100% - 16px )",
  183. mx: "auto",
  184. mt: 15.5,
  185. display: "flex",
  186. flexWrap: "wrap",
  187. justifyContent: "center",
  188. }}
  189. >
  190. {tvl && <Box
  191. sx={{
  192. mt: 2,
  193. mx: 1,
  194. pt: { xs: 1, sm: 0 },
  195. display: "flex",
  196. alignItems: "center",
  197. justifyContent: "space-evenly",
  198. flexBasis: { xs: "100%", sm: "calc(33.33333% - 16px)" },
  199. borderTop: "1px solid white",
  200. }}
  201. >
  202. <Typography sx={featuredNumber}>${amountFormatter(tvl)}</Typography>
  203. <Typography variant="body2">in TVL</Typography>
  204. </Box>}
  205. <Box
  206. sx={{
  207. mt: 2,
  208. mx: 1,
  209. pt: { xs: 1, sm: 0 },
  210. display: "flex",
  211. alignItems: "center",
  212. justifyContent: "space-evenly",
  213. flexBasis: { xs: "100%", sm: "calc(33.33333% - 16px)" },
  214. borderTop: "1px solid white",
  215. }}
  216. >
  217. <Typography sx={featuredNumber}>7</Typography>
  218. <Typography variant="body2">chain integrations</Typography>
  219. </Box>
  220. {messageTotal && <Box
  221. sx={{
  222. mt: 2,
  223. mx: 1,
  224. pt: { xs: 1, sm: 0 },
  225. display: "flex",
  226. alignItems: "center",
  227. justifyContent: "space-evenly",
  228. flexBasis: { xs: "100%", sm: "calc(33.33333% - 16px)" },
  229. borderTop: "1px solid white",
  230. }}
  231. >
  232. <Typography sx={featuredNumber}>
  233. {amountFormatter(messageTotal, 0)}
  234. </Typography>
  235. <Typography variant="body2">txs</Typography>
  236. </Box>}
  237. </Box>
  238. </Box>
  239. </Box>
  240. <Box sx={{ position: 'relative' }}>
  241. <Box
  242. ref={gradient1}
  243. sx={{
  244. position: "absolute",
  245. zIndex: -1,
  246. top: '50%',
  247. background: 'radial-gradient(closest-side at 50% 50%, #E72850 0%, #E7285000 100%)',
  248. transform: 'matrix(0.96, 0.29, -0.29, 0.96, 0, 0)',
  249. left: '60%',
  250. width: 1645,
  251. height: 903,
  252. pointerEvents: 'none',
  253. opacity: 0.7,
  254. }}
  255. />
  256. <Box
  257. ref={gradient2}
  258. sx={{
  259. position: "absolute",
  260. zIndex: -1,
  261. top: '65%',
  262. background: 'radial-gradient(closest-side at 50% 50%, #5189C8 0%, #5189C800 100%) ',
  263. transform: 'matrix(0.67, 0.74, -0.74, 0.67, 0, 0)',
  264. left: '5%',
  265. width: 1136,
  266. height: 1489,
  267. pointerEvents: 'none',
  268. opacity: 0.64,
  269. }}
  270. />
  271. <Box
  272. sx={{
  273. position: "absolute",
  274. zIndex: -1,
  275. background: `url(${shape})`,
  276. backgroundSize: 'contain',
  277. top: -100,
  278. right: '70vw',
  279. width: 1363,
  280. height: 1130,
  281. pointerEvents: 'none',
  282. display: { xs: 'none', md: 'block' },
  283. }}
  284. />
  285. <Box
  286. sx={{
  287. display: "flex",
  288. flexWrap: "wrap",
  289. maxWidth: 1220,
  290. px: 3.75,
  291. mt: 35,
  292. mx: "auto",
  293. alignItems: "center",
  294. justifyContent: "center",
  295. }}
  296. >
  297. <Box
  298. sx={{
  299. flexBasis: { xs: "100%", md: "40%" },
  300. flexGrow: 1,
  301. }}
  302. >
  303. <Box sx={{ px: { xs: 0, md: 4 } }}>
  304. <Box sx={{ maxWidth: 348, mx: "auto" }}>
  305. <Typography variant="h3">
  306. <Box component="span" sx={{ color: "#FFCE00" }}>
  307. Protocol:{" "}
  308. </Box>
  309. <Box component="span" sx={{ display: "inline-block" }}>
  310. the core layer
  311. </Box>
  312. </Typography>
  313. <Typography sx={{ mt: 2 }}>
  314. The foundation that an ecosystem of apps is built on top of.
  315. </Typography>
  316. <Button
  317. component={RouterLink}
  318. to="/buidl/"
  319. sx={{ mt: 3 }}
  320. variant="outlined"
  321. color="inherit"
  322. endIcon={<ArrowForward />}
  323. >
  324. Learn More
  325. </Button>
  326. </Box>
  327. </Box>
  328. </Box>
  329. <Box
  330. sx={{
  331. mt: { xs: 8, md: null },
  332. flexBasis: { xs: "100%", md: "60%" },
  333. textAlign: "center",
  334. flexGrow: 1,
  335. }}
  336. >
  337. <Box
  338. component="img"
  339. src={protocols}
  340. alt=""
  341. sx={{ maxWidth: "100%" }}
  342. />
  343. </Box>
  344. </Box>
  345. </Box>
  346. <Box
  347. sx={{
  348. display: "flex",
  349. flexWrap: "wrap-reverse",
  350. maxWidth: 1220,
  351. px: 3.75,
  352. mt: 15.5,
  353. mx: "auto",
  354. alignItems: "center",
  355. justifyContent: "center",
  356. }}
  357. >
  358. <Box
  359. sx={{
  360. mt: { xs: 8, md: null },
  361. flexBasis: { xs: "100%", md: "60%" },
  362. textAlign: "center",
  363. flexGrow: 1,
  364. }}
  365. >
  366. <Box component="img" src={apps} alt="" sx={{ maxWidth: "100%" }} />
  367. </Box>
  368. <Box sx={{ flexBasis: { xs: "100%", md: "40%" }, flexGrow: 1 }}>
  369. <Box sx={{ px: { xs: 0, md: 4 } }}>
  370. <Box sx={{ maxWidth: 348, mx: "auto" }}>
  371. <Typography variant="h3">
  372. <Box component="span" sx={{ color: "#FFCE00" }}>
  373. Apps:{" "}
  374. </Box>
  375. <Box component="span">endless possibilities</Box>
  376. </Typography>
  377. <Typography sx={{ mt: 2 }}>
  378. Apps can now live across chains at once and integrate the best
  379. of each.
  380. </Typography>
  381. <Button
  382. component={RouterLink}
  383. to="/apps/"
  384. sx={{ mt: 3 }}
  385. variant="outlined"
  386. color="inherit"
  387. endIcon={<ArrowForward />}
  388. >
  389. Learn More
  390. </Button>
  391. </Box>
  392. </Box>
  393. </Box>
  394. </Box>
  395. <Box sx={{ position: 'relative' }}>
  396. <Box
  397. sx={{
  398. position: "absolute",
  399. zIndex: -1,
  400. background: `url(${shape2})`,
  401. backgroundSize: 'contain',
  402. top: '50%',
  403. transform: 'translateY(-50%)',
  404. left: '75vw',
  405. width: 1612,
  406. height: 1316,
  407. pointerEvents: 'none',
  408. display: { xs: 'none', md: 'block' },
  409. }}
  410. />
  411. <Box
  412. sx={{
  413. display: "flex",
  414. flexWrap: "wrap",
  415. maxWidth: 1220,
  416. px: 3.75,
  417. mt: 15.5,
  418. mx: "auto",
  419. alignItems: "center",
  420. justifyContent: "center",
  421. }}
  422. >
  423. <Box
  424. sx={{
  425. flexBasis: { xs: "100%", md: "40%" },
  426. flexGrow: 1,
  427. }}
  428. >
  429. <Box sx={{ px: { xs: 0, md: 4 } }}>
  430. <Box sx={{ maxWidth: 340, mx: "auto" }}>
  431. <Typography variant="h3">
  432. <Box component="span" sx={{ color: "#FFCE00" }}>
  433. Portal:{" "}
  434. </Box>
  435. <Box component="span" sx={{ display: "inline-block" }}>
  436. a token bridge
  437. </Box>
  438. </Typography>
  439. <Typography sx={{ mt: 2 }}>
  440. Never have to retrace your steps, with unlimited transfers
  441. across chains for tokens and NFTs wrapped by Wormhole.
  442. </Typography>
  443. <Button
  444. href={portalUrl}
  445. sx={{ mt: 3 }}
  446. variant="outlined"
  447. color="inherit"
  448. endIcon={<ArrowForward />}
  449. >
  450. Learn More
  451. </Button>
  452. </Box>
  453. </Box>
  454. </Box>
  455. <Box
  456. sx={{
  457. mt: { xs: 8, md: null },
  458. flexBasis: { xs: "100%", md: "60%" },
  459. textAlign: "center",
  460. flexGrow: 1,
  461. }}
  462. >
  463. <Button variant="text" href={portalUrl} sx={{
  464. '&:hover': {
  465. background: 'transparent'
  466. },
  467. 'span': {
  468. display: 'none'
  469. }
  470. }}>
  471. <Box component="img" src={portal} alt="" sx={{ maxWidth: "100%" }} />
  472. </Button>
  473. </Box>
  474. </Box>
  475. </Box>
  476. <Box sx={{ textAlign: "center", mt: 12.5, px: 2 }}>
  477. <Typography variant="h3">
  478. <Box component="span" sx={{ color: "#FFCE00" }}>
  479. Cross-chain
  480. </Box>
  481. <Box component="span"> everything</Box>
  482. </Typography>
  483. <Typography sx={{ mt: 2, maxWidth: 480, mx: "auto", fontWeight: 300 }}>
  484. Each blockchain has a distinct strength. Wormhole lets you get the
  485. best out of every blockchain without compromise.
  486. </Typography>
  487. </Box>
  488. <Box sx={{ maxWidth: 1220, mx: "auto", mt: 12, px: 3.75 }}>
  489. <GridWithCards
  490. data={[
  491. {
  492. src: cross,
  493. size: 220,
  494. header: "Never stop expanding",
  495. description:
  496. "Chains, information, and users are growing everyday. Build on a protocol that is set up to scale, with no limits, right from the start.",
  497. },
  498. {
  499. src: blob,
  500. size: 220,
  501. header: "Explore and experiment",
  502. description:
  503. "Now is the time to explore and experiment. The only limit to what you're able to build is your imagination.",
  504. },
  505. {
  506. src: cube,
  507. size: 220,
  508. header: "Power your project",
  509. description:
  510. "Join the growing list of projects that are composing, raising, and succeeding with Wormhole core layer.",
  511. },
  512. ]}
  513. />
  514. </Box>
  515. </Layout>
  516. );
  517. };
  518. type IndexQueryType = {
  519. site: {
  520. siteMetadata: {
  521. siteUrl: string
  522. }
  523. }
  524. }
  525. const IndexStaticQuery = graphql`
  526. query Index {
  527. site {
  528. siteMetadata {
  529. siteUrl
  530. }
  531. }
  532. }
  533. `
  534. export default IndexPage;