Skip to content

Commit

Permalink
improve add boost ux
Browse files Browse the repository at this point in the history
  • Loading branch information
saml33 committed Jun 24, 2024
1 parent 5deb064 commit 88d016a
Show file tree
Hide file tree
Showing 11 changed files with 732 additions and 365 deletions.
145 changes: 72 additions & 73 deletions components/HeroTokenButton.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
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'
import { SOL_YIELD } from './Stake'
import Tooltip from './shared/Tooltip'
import Link from 'next/link'
import { StakeableToken } from 'hooks/useStakeableTokens'

export const HERO_TOKEN_BUTTON_CLASSES =
'inner-shadow-bottom default-transition relative w-full rounded-xl border border-th-bkg-3 bg-th-bkg-1 px-6 py-4 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'

export const HERO_TOKEN_IMAGE_WRAPPER_CLASSES =
'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 shrink-0'

const HeroTokenButton = ({
onClick,
tokenName,
tokenInfo,
}: {
tokenName: string
tokenInfo: StakeableToken
onClick: () => void
}) => {
const leverage = useLeverageMax(tokenName)
const { symbol, name } = tokenInfo.token
const { APY } = tokenInfo.financialMetrics
// const leverage = useLeverageMax(symbol)
const groupLoaded = mangoStore((s) => s.groupLoaded)

const { stakeBankDepositRate, financialMetrics } = useBankRates(
tokenName,
leverage,
)
// const { stakeBankDepositRate, financialMetrics } = useBankRates(
// symbol,
// leverage,
// )

const { financialMetrics: estimatedNetAPYFor1xLev } = useBankRates(
tokenName,
1,
)
// const { financialMetrics: estimatedNetAPYFor1xLev } = useBankRates(symbol, 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)
// const APY_Daily_Compound =
// Math.pow(1 + Number(stakeBankDepositRate) / 365, 365) - 1
// const UiRate =
// symbol === 'USDC'
// ? APY_Daily_Compound * 100
// : Math.max(estimatedNetAPYFor1xLev.APY, financialMetrics.APY)

const renderRateEmoji = (token: string, rate: number) => {
if (token.toLowerCase().includes('sol')) {
Expand All @@ -53,41 +56,64 @@ const HeroTokenButton = ({
}
}

const emoji = renderRateEmoji(tokenName, UiRate)
const emoji = renderRateEmoji(symbol, APY)

return (
<button
className={`inner-shadow-bottom default-transition relative 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}
>
<button className={HERO_TOKEN_BUTTON_CLASSES} 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`}
>
<div className="flex items-center space-x-2.5">
<div className={HERO_TOKEN_IMAGE_WRAPPER_CLASSES}>
<Image
src={`/icons/${tokenName.toLowerCase()}.svg`}
src={`/icons/${symbol.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-2xl 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
<div className="flex w-full justify-between">
<div className="text-left">
<span className="text-xl font-bold">
{formatTokenSymbol(symbol)}
</span>
<p className={`text-xs text-th-fgd-4`}>{name}</p>
</div>
<div className="text-right">
<p className={`text-xs text-th-fgd-4`}>Max APY</p>
<div className="flex items-center">
{emoji ? (
<Tooltip
content={
<>
<p className="mb-2">
The max APY is favorable right now. Rates can change
very quickly. Make sure you understand the risks
before boosting.
</p>
<Link href="/risks" shallow>
Risks
</Link>
</>
}
>
<span className="mr-2 text-lg">{emoji}</span>
</Tooltip>
) : null}
<span className={`text-xl font-bold`}>
{!groupLoaded ? (
<SheenLoader>
<div className={`h-6 w-10 bg-th-bkg-2`} />
</SheenLoader>
) : !APY || isNaN(APY) ? (
<span className="text-base font-normal text-th-fgd-4">
Rate Unavailable
</span>
) : (
`${APY.toFixed(2)}%`
)}
</span>
) : (
`${UiRate.toFixed(2)}%`
)}
</span>
{groupLoaded ? (
</div>
</div>
{/* {groupLoaded ? (
<div className="mt-1 flex items-center">
{SOL_YIELD.includes(tokenName) ? (
<>
Expand All @@ -113,37 +139,10 @@ const HeroTokenButton = ({
</>
)}
</div>
) : null}
) : null} */}
</div>
</div>
</div>
{emoji ? (
<div
className="absolute left-0 top-0 h-0 w-0 rounded-tl-xl"
style={{
borderTopWidth: '100px',
borderRightWidth: '100px',
borderTopColor: 'var(--bkg-2)',
borderRightColor: 'transparent',
}}
>
<Tooltip
content={
<>
<p className="mb-2">
The max APY is favorable right now. Rates can change very
quickly. Make sure you understand the risks before boosting.
</p>
<Link href="/risks" shallow>
Risks
</Link>
</>
}
>
<span className="absolute bottom-12 left-4 text-2xl">{emoji}</span>
</Tooltip>
</div>
) : null}
</button>
)
}
Expand Down
104 changes: 92 additions & 12 deletions components/Positions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import useMangoGroup from 'hooks/useMangoGroup'
import { useMemo, useState } from 'react'
import { SHOW_INACTIVE_POSITIONS_KEY } from 'utils/constants'
import TokenLogo from './shared/TokenLogo'
import Button from './shared/Button'
import { formatTokenSymbol } from 'utils/tokens'
import Button, { IconButton } from './shared/Button'
import {
formatTokenSymbol,
getStakableTokensDataForTokenName,
} from 'utils/tokens'
import mangoStore, { ActiveTab } from '@store/mangoStore'
import Switch from './forms/Switch'
import useLocalStorageState from 'hooks/useLocalStorageState'
Expand All @@ -15,10 +18,16 @@ import {
} from '@blockworks-foundation/mango-v4'
import useBankRates from 'hooks/useBankRates'
import usePositions from 'hooks/usePositions'
import { AdjustmentsHorizontalIcon } from '@heroicons/react/20/solid'
import {
AdjustmentsHorizontalIcon,
ArrowLeftIcon,
} from '@heroicons/react/20/solid'
import EditLeverageModal from './modals/EditLeverageModal'
import Tooltip from './shared/Tooltip'
import { useWallet } from '@solana/wallet-adapter-react'
import UnstakeForm from './UnstakeForm'
import StakeForm from './StakeForm'
import DespositForm from './DepositForm'

const set = mangoStore.getState().set

Expand All @@ -35,18 +44,20 @@ const Positions = ({
}: {
setActiveTab: (tab: ActiveTab) => void
}) => {
const selectedToken = mangoStore((s) => s.selectedToken)
const [showInactivePositions, setShowInactivePositions] =
useLocalStorageState(SHOW_INACTIVE_POSITIONS_KEY, true)
useLocalStorageState(SHOW_INACTIVE_POSITIONS_KEY, false)
const { positions, jlpBorrowBank, lstBorrowBank } = usePositions(
showInactivePositions,
)
const [showAddRemove, setShowAddRemove] = useState('')

const numberOfPositions = useMemo(() => {
if (!positions.length) return 0
return positions.filter((pos) => pos.stakeBalance > 0).length
}, [positions])

return (
return !showAddRemove ? (
<>
<div className="mb-2 flex items-center justify-between rounded-lg border-2 border-th-fgd-1 bg-th-bkg-1 px-6 py-3.5">
<p className="font-medium">{`You have ${numberOfPositions} active position${
Expand All @@ -69,6 +80,7 @@ const Positions = ({
key={bank.name}
position={position}
setActiveTab={setActiveTab}
setShowAddRemove={setShowAddRemove}
borrowBank={isUsdcBorrow ? jlpBorrowBank : lstBorrowBank}
/>
) : null
Expand All @@ -80,29 +92,84 @@ const Positions = ({
)}
</div>
</>
) : (
<div
className={`rounded-2xl border-2 border-th-fgd-1 bg-th-bkg-1 p-6 text-th-fgd-1 md:p-8`}
>
<div className="mb-3 flex items-center space-x-3">
<IconButton
onClick={() => setShowAddRemove('')}
size="medium"
isPrimary
>
<ArrowLeftIcon className="h-5 w-5" />
</IconButton>
<h2>
{showAddRemove === 'add' ? 'Add' : 'Withdraw'} {selectedToken}
</h2>
</div>
{showAddRemove === 'add' ? (
selectedToken === 'USDC' ? (
<DespositForm
token="USDC"
clientContext={
getStakableTokensDataForTokenName('USDC').clientContext
}
/>
) : (
<StakeForm
token={selectedToken}
clientContext={
getStakableTokensDataForTokenName(selectedToken)?.clientContext
}
/>
)
) : (
<UnstakeForm
token={selectedToken}
clientContext={
getStakableTokensDataForTokenName(selectedToken)?.clientContext
}
/>
)}
</div>
)
}

const PositionItem = ({
position,
setActiveTab,
setShowAddRemove,
borrowBank,
}: {
position: Position
setActiveTab: (v: ActiveTab) => void
setShowAddRemove: (v: 'add' | 'remove') => void
borrowBank: Bank | undefined
}) => {
const { connected } = useWallet()
const { jlpGroup, lstGroup } = useMangoGroup()
const { stakeBalance, bank, pnl, acct } = position
const [showEditLeverageModal, setShowEditLeverageModal] = useState(false)

const handleAddOrManagePosition = (token: string) => {
const handleAddNoPosition = (token: string) => {
setActiveTab('Boost!')
set((state) => {
state.selectedToken = token
})
}
const [showEditLeverageModal, setShowEditLeverageModal] = useState(false)
const handleAddPosition = (token: string) => {
setShowAddRemove('add')
set((state) => {
state.selectedToken = token
})
}
const handleRemovePosition = (token: string) => {
setShowAddRemove('remove')
set((state) => {
state.selectedToken = token
})
}

const leverage = useMemo(() => {
if (!acct || !bank) return 1
Expand Down Expand Up @@ -162,11 +229,24 @@ const PositionItem = ({
<p>${bank.uiPrice.toFixed(2)}</p>
</div>
</div>
<Button onClick={() => handleAddOrManagePosition(bank.name)}>
<p className="mb-1 text-base tracking-wider text-th-bkg-1">
{stakeBalance ? 'Add/Remove' : `Boost! ${bank.name}`}
</p>
</Button>
{stakeBalance ? (
<div className="flex space-x-2">
<Button onClick={() => handleAddPosition(bank.name)}>
<p className="mb-1 text-base tracking-wider text-th-bkg-1">Add</p>
</Button>
<Button onClick={() => handleRemovePosition(bank.name)}>
<p className="mb-1 text-base tracking-wider text-th-bkg-1">
Withdraw
</p>
</Button>
</div>
) : (
<Button onClick={() => handleAddNoPosition(bank.name)}>
<p className="mb-1 text-base tracking-wider text-th-bkg-1">
{`Boost! ${bank.name}`}
</p>
</Button>
)}
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
Expand Down
Loading

0 comments on commit 88d016a

Please sign in to comment.