Compare commits

...

4 Commits

Author SHA1 Message Date
saml33 d519685520 fix shadow 2024-04-03 14:46:03 +11:00
saml33 b53facc749 terms modal style fix 2024-04-03 14:41:21 +11:00
saml33 9d44e47827 hide edit leverage when no position 2024-04-03 14:37:59 +11:00
saml33 b1dd5fc7f4 show all token rates 2024-04-03 14:17:19 +11:00
9 changed files with 220 additions and 89 deletions

View File

@ -0,0 +1,79 @@
import Image from 'next/image'
import { formatTokenSymbol } from 'utils/tokens'
import useBankRates from 'hooks/useBankRates'
import useLeverageMax from 'hooks/useLeverageMax'
import mangoStore from '@store/mangoStore'
import SheenLoader from './shared/SheenLoader'
const HeroTokenButton = ({
onClick,
tokenName,
}: {
tokenName: string
onClick: () => void
}) => {
const leverage = useLeverageMax(tokenName)
const groupLoaded = mangoStore((s) => s.groupLoaded)
const { stakeBankDepositRate, financialMetrics } = useBankRates(
tokenName,
leverage,
)
const { financialMetrics: estimatedNetAPYFor1xLev } = useBankRates(
tokenName,
1,
)
const APY_Daily_Compound =
Math.pow(1 + Number(stakeBankDepositRate) / 365, 365) - 1
const UiRate =
tokenName === 'USDC'
? APY_Daily_Compound * 100
: Math.max(estimatedNetAPYFor1xLev.APY, financialMetrics.APY)
return (
<button
className={`inner-shadow-bottom default-transition w-full rounded-xl border border-th-bkg-3 bg-th-bkg-1 p-6 text-th-fgd-1 focus:outline-none focus-visible:border-th-fgd-4 md:hover:bg-th-bkg-2 md:hover:focus-visible:border-th-fgd-4`}
onClick={onClick}
>
<div>
<div className="flex flex-col items-center">
<div
className={`inner-shadow-bottom-sm mb-2 flex h-14 w-14 items-center justify-center rounded-full border border-th-bkg-2 bg-gradient-to-b from-th-bkg-1 to-th-bkg-2`}
>
<Image
src={`/icons/${tokenName.toLowerCase()}.svg`}
width={32}
height={32}
alt="Select a token"
/>
</div>
<div className="flex flex-col items-center">
<p className={`text-th-fgd-1`}>{formatTokenSymbol(tokenName)}</p>
<span className={`text-xl font-bold`}>
{!groupLoaded ? (
<SheenLoader>
<div className={`h-6 w-10 bg-th-bkg-2`} />
</SheenLoader>
) : !UiRate || isNaN(UiRate) ? (
<span className="text-base font-normal text-th-fgd-4">
Rate Unavailable
</span>
) : (
`${UiRate.toFixed(2)}%`
)}
</span>
{groupLoaded ? (
<span className="text-sm text-th-fgd-4">
{tokenName === 'USDC' ? 'APY' : 'Max APY'}
</span>
) : null}
</div>
</div>
</div>
</button>
)
}
export default HeroTokenButton

View File

@ -17,7 +17,7 @@ const HydrateStore = () => {
const { mangoAccountPk } = useMangoAccount()
const selectedToken = mangoStore((s) => s.selectedToken)
const clientContext =
getStakableTokensDataForTokenName(selectedToken).clientContext
getStakableTokensDataForTokenName(selectedToken)?.clientContext
const connection = mangoStore((s) => s.connection)

View File

@ -18,6 +18,7 @@ import usePositions from 'hooks/usePositions'
import { AdjustmentsHorizontalIcon } from '@heroicons/react/20/solid'
import EditLeverageModal from './modals/EditLeverageModal'
import Tooltip from './shared/Tooltip'
import { useWallet } from '@solana/wallet-adapter-react'
const set = mangoStore.getState().set
@ -91,6 +92,7 @@ const PositionItem = ({
setActiveTab: (v: ActiveTab) => void
borrowBank: Bank | undefined
}) => {
const { connected } = useWallet()
const { jlpGroup, lstGroup } = useMangoGroup()
const { stakeBalance, bank, pnl, acct } = position
@ -286,20 +288,24 @@ const PositionItem = ({
<p className="mb-1 text-th-fgd-4">Leverage</p>
<div className="flex items-center">
<span className="mr-3 text-xl font-bold text-th-fgd-1">
{leverage ? leverage.toFixed(2) : 0.0}x
{connected && stakeBalance && leverage
? `${leverage.toFixed(2)}x`
: ''}
</span>
<button
onClick={async () => {
await set((state) => {
state.selectedToken = bank.name
})
setShowEditLeverageModal(!showEditLeverageModal)
}}
className="default-transition flex items-center rounded-md border-b-2 border-th-bkg-4 bg-th-bkg-2 px-2.5 py-1 text-th-fgd-1 md:hover:bg-th-bkg-3"
>
<AdjustmentsHorizontalIcon className="mr-1.5 h-4 w-4" />
<span className="font-bold">Edit</span>
</button>
{connected && stakeBalance ? (
<button
onClick={async () => {
await set((state) => {
state.selectedToken = bank.name
})
setShowEditLeverageModal(!showEditLeverageModal)
}}
className="default-transition flex items-center rounded-md border-b-2 border-th-bkg-4 bg-th-bkg-2 px-2.5 py-1 text-th-fgd-1 md:hover:bg-th-bkg-3"
>
<AdjustmentsHorizontalIcon className="mr-1.5 h-4 w-4" />
<span className="font-bold">Edit</span>
</button>
) : null}
</div>
</div>
<div>

View File

@ -17,6 +17,7 @@ import TokenSelect from './TokenSelect'
import Label from './forms/Label'
import usePositions from 'hooks/usePositions'
import { IconButton } from './shared/Button'
import HeroTokenButton from './HeroTokenButton'
const set = mangoStore.getState().set
@ -48,8 +49,13 @@ const Stake = () => {
state.selectedToken = positions[0].bank.name
})
}
if (tab === 'Add' && selectedToken) {
set((state) => {
state.selectedToken = ''
})
}
},
[hasPosition, positions],
[hasPosition, positions, selectedToken],
)
const selectableTokens = useMemo(() => {
@ -143,58 +149,79 @@ const Stake = () => {
/>
</div>
{selectableTokens.length ? (
<>
<div className="pb-6">
<Label text="Token" />
<TokenButton
onClick={() => setShowTokenSelect(true)}
tokenName={selectedToken}
/>
</div>
{selectedToken == 'USDC' ? (
<>
{activeFormTab === 'Add' ? (
<DespositForm
token="USDC"
clientContext={
getStakableTokensDataForTokenName('USDC')
.clientContext
!selectedToken ? (
<>
<h2 className="mb-3 text-center text-lg font-normal">
Select token to Boost!
</h2>
<div className="grid grid-cols-2 gap-4">
{selectableTokens.map((token) => (
<HeroTokenButton
key={token}
onClick={() =>
set((state) => {
state.selectedToken = token
})
}
tokenName={token}
/>
) : null}
{activeFormTab === 'Remove' ? (
<UnstakeForm
token="USDC"
clientContext={
getStakableTokensDataForTokenName('USDC')
.clientContext
}
/>
) : null}
</>
) : (
<>
{activeFormTab === 'Add' ? (
<StakeForm
token={selectedToken}
clientContext={
getStakableTokensDataForTokenName(selectedToken)
.clientContext
}
/>
) : null}
{activeFormTab === 'Remove' ? (
<UnstakeForm
token={selectedToken}
clientContext={
getStakableTokensDataForTokenName(selectedToken)
.clientContext
}
/>
) : null}
</>
)}
</>
))}
</div>
</>
) : (
<>
<div className="pb-6">
<Label text="Token" />
<TokenButton
onClick={() => setShowTokenSelect(true)}
tokenName={selectedToken}
/>
</div>
{selectedToken == 'USDC' ? (
<>
{activeFormTab === 'Add' ? (
<DespositForm
token="USDC"
clientContext={
getStakableTokensDataForTokenName('USDC')
.clientContext
}
/>
) : null}
{activeFormTab === 'Remove' ? (
<UnstakeForm
token="USDC"
clientContext={
getStakableTokensDataForTokenName('USDC')
.clientContext
}
/>
) : null}
</>
) : (
<>
{activeFormTab === 'Add' ? (
<StakeForm
token={selectedToken}
clientContext={
getStakableTokensDataForTokenName(selectedToken)
?.clientContext
}
/>
) : null}
{activeFormTab === 'Remove' ? (
<UnstakeForm
token={selectedToken}
clientContext={
getStakableTokensDataForTokenName(selectedToken)
?.clientContext
}
/>
) : null}
</>
)}
</>
)
) : (
<div className="p-10">
<p className="text-center text-th-fgd-4">
@ -205,7 +232,7 @@ const Stake = () => {
</div>
</div>
</div>
{activeFormTab === 'Add' ? (
{activeFormTab === 'Add' && selectedToken ? (
<div className="fixed bottom-0 left-0 z-20 w-full lg:bottom-8 lg:left-8 lg:w-auto">
{isDesktop ? (
<a

View File

@ -35,7 +35,7 @@ const TokenButton = ({
return (
<button
className={`inner-shadow-bottom-sm w-full rounded-xl border border-th-bkg-3 bg-th-bkg-1 p-3 text-th-fgd-1 focus:outline-none focus-visible:border-th-fgd-4 md:hover:border-th-bkg-4 md:hover:focus-visible:border-th-fgd-4`}
className={`inner-shadow-bottom-sm default-transition w-full rounded-xl border border-th-bkg-3 bg-th-bkg-1 p-3 text-th-fgd-1 focus:outline-none focus-visible:border-th-fgd-4 md:hover:bg-th-bkg-2 md:hover:focus-visible:border-th-fgd-4`}
onClick={onClick}
>
<div className="flex items-center justify-between">
@ -51,20 +51,30 @@ const TokenButton = ({
/>
</div>
<div className="text-left">
<p className={`text-lg font-bold text-th-fgd-1`}>
<p className={`mb-0.5 text-th-fgd-1`}>
{formatTokenSymbol(tokenName)}
</p>
<span className={`font-medium text-th-fgd-4`}>
<span className={`text-lg font-bold leading-none text-th-fgd-1`}>
{!groupLoaded ? (
<SheenLoader>
<div className={`h-5 w-10 bg-th-bkg-2`} />
</SheenLoader>
) : !UiRate || isNaN(UiRate) ? (
'Rate Unavailable'
<span className="text-base font-normal text-th-fgd-4">
Rate Unavailable
</span>
) : tokenName === 'USDC' ? (
`${UiRate.toFixed(2)}% APY`
<>
{`${UiRate.toFixed(2)}%`}{' '}
<span className="text-sm font-normal text-th-fgd-4">APY</span>
</>
) : (
`Up to ${UiRate.toFixed(2)}% APY`
<>
{`${UiRate.toFixed(2)}%`}{' '}
<span className="text-sm font-normal text-th-fgd-4">
Max APY
</span>
</>
)}
</span>
</div>

View File

@ -70,10 +70,12 @@ const TokenSelect = ({
/>
</div>
<div className="text-left">
<p className={`text-sm font-bold text-th-fgd-1 sm:text-lg`}>
<p className={`text-sm text-th-fgd-1 lg:text-base`}>
{formatTokenSymbol(tokenName)}
</p>
<span className={`text-sm text-th-fgd-4`}>
<span
className={`text-sm font-bold leading-none text-th-fgd-1 sm:text-lg`}
>
{!groupLoaded ? (
<SheenLoader>
<div className={`h-5 w-10 bg-th-bkg-2`} />
@ -81,9 +83,17 @@ const TokenSelect = ({
) : !UiRate || isNaN(UiRate) ? (
'Rate Unavailable'
) : tokenName === 'USDC' ? (
`${UiRate.toFixed(2)}% APY`
<>
{`${UiRate.toFixed(2)}%`}{' '}
<span className="text-sm font-normal text-th-fgd-4">APY</span>
</>
) : (
`Up to ${UiRate.toFixed(2)}% APY`
<>
{`${UiRate.toFixed(2)}%`}{' '}
<span className="text-sm font-normal text-th-fgd-4">
Max APY
</span>
</>
)}
</span>
</div>

View File

@ -23,7 +23,7 @@ const TermsOfUseModal = ({ isOpen, onClose }: ModalProps) => {
<div className="mb-4 border-b border-th-bkg-3 pb-4">
<div className="group mb-2 flex items-center justify-center">
<BoostLogo className="h-auto w-12 shrink-0 cursor-pointer group-hover:animate-shake" />
<span className="text-shadow ml-2 hidden text-[32px] font-black text-th-bkg-1 md:block">
<span className="text-shadow ml-2 block text-[32px] font-black text-th-bkg-1">
Boost!
</span>
<div className="ml-2.5 hidden rounded border border-th-fgd-1 bg-th-active px-1.5 py-1 md:block">
@ -38,15 +38,15 @@ const TermsOfUseModal = ({ isOpen, onClose }: ModalProps) => {
</div>
<ul className="space-y-2 border-b border-th-bkg-3 pb-4">
<li className="flex items-center">
<CheckCircleIcon className="mr-2 h-5 w-5 text-th-success" />
<CheckCircleIcon className="mr-2 h-5 w-5 shrink-0 text-th-success" />
<span>Easily add leverage to boost your yield.</span>
</li>
<li className="flex items-center">
<CheckCircleIcon className="mr-2 h-5 w-5 text-th-success" />
<CheckCircleIcon className="mr-2 h-5 w-5 shrink-0 text-th-success" />
<span>No lockup. Remove your assets when you want.</span>
</li>
<li className="flex items-center">
<CheckCircleIcon className="mr-2 h-5 w-5 text-th-success" />
<CheckCircleIcon className="mr-2 h-5 w-5 shrink-0 text-th-success" />
<span>
Powered by{' '}
<a

View File

@ -43,7 +43,6 @@ import {
MAX_PRIORITY_FEE_KEYS,
PAGINATION_PAGE_LENGTH,
RPC_PROVIDER_KEY,
STAKEABLE_TOKENS,
SWAP_MARGIN_KEY,
} from '../utils/constants'
import {
@ -332,7 +331,7 @@ const mangoStore = create<MangoStore>()(
return {
// leverage stake
activeTab: 'Boost!',
selectedToken: STAKEABLE_TOKENS[0],
selectedToken: '',
estimatedMaxAPY: {
current: 0,
},

View File

@ -386,27 +386,27 @@ table p {
/* shadows */
.inner-shadow-top {
@apply shadow-[inset_0_4px_0px_rgba(0,0,0,0.15)];
@apply shadow-[inset_0_4px_0px_rgba(0,0,0,0.2)];
}
.inner-shadow-bottom {
@apply shadow-[inset_0_-4px_0px_rgba(0,0,0,0.15)];
@apply shadow-[inset_0_-4px_0px_rgba(0,0,0,0.2)];
}
.inner-shadow-top-sm {
@apply shadow-[inset_0_2px_0px_rgba(0,0,0,0.15)];
@apply shadow-[inset_0_2px_0px_rgba(0,0,0,0.2)];
}
.inner-shadow-bottom-sm {
@apply shadow-[inset_0_-2px_0px_rgba(0,0,0,0.15)];
@apply shadow-[inset_0_-2px_0px_rgba(0,0,0,0.2)];
}
.inner-shadow-top-xs {
@apply shadow-[inset_0_1px_0px_rgba(0,0,0,0.15)];
@apply shadow-[inset_0_1px_0px_rgba(0,0,0,0.2)];
}
.inner-shadow-bottom-xs {
@apply shadow-[inset_0_-1px_0px_rgba(0,0,0,0.15)];
@apply shadow-[inset_0_-1px_0px_rgba(0,0,0,0.2)];
}
.text-shadow {
@ -457,4 +457,4 @@ table p {
.rotate-bg-5x {
animation: rotate-bg 3s linear infinite;
}
}