Compare commits
4 Commits
c73b5346a3
...
d519685520
Author | SHA1 | Date |
---|---|---|
saml33 | d519685520 | |
saml33 | b53facc749 | |
saml33 | 9d44e47827 | |
saml33 | b1dd5fc7f4 |
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue