Skip to content

Commit

Permalink
Merge pull request #23 from blockworks-foundation/mangosol-page
Browse files Browse the repository at this point in the history
mangosol page
  • Loading branch information
saml33 authored Jul 19, 2024
2 parents d4aef8c + 6c1295a commit 99e06f1
Show file tree
Hide file tree
Showing 16 changed files with 32,642 additions and 11 deletions.
37 changes: 37 additions & 0 deletions app/(pages)/mangosol/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Metadata } from 'next'
import MangoSolPage from '../../components/mangosol/MangoSolPage'

const metaTitle = 'Mango Markets | Safer. Smarter. Faster.'
const metaDescription =
'A magical DeFi experience powered by flashloans. Cross-margin trading with multi-collateral and groudbreaking safety features.'

export const metadata: Metadata = {
title: metaTitle,
description: metaDescription,
openGraph: {
title: metaTitle,
description: metaDescription,
url: 'https://mango.markets',
siteName: 'Mango Markets',
images: [
{
url: 'https://mango.markets/twitter-card.png',
width: 1200,
height: 600,
alt: metaTitle,
},
],
locale: 'en_US',
type: 'website',
},
}

async function MangoSol() {
return (
<div>
<MangoSolPage />
</div>
)
}

export default MangoSol
59 changes: 59 additions & 0 deletions app/components/mangosol/Faqs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use client'

import { Disclosure } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'

const FAQS = [
{
q: 'What is liquid staking?',
a: 'Liquid staking allows you to participate in staking without locking up your tokens. When you mint or buy a liquid staking token (LST) like mangoSOL it is representative of the underlying staked SOL. The price of the LST increases vs the underlying asset (SOL) as the staking rewards contribute to the value.',
},
{
q: 'What is mangoSOL?',
a: "mangoSOL is Mango's liquid staking token. The underlying SOL is staked to Mango's validator. Holding mangoSOL has exclusive benefits on Mango and helps to support the project.",
},
{
q: 'Where does the yield come from?',
a: 'Staking rewards. Validators earn SOL rewards for validating transactions. These rewards are passed on and accrue to the value of mangoSOL. This increases its value vs SOL over time.',
},
{
q: 'Is mangoSOL safe?',
a: 'mangoSOL was created via Sanctum. Sanctum LSTs use a version of the SPL stake pool program 4. Multiple security firms have audited this program. So far it controls $1B+ of value and has done so for more than two years, with no exploits. Of course, just because a contract has not been exploited in the past does not mean it will never ever be exploited in the future.',
},
{
q: 'How can I get mangoSOL?',
a: 'You can get mangoSOL on Mango, Jupiter or Sanctum.',
},
]

const Faqs = () => {
return (
<div className="border-b border-th-bkg-4">
{FAQS.map((faq, i) => (
<Disclosure key={i}>
{({ open }) => (
<div>
<Disclosure.Button
className={`w-full rounded-lg border-t border-th-bkg-4 p-6 text-left focus:outline-none`}
>
<div className="flex items-center justify-between">
<p className="text-lg">{faq.q}</p>
<ChevronDownIcon
className={`${
open ? 'rotate-180' : 'rotate-360'
} h-6 w-6 flex-shrink-0 text-th-fgd-3`}
/>
</div>
</Disclosure.Button>
<Disclosure.Panel className="p-6">
<p className="rewards-p">{faq.a}</p>
</Disclosure.Panel>
</div>
)}
</Disclosure>
))}
</div>
)
}

export default Faqs
30 changes: 30 additions & 0 deletions app/components/mangosol/ImageHeaderWrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client'
import { ReactNode, useEffect, useState } from 'react'
import SectionWrapper from '../shared/SectionWrapper'
import { useTheme } from 'next-themes'

const ImageHeaderWrapper = ({ children }: { children: ReactNode }) => {
const [mounted, setMounted] = useState(false)
const { theme } = useTheme()
const imageSrc =
theme === 'Dark'
? `bg-[url('/images/mangosol-bg-dark.webp')]`
: `bg-[url('/images/mangosol-bg.webp')]`

useEffect(() => {
setMounted(true)
}, [])

return mounted ? (
<SectionWrapper
noPaddingY
className={`${imageSrc} bg-cover bg-center bg-fixed bg-no-repeat border-t border-th-bkg-2 py-20`}
>
{children}
</SectionWrapper>
) : (
<div className="bg-th-bkg-2 py-20">{children}</div>
)
}

export default ImageHeaderWrapper
156 changes: 156 additions & 0 deletions app/components/mangosol/MangoSolPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import { BoltIcon, CakeIcon, SparklesIcon } from '@heroicons/react/20/solid'
import SectionWrapper from '../shared/SectionWrapper'
import IconWithText from '../shared/IconWithText'
import Image from 'next/image'
import ImageHeaderWrapper from './ImageHeaderWrapper'
import Stats from './Stats'
import LandingPageButton from '../shared/LandingPageButton'
import Faqs from './Faqs'

const HERO_P_CLASSES = 'text-lg md:text-2xl text-th-fgd-1 text-center max-w-3xl'
const H2_CLASSES = 'font-display text-center mb-6 text-4xl'

const MangoSolPage = () => {
// const [days, hours, minutes, seconds] = useCountdown(Date.now())

// const showCountdown =
// !isNaN(days) && !isNaN(hours) && !isNaN(minutes) && !isNaN(seconds)
return (
<>
<ImageHeaderWrapper>
<div className="flex flex-col items-center">
<Image
className="mb-6"
src="/icons/tokens/mangosol.svg"
height={72}
width={72}
alt="Chest"
/>
{/* <h1 className="mb-4 text-center lg:text-left font-display md:text-6xl">
Yield. Utility. Rewards.
</h1> */}
<h1 className="mb-4 text-center lg:text-left font-display md:text-6xl">
Yield x Utility
</h1>
<p className={`${HERO_P_CLASSES}`}>
mangoSOL is staked SOL fit for a king (of fruits)
</p>
<LandingPageButton
className="mt-8 mx-auto lg:mx-0"
linkText="Get mangoSOL"
path="https://app.mango.markets/swap?in=SOL&out=mangoSOL"
/>
{/* {showCountdown ? (
<>
<p className="mt-8 font-display mb-1 text-th-fgd-2 text-lg">
Season 1 ends in:
</p>
<div className="flex items-center space-x-0.5">
<CountdownDisplay value={days} type="Days" />
<span className="text-white font-display text-xl">:</span>
<CountdownDisplay value={hours} type="Hours" />
<span className="text-white font-display text-xl">:</span>
<CountdownDisplay value={minutes} type="Minutes" />
<span className="text-white font-display text-xl">:</span>
<CountdownDisplay value={seconds} type="Seconds" />
</div>
<p className="text-yellow-300 text-sm mt-2">
Hurry! There&apos;s still time to win this season
</p>
</>
) : null} */}
</div>
</ImageHeaderWrapper>
<Stats />
<SectionWrapper className="bg-th-bkg-2 flex flex-col items-center">
<h2 className={H2_CLASSES}>Yield.</h2>
<p className={`${HERO_P_CLASSES}`}>
All block rewards from the Mango validator are passed on to mangoSOL
holders to maximize yield.
</p>
</SectionWrapper>
<SectionWrapper>
<h2 className={H2_CLASSES}>Utility.</h2>
<div className="grid grid-cols-1 lg:grid-cols-6 gap-4 md:gap-6 xl:gap-8 mt-10">
<IconWithText
desc={
<span>
Get more oomph from your collateral. mangoSOL has equal
collateral weight to SOL and BTC making it the highest of any
LST listed on Mango.
</span>
}
icon={<SparklesIcon className="h-6 w-6 text-yellow-300" />}
title={'Immaculate collateral'}
/>
<IconWithText
desc={
<span>
mangoSOL is expempt from collateral funding fees on Mango. This
is a fee charged when an account borrows against risky
collateral.
</span>
}
icon={<CakeIcon className="h-6 w-6 text-yellow-300" />}
title={'Collateral fee free'}
/>
<IconWithText
desc={
<span>
By holding mangoSOL you are helping the Mango Validator land
transactions more effectively. This improves performance for all
traders.
</span>
}
icon={<BoltIcon className="h-6 w-6 text-yellow-300" />}
title={'Performance enhancing'}
/>
</div>
<LandingPageButton
className="mt-12 mx-auto"
linkText="Get mangoSOL"
path="https://app.mango.markets"
/>
</SectionWrapper>
{/* <SectionWrapper className="bg-th-bkg-2 flex flex-col items-center">
<h2 className={H2_CLASSES}>Rewards.</h2>
<p className={`${HERO_P_CLASSES}`}>
Holding at least one mangoSOL in your Mango Account earns you
automatic eligibility to participate in Mango&apos;s weekly rewards
program.
</p>
</SectionWrapper> */}
<SectionWrapper className="bg-th-bkg-2 border-b border-th-bkg-2">
<div className="max-w-4xl mx-auto">
<h2 className={H2_CLASSES}>FAQs.</h2>
<Faqs />
<LandingPageButton
className="mt-8 mx-auto"
linkText="Get mangoSOL"
path="https://app.mango.markets/swap?in=SOL&out=mangoSOL"
/>
</div>
</SectionWrapper>
</>
)
}

export default MangoSolPage

// const CountdownDisplay = ({ value, type }) => {
// const displayValue =
// value > 9 || type === 'Days' ? value.toString() : `0${value}`
// return (
// <div className="font-display bg-black px-2 rounded">
// <div className="mt-1.5">
// <FlipNumbers
// color="white"
// height={36}
// width={24}
// play
// numbers={displayValue}
// />
// </div>
// </div>
// )
// }
113 changes: 113 additions & 0 deletions app/components/mangosol/Stats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
'use client'
import { useQuery } from '@tanstack/react-query'
import store from '../../store/store'
import { useEffect, useMemo } from 'react'
import { floorToDecimal, numberCompacter } from '../../utils/numbers'
import SectionWrapper from '../shared/SectionWrapper'
import { Connection, PublicKey } from '@solana/web3.js'
import { getStakePoolAccount } from '@solana/spl-stake-pool'

const actions = store.getState().actions

const MANGOSOL_STAKE_POOL = '9jWbABPXfc75wseAbLEkBCb1NRaX9EbJZJTDQnbtpzc1'
const MANGOSOL_DECIMALS = 9
const MANGOSOL_MINT = 'MangmsBgFqJhW4cLUR9LxfVgMboY1xAoP8UUBiWwwuY'
const fetchStakePool = async (conn: Connection) => {
try {
const stakePool = await getStakePoolAccount(
conn,
new PublicKey(MANGOSOL_STAKE_POOL),
)
const nativeSupply = stakePool?.account?.data?.poolTokenSupply
if (nativeSupply) {
return nativeSupply.toNumber() / Math.pow(10, MANGOSOL_DECIMALS)
}

return 0
} catch (e) {
console.log('failed to fetch mangoSOL stake pool', e)
return 0
}
}

async function fetchApyToSol() {
const resp = await fetch(
'https://api.mngo.cloud/data/boost/stats/monthly-sol-price',
)
const json: { data: { mint: string; monthly_price_change: number }[] } =
await resp.json()
const tokenToApy: { [key: string]: number } = {}
const record = json.data.find((x) => x.mint === MANGOSOL_MINT)
const apy = (1 + (record?.monthly_price_change || 0)) ** 12 - 1
tokenToApy['mangosol'] = apy
return tokenToApy?.mangosol ? tokenToApy.mangosol * 100 : 0
}

const Stats = () => {
const group = store((s) => s.group)
const connection = store((s) => s.connection)
const { data: mangoSolSupply } = useQuery({
queryKey: ['mangosol-supply-data'],
queryFn: () => fetchStakePool(connection),
enabled: !!connection,
})
const { data: mangoSolApy } = useQuery({
queryKey: ['mangosol-apy'],
queryFn: () => fetchApyToSol(),
})

useEffect(() => {
actions.fetchGroup()
}, [])

const leverage = useMemo(() => {
if (!group) return 0
const bank = group.banksMapByName.get('mangoSOL')?.[0]
if (!bank) return 0
const weight = bank.scaledInitAssetWeight(bank.price)
const leverageFactor = 1 / (1 - weight.toNumber())
return floorToDecimal(leverageFactor, 1).toNumber()
}, [group])

const solPrice = useMemo(() => {
if (!group) return 0
const bank = group.banksMapByName.get('SOL')?.[0]
if (!bank) return 0
return bank?.uiPrice
}, [group])

return (
<SectionWrapper noPaddingY>
<div className="grid grid-cols-3 gap-6 py-12">
<div className="col-span-3 sm:col-span-1">
<p>APY</p>
<GradientText>
{mangoSolApy ? `${mangoSolApy.toFixed(1)}%` : '–'}
</GradientText>
</div>
<div className="col-span-3 sm:col-span-1">
<p>Total stake</p>
<GradientText>
{mangoSolSupply && solPrice
? `$${numberCompacter.format(mangoSolSupply * solPrice)}`
: '–'}
</GradientText>
</div>
<div className="col-span-3 sm:col-span-1">
<p>Leverage</p>
<GradientText>
{leverage ? `${leverage.toFixed(0)}x` : '–'}
</GradientText>
</div>
</div>
</SectionWrapper>
)
}

export default Stats

const GradientText = (props) => (
<span className="bg-gradient-to-bl from-yellow-500 via-orange-500 to-red-500 bg-clip-text text-transparent font-display text-5xl">
{props.children}
</span>
)
Loading

0 comments on commit 99e06f1

Please sign in to comment.