Mp 2540 farm overview (#172)
* tidy: refactor text
* tidy: refactor text
* fix display of vaults table
* feat: added unstyled select
* tidy: useToggle
* tidy: useToggle
* add vaults to types
* MP-2344: first unstyled version of Select
* fix: fixed the build
* MP-2344: progress on the Select
* MP-2344: almost finished the Select
* implement basic vault modal (no logic)
* 🍱 max + displaycur for token input
* Convert to BN for TokenInputs
* env: update wallet-connector
* fix: fixed build errors and relative imports
* fix: updated TokenInput
* tidy: format
* fix: BN instead of new BigNumber
---------
Co-authored-by: Linkie Link <linkielink.dev@gmail.com>
This commit is contained in:
parent
ac09862f1f
commit
cd5ec3ee3b
14
package.json
14
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mars-v2-frontend",
|
||||
"version": "0.1.0",
|
||||
"version": "2.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
@ -13,13 +13,13 @@
|
||||
"dependencies": {
|
||||
"@cosmjs/cosmwasm-stargate": "^0.30.1",
|
||||
"@cosmjs/stargate": "^0.30.1",
|
||||
"@marsprotocol/wallet-connector": "^1.5.3",
|
||||
"@sentry/nextjs": "^7.47.0",
|
||||
"@marsprotocol/wallet-connector": "^1.5.4",
|
||||
"@sentry/nextjs": "^7.48.0",
|
||||
"@tanstack/react-table": "^8.8.5",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"bignumber.js": "^9.1.1",
|
||||
"classnames": "^2.3.2",
|
||||
"graphql-request": "^5.2.0",
|
||||
"graphql-request": "^6.0.0",
|
||||
"moment": "^2.29.4",
|
||||
"next": "^13.3.0",
|
||||
"react": "^18.2.0",
|
||||
@ -30,14 +30,14 @@
|
||||
"react-toastify": "^9.1.2",
|
||||
"react-use-clipboard": "^1.0.9",
|
||||
"recharts": "^2.5.0",
|
||||
"swr": "^2.1.2",
|
||||
"swr": "^2.1.3",
|
||||
"tailwind-scrollbar-hide": "^1.1.7",
|
||||
"zustand": "^4.3.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/webpack": "^7.0.0",
|
||||
"@types/node": "^18.15.11",
|
||||
"@types/react": "18.0.33",
|
||||
"@types/react": "18.0.35",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"eslint": "8.38.0",
|
||||
@ -45,7 +45,7 @@
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "^2.8.7",
|
||||
"prettier-plugin-tailwindcss": "^0.2.6",
|
||||
"prettier-plugin-tailwindcss": "^0.2.7",
|
||||
"tailwindcss": "^3.3.1",
|
||||
"typescript": "5.0.4"
|
||||
},
|
||||
|
@ -4,6 +4,7 @@ import { headers } from 'next/headers'
|
||||
import AccountDetails from 'components/Account/AccountDetails'
|
||||
import Background from 'components/Background'
|
||||
import FetchPrices from 'components/FetchPrices'
|
||||
import Footer from 'components/Footer'
|
||||
import DesktopHeader from 'components/Header/DesktopHeader'
|
||||
import { Modals } from 'components/Modals'
|
||||
import Toaster from 'components/Toaster'
|
||||
@ -23,13 +24,14 @@ export default function RootLayout(props: { children: React.ReactNode }) {
|
||||
<FetchPrices />
|
||||
<main
|
||||
className={classNames(
|
||||
'relative flex justify-center py-6',
|
||||
'lg:mt-[65px] lg:h-[calc(100vh-65px)]',
|
||||
'relative flex justify-center pt-6',
|
||||
'lg:mt-[65px] lg:h-[calc(100vh-89px)]',
|
||||
)}
|
||||
>
|
||||
<div className='flex max-w-content flex-grow flex-col flex-wrap'>{props.children}</div>
|
||||
<AccountDetails />
|
||||
</main>
|
||||
<Footer />
|
||||
<Modals />
|
||||
<Toaster />
|
||||
</body>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Card from 'components/Card'
|
||||
import { Plus, Subtract } from 'components/Icons'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props {
|
||||
items: Item[]
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import { Gauge } from 'components/Gauge'
|
||||
import { Heart } from 'components/Icons'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import { isNumber } from 'utils/parsers'
|
||||
import useParams from 'utils/route'
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import AccountStats from 'components/Account/AccountStats'
|
||||
import { Button } from 'components/Button'
|
||||
@ -10,13 +10,12 @@ import Card from 'components/Card'
|
||||
import { ArrowCircledTopRight, ArrowDownLine, ArrowUpLine, TrashBin } from 'components/Icons'
|
||||
import Radio from 'components/Radio'
|
||||
import SwitchWithLabel from 'components/SwitchWithLabel'
|
||||
import { Text } from 'components/Text'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import useParams, { getRoute } from 'utils/route'
|
||||
import Text from 'components/Text'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import useStore from 'store'
|
||||
import { calculateAccountBalance } from 'utils/accounts'
|
||||
import { hardcodedFee } from 'utils/contants'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
import useParams, { getRoute } from 'utils/route'
|
||||
|
||||
interface Props {
|
||||
setShowFundAccount: (showFundAccount: boolean) => void
|
||||
@ -37,7 +36,7 @@ export default function AccountList(props: Props) {
|
||||
|
||||
const deleteAccount = useStore((s) => s.deleteAccount)
|
||||
|
||||
const [isLending, setIsLending] = useState(false)
|
||||
const [isLending, setIsLending] = useToggle()
|
||||
const accountSelected = !!selectedAccount && !isNaN(Number(selectedAccount))
|
||||
const selectedAccountDetails = props.accounts.find((account) => account.id === selectedAccount)
|
||||
const selectedAccountBalance = selectedAccountDetails
|
||||
@ -52,8 +51,8 @@ export default function AccountList(props: Props) {
|
||||
}
|
||||
}
|
||||
|
||||
function onChangeLendSwitch(isLending: boolean) {
|
||||
setIsLending(isLending)
|
||||
function onChangeLendSwitch() {
|
||||
setIsLending(!isLending)
|
||||
/* TODO: handle lending assets */
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import AccountList from 'components/Account/AccountList'
|
||||
import CreateAccount from 'components/Account/CreateAccount'
|
||||
@ -10,15 +10,15 @@ import FundAccount from 'components/Account/FundAccount'
|
||||
import { Button } from 'components/Button'
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import { Account, Plus, PlusCircled } from 'components/Icons'
|
||||
import { Overlay } from 'components/Overlay/Overlay'
|
||||
import { Text } from 'components/Text'
|
||||
import useParams from 'utils/route'
|
||||
import Overlay from 'components/Overlay/Overlay'
|
||||
import Text from 'components/Text'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import useStore from 'store'
|
||||
import { hardcodedFee } from 'utils/contants'
|
||||
import { isNumber } from 'utils/parsers'
|
||||
import useParams from 'utils/route'
|
||||
|
||||
const menuClasses =
|
||||
'absolute isolate flex w-full flex-wrap overflow-y-scroll scrollbar-hide scroll-smooth'
|
||||
const menuClasses = 'absolute isolate flex w-full flex-wrap scrollbar-hide'
|
||||
|
||||
interface Props {
|
||||
accounts: Account[]
|
||||
@ -28,8 +28,8 @@ export default function AccountMenuContent(props: Props) {
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const createAccount = useStore((s) => s.createAccount)
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
const [isCreating, setIsCreating] = useState(false)
|
||||
const [showMenu, setShowMenu] = useToggle()
|
||||
const [isCreating, setIsCreating] = useToggle()
|
||||
|
||||
const selectedAccountId = params.accountId
|
||||
const hasCreditAccounts = !!props.accounts.length
|
||||
@ -72,7 +72,10 @@ export default function AccountMenuContent(props: Props) {
|
||||
: 'Create Account'}
|
||||
</Button>
|
||||
<Overlay
|
||||
className='max-w-screen right-0 mt-2 flex h-[530px] w-[336px] overflow-hidden'
|
||||
className={classNames(
|
||||
'max-w-screen right-0 mt-2 flex h-[530px] w-[336px]',
|
||||
!showFundAccount && 'overflow-hidden',
|
||||
)}
|
||||
show={showMenu}
|
||||
setShow={setShowMenu}
|
||||
>
|
||||
@ -96,7 +99,13 @@ export default function AccountMenuContent(props: Props) {
|
||||
onClick={createAccountHandler}
|
||||
/>
|
||||
</div>
|
||||
<div className={classNames(menuClasses, 'top-[54px] h-[calc(100%-54px)] items-start')}>
|
||||
<div
|
||||
className={classNames(
|
||||
menuClasses,
|
||||
!showFundAccount && 'overflow-y-scroll scroll-smooth',
|
||||
'top-[54px] h-[calc(100%-54px)] items-start',
|
||||
)}
|
||||
>
|
||||
{isAccountSelected && isLoadingAccount && (
|
||||
<div className='flex h-full w-full items-center justify-center p-4'>
|
||||
<CircularProgress size={40} />
|
||||
@ -108,7 +117,13 @@ export default function AccountMenuContent(props: Props) {
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<div className={classNames(menuClasses, 'inset-0 h-full items-end bg-account')}>
|
||||
<div
|
||||
className={classNames(
|
||||
menuClasses,
|
||||
!showFundAccount && 'overflow-y-scroll scroll-smooth',
|
||||
'inset-0 h-full items-end bg-account',
|
||||
)}
|
||||
>
|
||||
{showCreateAccount ? (
|
||||
<CreateAccount createAccount={createAccountHandler} isCreating={isCreating} />
|
||||
) : showFundAccount ? (
|
||||
|
@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import { Heart, Shield } from 'components/Icons'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Accordion from 'components/Accordion'
|
||||
import Card from 'components/Card'
|
||||
import { ArrowChartLineUp, Shield } from 'components/Icons'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import useParams from 'utils/route'
|
||||
|
||||
export default function AccountSummary() {
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props {
|
||||
isCreating: boolean
|
||||
|
@ -1,17 +1,19 @@
|
||||
'use client'
|
||||
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useCallback, useState } from 'react'
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import { ArrowRight, Cross } from 'components/Icons'
|
||||
import SwitchWithLabel from 'components/SwitchWithLabel'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import TokenInputWithSlider from 'components/TokenInputWithSlider'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import useParams from 'utils/route'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import useStore from 'store'
|
||||
import { hardcodedFee } from 'utils/contants'
|
||||
import { BN } from 'utils/helpers'
|
||||
import useParams from 'utils/route'
|
||||
|
||||
interface Props {
|
||||
setShowFundAccount: (show: boolean) => void
|
||||
@ -22,27 +24,34 @@ export default function FundAccount(props: Props) {
|
||||
const params = useParams()
|
||||
const deposit = useStore((s) => s.deposit)
|
||||
|
||||
const [amount, setAmount] = useState(0)
|
||||
const [isLending, setIsLending] = useState(false)
|
||||
const [isFunding, setIsFunding] = useState(false)
|
||||
const [amount, setAmount] = useState(BN(0))
|
||||
const [asset, setAsset] = useState<Asset>(ASSETS[0])
|
||||
const [isLending, setIsLending] = useToggle()
|
||||
const [isFunding, setIsFunding] = useToggle()
|
||||
|
||||
const onChangeAmount = useCallback((amount: number) => {
|
||||
const onChangeAmount = useCallback((amount: BigNumber) => {
|
||||
setAmount(amount)
|
||||
}, [])
|
||||
|
||||
const handleLendAssets = useCallback((val: boolean) => {
|
||||
setIsLending(val)
|
||||
/* TODO: handle lending assets */
|
||||
const onChangeAsset = useCallback((asset: Asset) => {
|
||||
setAsset(asset)
|
||||
}, [])
|
||||
|
||||
const handleLendAssets = useCallback(
|
||||
(val: boolean) => {
|
||||
setIsLending(val)
|
||||
/* TODO: handle lending assets */
|
||||
},
|
||||
[setIsLending],
|
||||
)
|
||||
|
||||
async function onDeposit() {
|
||||
setIsFunding(true)
|
||||
// TODO: Make this dynamic (token select)
|
||||
const result = await deposit({
|
||||
fee: hardcodedFee,
|
||||
accountId: params.accountId,
|
||||
coin: {
|
||||
denom: ASSETS[0].denom,
|
||||
denom: asset.denom,
|
||||
amount: amount.toString(),
|
||||
},
|
||||
})
|
||||
@ -73,27 +82,28 @@ export default function FundAccount(props: Props) {
|
||||
your Osmosis address has no assets.
|
||||
</Text>
|
||||
<TokenInputWithSlider
|
||||
asset={ASSETS[0]}
|
||||
onChange={onChangeAmount}
|
||||
onChangeAsset={onChangeAsset}
|
||||
amount={amount}
|
||||
max={new BigNumber(1).shiftedBy(ASSETS[0].decimals).toNumber()}
|
||||
max={BN(1).shiftedBy(ASSETS[0].decimals)}
|
||||
className='mb-4'
|
||||
disabled={isFunding}
|
||||
hasSelect
|
||||
/>
|
||||
<div className='mb-4 w-full border-b border-white/10' />
|
||||
<SwitchWithLabel
|
||||
name='isLending'
|
||||
label='Lend assets to earn yield'
|
||||
value={isLending}
|
||||
onChange={handleLendAssets}
|
||||
onChange={() => handleLendAssets(!isLending)}
|
||||
className='mb-4'
|
||||
tooltip="Fund your account and lend assets effortlessly! By lending, you'll earn attractive interest (APY) without impacting your LTV. It's a win-win situation - don't miss out on this easy opportunity to grow your holdings!"
|
||||
disabled={isFunding || amount === 0}
|
||||
disabled={isFunding || amount.isEqualTo(0)}
|
||||
/>
|
||||
<Button
|
||||
className='w-full'
|
||||
showProgressIndicator={isFunding}
|
||||
disabled={amount === 0}
|
||||
disabled={amount.isEqualTo(0)}
|
||||
text='Fund Account'
|
||||
rightIcon={<ArrowRight />}
|
||||
onClick={onDeposit}
|
||||
|
@ -9,10 +9,10 @@ import {
|
||||
YAxis,
|
||||
} from 'recharts'
|
||||
|
||||
import { formatValue } from 'utils/formatters'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import useStore from 'store'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
|
||||
export const RiskChart = ({ data }: RiskChartProps) => {
|
||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||
|
@ -17,7 +17,7 @@ import AssetExpanded from 'components/Borrow/AssetExpanded'
|
||||
import { AssetRow } from 'components/Borrow/AssetRow'
|
||||
import { ChevronDown, SortAsc, SortDesc, SortNone } from 'components/Icons'
|
||||
import Loading from 'components/Loading'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import { getMarketAssets } from 'utils/assets'
|
||||
import { formatPercent } from 'utils/formatters'
|
||||
|
@ -2,7 +2,7 @@ import classNames from 'classnames'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import useStore from 'store'
|
||||
|
||||
@ -65,7 +65,7 @@ export const BorrowCapacity = ({
|
||||
className={classNames(
|
||||
enableAnimations && 'duration-800 transition-[opcity] delay-[1600ms]',
|
||||
'text-3xs-caps',
|
||||
limitPercentOfMax ? 'opacity-60' : 'opacity-0',
|
||||
limitPercentOfMax ? 'opacity-50' : 'opacity-0',
|
||||
)}
|
||||
>
|
||||
<FormattedNumber animate amount={limit} />
|
||||
@ -137,7 +137,7 @@ export const BorrowCapacity = ({
|
||||
</div>
|
||||
</Tooltip>
|
||||
{!hideValues && (
|
||||
<div className='mt-2 flex opacity-60 text-3xs-caps'>
|
||||
<div className='mt-2 flex opacity-50 text-3xs-caps'>
|
||||
<FormattedNumber animate amount={balance} className='mr-1' />
|
||||
<span className='mr-1'>of</span>
|
||||
<FormattedNumber animate amount={max} />
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Image from 'next/image'
|
||||
import { useState } from 'react'
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import AccountSummary from 'components/Account/AccountSummary'
|
||||
import { Button } from 'components/Button'
|
||||
@ -7,18 +8,19 @@ import Card from 'components/Card'
|
||||
import Divider from 'components/Divider'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import { Modal } from 'components/Modal'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import TokenInputWithSlider from 'components/TokenInputWithSlider'
|
||||
import useStore from 'store'
|
||||
import { hardcodedFee } from 'utils/contants'
|
||||
import { formatPercent, formatValue } from 'utils/formatters'
|
||||
import TokenInputWithSlider from 'components/TokenInputWithSlider'
|
||||
import useParams from 'utils/route'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default function BorrowModal() {
|
||||
const params = useParams()
|
||||
const [percentage, setPercentage] = useState(0)
|
||||
const [amount, setAmount] = useState(0)
|
||||
const [amount, setAmount] = useState(BN(0))
|
||||
const [selectedAccount, setSelectedAccount] = useState(params.accountId)
|
||||
const modal = useStore((s) => s.borrowModal)
|
||||
const borrow = useStore((s) => s.borrow)
|
||||
@ -33,7 +35,7 @@ export default function BorrowModal() {
|
||||
|
||||
function setOpen(isOpen: boolean) {
|
||||
useStore.setState({ borrowModal: null })
|
||||
setAmount(0)
|
||||
setAmount(BN(0))
|
||||
setPercentage(0)
|
||||
}
|
||||
|
||||
@ -75,7 +77,7 @@ export default function BorrowModal() {
|
||||
if ((modal.marketData as BorrowAssetActive)?.debt)
|
||||
debtAmount = Number((modal.marketData as BorrowAssetActive).debt)
|
||||
|
||||
const maxAmount = modal.isRepay ? debtAmount : liquidityAmount
|
||||
const max = BN(modal.isRepay ? debtAmount : liquidityAmount)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -115,9 +117,12 @@ export default function BorrowModal() {
|
||||
>
|
||||
<TokenInputWithSlider
|
||||
asset={modal.asset}
|
||||
onChange={setAmount}
|
||||
onChange={(val) => {
|
||||
console.log('new value received', val)
|
||||
setAmount(val)
|
||||
}}
|
||||
amount={amount}
|
||||
max={maxAmount}
|
||||
max={max}
|
||||
/>
|
||||
<Divider />
|
||||
<Text size='lg'>{modal.isRepay ? 'Repay for' : 'Borrow to'}</Text>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
import { ReactElement, ReactNode } from 'react'
|
||||
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props {
|
||||
children: ReactNode
|
||||
|
@ -1,6 +1,6 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
|
@ -2,7 +2,7 @@ import { Suspense } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import Loading from 'components/Loading'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
|
||||
async function Content(props: PageProps) {
|
||||
const address = props.params.address
|
||||
|
@ -3,14 +3,12 @@ import BigNumber from 'bignumber.js'
|
||||
|
||||
import useStore from 'store'
|
||||
import { getMarketAssets } from 'utils/assets'
|
||||
|
||||
import { FormattedNumber } from './FormattedNumber'
|
||||
import { BN } from 'utils/helpers'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
|
||||
interface Props {
|
||||
coin: Coin
|
||||
className?: string
|
||||
prefixClassName?: string
|
||||
valueClassName?: string
|
||||
isApproximation?: boolean
|
||||
}
|
||||
|
||||
@ -26,7 +24,7 @@ export default function DisplayCurrency(props: Props) {
|
||||
|
||||
if (!price || !asset || !displayPrice) return '0'
|
||||
|
||||
return new BigNumber(coin.amount)
|
||||
return BN(coin.amount)
|
||||
.times(price.amount)
|
||||
.div(displayPrice.amount)
|
||||
.integerValue(BigNumber.ROUND_HALF_DOWN)
|
||||
@ -35,13 +33,14 @@ export default function DisplayCurrency(props: Props) {
|
||||
|
||||
return (
|
||||
<FormattedNumber
|
||||
className={props.className}
|
||||
amount={convertToDisplayAmount(props.coin)}
|
||||
options={{
|
||||
minDecimals: 0,
|
||||
maxDecimals: 2,
|
||||
abbreviated: true,
|
||||
decimals: displayCurrency.decimals,
|
||||
prefix: `${props.isApproximation ? '~' : ''}${
|
||||
prefix: `${props.isApproximation ? '~ ' : ''}${
|
||||
displayCurrency.prefix ? displayCurrency.prefix : ''
|
||||
}`,
|
||||
suffix: displayCurrency.symbol ? ` ${displayCurrency.symbol}` : '',
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { Suspense } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import { getVaults } from 'utils/api'
|
||||
import { Text } from 'components/Text'
|
||||
import { VaultTable } from 'components/Earn/vault/VaultTable'
|
||||
import Text from 'components/Text'
|
||||
import { VAULTS } from 'constants/vaults'
|
||||
|
||||
import { VaultTable } from './VaultTable'
|
||||
import { getVaults } from 'utils/api'
|
||||
|
||||
async function Content() {
|
||||
const vaults = await getVaults()
|
@ -1,10 +1,8 @@
|
||||
import { Suspense } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import VaultCard from 'components/Earn/vault/VaultCard'
|
||||
import { getVaults } from 'utils/api'
|
||||
import { Text } from 'components/Text'
|
||||
|
||||
import VaultCard from './VaultCard'
|
||||
|
||||
async function Content() {
|
||||
const vaults = await getVaults()
|
||||
@ -20,7 +18,13 @@ async function Content() {
|
||||
contentClassName='grid grid-cols-3'
|
||||
>
|
||||
{featuredVaults.map((vault) => (
|
||||
<VaultCard key={vault.address} vault={vault} />
|
||||
<VaultCard
|
||||
key={vault.address}
|
||||
vault={vault}
|
||||
title={vault.name}
|
||||
subtitle='Hot off the presses'
|
||||
provider={vault.provider}
|
||||
/>
|
||||
))}
|
||||
</Card>
|
||||
)
|
@ -1,33 +1,40 @@
|
||||
'use client'
|
||||
|
||||
import Image from 'next/image'
|
||||
|
||||
import { Text } from 'components/Text'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import { formatPercent, formatValue } from 'utils/formatters'
|
||||
import { Button } from 'components/Button'
|
||||
import VaultLogo from 'components/Earn/VaultLogo'
|
||||
import VaultLogo from 'components/Earn/vault/VaultLogo'
|
||||
import Text from 'components/Text'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import useStore from 'store'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
import { formatPercent, formatValue } from 'utils/formatters'
|
||||
|
||||
interface Props {
|
||||
vault: Vault
|
||||
title: string
|
||||
subtitle: string
|
||||
provider?: string
|
||||
unbondingPeriod?: number
|
||||
}
|
||||
|
||||
export default function VaultCard(props: Props) {
|
||||
function openVaultModal() {}
|
||||
function openVaultModal() {
|
||||
useStore.setState({ vaultModal: { vault: props.vault } })
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='border-r-[1px] border-r-white/10 p-4'>
|
||||
<div className='align-center mb-8 flex justify-between'>
|
||||
<div>
|
||||
<Text size='xs' className='mb-2 text-white/60'>
|
||||
Hot off the presses
|
||||
{props.subtitle}
|
||||
</Text>
|
||||
<span className='flex'>
|
||||
<Text className='mr-2 font-bold'>{props.vault.name}</Text>
|
||||
<Text size='sm' className='text-white/60'>
|
||||
via {props.vault.provider}
|
||||
</Text>
|
||||
<Text className='mr-2 font-bold'>{props.title}</Text>
|
||||
{props.provider && (
|
||||
<Text size='sm' className='text-white/60'>
|
||||
via {props.provider}
|
||||
</Text>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<VaultLogo vault={props.vault} />
|
53
src/components/Earn/vault/VaultExpanded.tsx
Normal file
53
src/components/Earn/vault/VaultExpanded.tsx
Normal file
@ -0,0 +1,53 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
|
||||
import VaultCard from 'components/Earn/vault/VaultCard'
|
||||
import Text from 'components/Text'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
row: Row<Vault>
|
||||
resetExpanded: (defaultState?: boolean | undefined) => void
|
||||
}
|
||||
|
||||
export default function VaultExpanded(props: Props) {
|
||||
function enterVaultHandler() {
|
||||
useStore.setState({ vaultModal: { vault: props.row.original } })
|
||||
}
|
||||
|
||||
return (
|
||||
<tr
|
||||
key={props.row.id}
|
||||
className='cursor-pointer bg-black/20 transition-colors'
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
const isExpanded = props.row.getIsExpanded()
|
||||
props.resetExpanded()
|
||||
!isExpanded && props.row.toggleExpanded()
|
||||
}}
|
||||
>
|
||||
<td colSpan={5}>
|
||||
<Text className='border-b border-white/10 px-4 py-5 '>Select bonding period</Text>
|
||||
<div className='grid grid-cols-3 md:[&>div:nth-child(3)]:border-none'>
|
||||
<VaultCard
|
||||
vault={props.row.original}
|
||||
title='1 day unbonding'
|
||||
subtitle='$0 deposited'
|
||||
unbondingPeriod={1}
|
||||
/>
|
||||
<VaultCard
|
||||
vault={props.row.original}
|
||||
title='7 day unbonding'
|
||||
subtitle='$0 deposited'
|
||||
unbondingPeriod={7}
|
||||
/>
|
||||
<VaultCard
|
||||
vault={props.row.original}
|
||||
title='14 day unbonding'
|
||||
subtitle='$0 deposited'
|
||||
unbondingPeriod={14}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
114
src/components/Earn/vault/VaultModal.tsx
Normal file
114
src/components/Earn/vault/VaultModal.tsx
Normal file
@ -0,0 +1,114 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useState } from 'react'
|
||||
|
||||
import AccountSummary from 'components/Account/AccountSummary'
|
||||
import { Button } from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import Divider from 'components/Divider'
|
||||
import VaultLogo from 'components/Earn/vault/VaultLogo'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import { Modal } from 'components/Modal'
|
||||
import Slider from 'components/Slider'
|
||||
import Switch from 'components/Switch'
|
||||
import Text from 'components/Text'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import TokenInput from 'components/TokenInput'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import useStore from 'store'
|
||||
import { getAmount } from 'utils/accounts'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
import useParams from 'utils/route'
|
||||
|
||||
export default function VaultModal() {
|
||||
const modal = useStore((s) => s.vaultModal)
|
||||
const accounts = useStore((s) => s.accounts)
|
||||
const params = useParams()
|
||||
const [amount, setAmount] = useState(BN(0))
|
||||
const [percentage, setPercentage] = useState(0)
|
||||
const [isCustomAmount, setIsCustomAmount] = useState(false)
|
||||
const currentAccount = accounts?.find((account) => account.id === params.accountId)
|
||||
|
||||
function handleSwitch() {
|
||||
setIsCustomAmount(() => !isCustomAmount)
|
||||
}
|
||||
|
||||
function setOpen(isOpen: boolean) {
|
||||
useStore.setState({ vaultModal: null })
|
||||
}
|
||||
|
||||
function onChangeSlider(value: number) {}
|
||||
function onChangePrimary(value: BigNumber) {}
|
||||
function onChangeSecondary(value: BigNumber) {}
|
||||
|
||||
if (!modal || !currentAccount) return null
|
||||
|
||||
const primaryAsset = ASSETS.find((asset) => asset.denom === modal.vault.denoms.primary)
|
||||
const secondaryAsset = ASSETS.find((asset) => asset.denom === modal.vault.denoms.secondary)
|
||||
|
||||
if (!primaryAsset || !secondaryAsset) return null
|
||||
|
||||
const primaryMaxAmount = getAmount(primaryAsset.denom, currentAccount.deposits)
|
||||
const secondaryMaxAmount = getAmount(secondaryAsset.denom, currentAccount.deposits)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={true}
|
||||
setOpen={setOpen}
|
||||
header={
|
||||
<span className='flex items-center gap-4 px-4'>
|
||||
<VaultLogo vault={modal.vault} />
|
||||
<Text>{`${modal.vault.symbols.primary} - ${modal.vault.symbols.secondary}`}</Text>
|
||||
</span>
|
||||
}
|
||||
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
|
||||
contentClassName='flex flex-col'
|
||||
>
|
||||
<div className='flex gap-3 border-b border-b-white/5 px-6 py-4 gradient-header'>
|
||||
<TitleAndSubCell
|
||||
title={formatValue(1000000, { abbreviated: true, decimals: 6 })}
|
||||
sub={'Borrowed'}
|
||||
/>
|
||||
<div className='h-100 w-[1px] bg-white/10'></div>
|
||||
<TitleAndSubCell title={`${1000} (${10000})`} sub={'Liquidity available'} />
|
||||
</div>
|
||||
<div className='flex flex-grow items-start gap-6 p-6'>
|
||||
<Card
|
||||
className='w-full bg-white/5 p-4'
|
||||
contentClassName='gap-6 flex flex-col justify-between h-full'
|
||||
>
|
||||
<TokenInput
|
||||
onChange={onChangePrimary}
|
||||
amount={amount}
|
||||
max={primaryMaxAmount}
|
||||
asset={primaryAsset}
|
||||
/>
|
||||
<Slider value={percentage} onChange={onChangeSlider} />
|
||||
<TokenInput
|
||||
onChange={onChangeSecondary}
|
||||
amount={amount}
|
||||
max={secondaryMaxAmount}
|
||||
asset={secondaryAsset}
|
||||
/>
|
||||
<Divider />
|
||||
<div className='flex justify-between'>
|
||||
<Text className='text-white/50'>Custom amount</Text>
|
||||
<Switch checked={isCustomAmount} onChange={handleSwitch} name='customAmount' />
|
||||
</div>
|
||||
<div className='flex justify-between'>
|
||||
<Text className='text-white/50'>{`${modal.vault.symbols.primary}-${modal.vault.symbols.secondary} Position Value`}</Text>
|
||||
<FormattedNumber amount={0} options={{ prefix: '$' }} />
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => {}}
|
||||
className='w-full'
|
||||
text='Continue'
|
||||
rightIcon={<ArrowRight />}
|
||||
/>
|
||||
</Card>
|
||||
<AccountSummary />
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
@ -11,8 +11,7 @@ export const VaultRow = (props: AssetRowProps) => {
|
||||
<tr
|
||||
key={props.row.id}
|
||||
className={classNames(
|
||||
'cursor-pointer transition-colors',
|
||||
props.row.getIsExpanded() ? ' bg-black/20' : 'bg-white/0 hover:bg-white/5',
|
||||
'bg-white/3 cursor-pointer border-b border-t border-white/5 transition-colors hover:bg-white/5',
|
||||
)}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
@ -11,15 +11,15 @@ import {
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import VaultExpanded from 'components/Earn/vault/VaultExpanded'
|
||||
import VaultLogo from 'components/Earn/vault/VaultLogo'
|
||||
import { VaultRow } from 'components/Earn/vault/VaultRow'
|
||||
import { ChevronDown, SortAsc, SortDesc, SortNone } from 'components/Icons'
|
||||
import { Text } from 'components/Text'
|
||||
import { getAssetByDenom, getMarketAssets } from 'utils/assets'
|
||||
import VaultLogo from 'components/Earn/VaultLogo'
|
||||
import Text from 'components/Text'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import { convertPercentage, formatPercent, formatValue } from 'utils/formatters'
|
||||
import { VAULT_DEPOSIT_BUFFER } from 'constants/vaults'
|
||||
|
||||
import { VaultRow } from 'components/Earn/VaultRow'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
import { convertPercentage, formatPercent, formatValue } from 'utils/formatters'
|
||||
|
||||
type Props = {
|
||||
data: Vault[]
|
||||
@ -27,7 +27,6 @@ type Props = {
|
||||
|
||||
export const VaultTable = (props: Props) => {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
||||
const marketAssets = getMarketAssets()
|
||||
|
||||
const columns = React.useMemo<ColumnDef<Vault>[]>(
|
||||
() => [
|
||||
@ -90,7 +89,7 @@ export const VaultTable = (props: Props) => {
|
||||
),
|
||||
},
|
||||
],
|
||||
[marketAssets, props.data],
|
||||
[],
|
||||
)
|
||||
|
||||
const table = useReactTable({
|
||||
@ -157,6 +156,7 @@ export const VaultTable = (props: Props) => {
|
||||
return (
|
||||
<React.Fragment key={`${row.id}_subrow`}>
|
||||
<VaultRow row={row} resetExpanded={table.resetExpanded} />
|
||||
<VaultExpanded row={row} resetExpanded={table.resetExpanded} />
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
15
src/components/Footer.tsx
Normal file
15
src/components/Footer.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import Text from 'components/Text'
|
||||
|
||||
import packageInfo from '../../package.json'
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className='flex h-6 w-full items-center justify-center'>
|
||||
<div className='w-full max-w-screen-lg text-right'>
|
||||
<Text size='xs' className='opacity-50'>
|
||||
v{packageInfo.version}
|
||||
</Text>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { Text } from 'components/Text'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface ValueData extends FormattedNumberProps {
|
||||
format?: 'number' | 'string'
|
||||
|
@ -1,9 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import BorrowModal from 'components/BorrowModal'
|
||||
import VaultModal from 'components/Earn/vault/VaultModal'
|
||||
|
||||
export const Modals = () => (
|
||||
<>
|
||||
<BorrowModal />
|
||||
<VaultModal />
|
||||
</>
|
||||
)
|
||||
|
@ -4,47 +4,43 @@ import BigNumber from 'bignumber.js'
|
||||
import classNames from 'classnames'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
|
||||
import { formatValue } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
asset: Asset
|
||||
amount: number
|
||||
amount: BigNumber
|
||||
min?: BigNumber
|
||||
max?: BigNumber
|
||||
className: string
|
||||
maxDecimals: number
|
||||
minValue?: number
|
||||
max?: number
|
||||
maxLength?: number
|
||||
allowNegative?: boolean
|
||||
style?: {}
|
||||
disabled?: boolean
|
||||
|
||||
onChange: (amount: number) => void
|
||||
onChange: (amount: BigNumber) => void
|
||||
onBlur?: () => void
|
||||
onFocus?: () => void
|
||||
onRef?: (ref: React.RefObject<HTMLInputElement>) => void
|
||||
}
|
||||
|
||||
function magnify(value: number, asset: Asset) {
|
||||
return value === 0 ? 0 : new BigNumber(value).shiftedBy(asset.decimals).toNumber()
|
||||
}
|
||||
|
||||
function demagnify(amount: number, asset: Asset) {
|
||||
return amount === 0 ? 0 : new BigNumber(amount).dividedBy(-1 * asset.decimals).toNumber()
|
||||
}
|
||||
|
||||
export default function NumberInput(props: Props) {
|
||||
const inputRef = React.useRef<HTMLInputElement>(null)
|
||||
const cursorRef = React.useRef(0)
|
||||
const max = props.max ? demagnify(props.max, props.asset) : undefined
|
||||
// const max = props.max ? demagnify(props.max, props.asset) : undefined
|
||||
|
||||
const [inputValue, setInputValue] = useState({
|
||||
formatted: demagnify(props.amount, props.asset).toString(),
|
||||
value: demagnify(props.amount, props.asset),
|
||||
})
|
||||
const [formattedAmount, setFormattedAmount] = useState(
|
||||
props.amount.shiftedBy(-1 * props.asset.decimals).toString(),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setInputValue({
|
||||
formatted: demagnify(props.amount, props.asset).toString(),
|
||||
value: demagnify(props.amount, props.asset),
|
||||
})
|
||||
setFormattedAmount(
|
||||
formatValue(props.amount.toNumber(), {
|
||||
decimals: props.asset.decimals,
|
||||
maxDecimals: props.asset.decimals,
|
||||
thousandSeparator: false,
|
||||
}),
|
||||
)
|
||||
}, [props.amount, props.asset])
|
||||
|
||||
useEffect(() => {
|
||||
@ -58,16 +54,17 @@ export default function NumberInput(props: Props) {
|
||||
props.onFocus && props.onFocus()
|
||||
}
|
||||
|
||||
const updateValues = (formatted: string, value: number) => {
|
||||
const updateValues = (formatted: string, amount: BigNumber) => {
|
||||
const lastChar = formatted.charAt(formatted.length - 1)
|
||||
if (lastChar === '.') {
|
||||
cursorRef.current = (inputRef.current?.selectionEnd || 0) + 1
|
||||
} else {
|
||||
cursorRef.current = inputRef.current?.selectionEnd || 0
|
||||
}
|
||||
setInputValue({ formatted, value })
|
||||
if (value !== inputValue.value) {
|
||||
props.onChange(magnify(value, props.asset))
|
||||
setFormattedAmount(formatted)
|
||||
console.log(props.amount.toNumber(), amount.toNumber())
|
||||
if (props.amount.isEqualTo(amount)) {
|
||||
props.onChange(amount)
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +72,7 @@ export default function NumberInput(props: Props) {
|
||||
if (!inputRef.current) return
|
||||
|
||||
const cursor = cursorRef.current
|
||||
const length = inputValue.formatted.length
|
||||
const length = formattedAmount.length
|
||||
|
||||
if (cursor > length) {
|
||||
inputRef.current.setSelectionRange(length, length)
|
||||
@ -83,70 +80,72 @@ export default function NumberInput(props: Props) {
|
||||
}
|
||||
|
||||
inputRef.current.setSelectionRange(cursor, cursor)
|
||||
}, [inputValue, inputRef])
|
||||
}, [formattedAmount, inputRef])
|
||||
|
||||
const onInputChange = (value: string) => {
|
||||
const numberCount = value.match(/[0-9]/g)?.length || 0
|
||||
const decimals = value.split('.')[1]?.length || 0
|
||||
const lastChar = value.charAt(value.length - 1)
|
||||
const isNumber = !isNaN(Number(value))
|
||||
const hasMultipleDots = (value.match(/[.,]/g)?.length || 0) > 1
|
||||
const onInputChange = (formattedAmount: string) => {
|
||||
const numberCount = formattedAmount.match(/[0-9]/g)?.length || 0
|
||||
const decimals = formattedAmount.split('.')[1]?.length || 0
|
||||
const lastChar = formattedAmount.charAt(formattedAmount.length - 1)
|
||||
const isNumber = !isNaN(Number(formattedAmount))
|
||||
const hasMultipleDots = (formattedAmount.match(/[.,]/g)?.length || 0) > 1
|
||||
const isSeparator = lastChar === '.' || lastChar === ','
|
||||
const isNegative = value.indexOf('-') > -1
|
||||
const isLowerThanMinimum = props.minValue !== undefined && Number(value) < props.minValue
|
||||
const isHigherThanMaximum = max !== undefined && Number(value) > max
|
||||
const isNegative = formattedAmount.indexOf('-') > -1
|
||||
const isLowerThanMinimum = props.min !== undefined && props.min.isGreaterThan(formattedAmount)
|
||||
const isHigherThanMaximum = props.max !== undefined && props.max.isLessThan(formattedAmount)
|
||||
const isTooLong = props.maxLength !== undefined && numberCount > props.maxLength
|
||||
const exceedsMaxDecimals = props.maxDecimals !== undefined && decimals > props.maxDecimals
|
||||
|
||||
if (isNegative && !props.allowNegative) return
|
||||
|
||||
if (isSeparator && value.length === 1) {
|
||||
updateValues('0.', 0)
|
||||
if (isSeparator && formattedAmount.length === 1) {
|
||||
updateValues('0.', BN(0))
|
||||
return
|
||||
}
|
||||
|
||||
if (isSeparator && !hasMultipleDots) {
|
||||
updateValues(value.replace(',', '.'), inputValue.value)
|
||||
updateValues(formattedAmount.replace(',', '.'), props.amount)
|
||||
return
|
||||
}
|
||||
|
||||
if (!isNumber || hasMultipleDots) return
|
||||
|
||||
if (exceedsMaxDecimals) {
|
||||
value = value.substring(0, value.length - 1)
|
||||
formattedAmount = formattedAmount.substring(0, formattedAmount.length - 1)
|
||||
}
|
||||
|
||||
if (isTooLong) return
|
||||
|
||||
if (isLowerThanMinimum) {
|
||||
updateValues(String(props.minValue), props.minValue!)
|
||||
if (isLowerThanMinimum && props.min) {
|
||||
updateValues(String(props.min), props.min)
|
||||
return
|
||||
}
|
||||
|
||||
if (isHigherThanMaximum) {
|
||||
updateValues(String(max), max!)
|
||||
if (isHigherThanMaximum && props.max) {
|
||||
updateValues(String(props.max), props.max)
|
||||
return
|
||||
}
|
||||
|
||||
if (lastChar === '0' && Number(value) === Number(inputValue.value)) {
|
||||
const amount = BN(formattedAmount).shiftedBy(props.asset.decimals)
|
||||
|
||||
if (lastChar === '0' && amount.isEqualTo(props.amount)) {
|
||||
cursorRef.current = (inputRef.current?.selectionEnd || 0) + 1
|
||||
setInputValue({ ...inputValue, formatted: value })
|
||||
setFormattedAmount(formattedAmount)
|
||||
return
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
updateValues(value, 0)
|
||||
if (!formattedAmount) {
|
||||
updateValues(formattedAmount, BN(0))
|
||||
return
|
||||
}
|
||||
|
||||
updateValues(String(Number(value)), Number(value))
|
||||
updateValues(formattedAmount, amount)
|
||||
}
|
||||
|
||||
return (
|
||||
<input
|
||||
ref={inputRef}
|
||||
type='text'
|
||||
value={inputValue.formatted === '0' ? '' : inputValue.formatted}
|
||||
value={formattedAmount === '0' ? '' : formattedAmount}
|
||||
onFocus={onInputFocus}
|
||||
onChange={(e) => onInputChange(e.target.value)}
|
||||
onBlur={props.onBlur}
|
||||
|
@ -5,25 +5,27 @@ interface Props {
|
||||
children?: ReactNode | string
|
||||
content?: ReactNode | string
|
||||
className?: string
|
||||
hasBackdropIsolation?: boolean
|
||||
show: boolean
|
||||
setShow: (show: boolean) => void
|
||||
}
|
||||
|
||||
export const Overlay = ({ children, content, className, show, setShow }: Props) => {
|
||||
export default function Overlay(props: Props) {
|
||||
const onClickAway = () => {
|
||||
setShow(false)
|
||||
props.setShow(false)
|
||||
}
|
||||
|
||||
return show ? (
|
||||
return props.show ? (
|
||||
<>
|
||||
<div
|
||||
className={classNames(
|
||||
'max-w-screen absolute isolate z-50 rounded-sm shadow-overlay backdrop-blur-lg gradient-popover',
|
||||
'max-w-screen absolute isolate z-50 rounded-sm shadow-overlay backdrop-blur-lg',
|
||||
props.hasBackdropIsolation ? 'bg-body' : 'gradient-popover',
|
||||
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas',
|
||||
className,
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
{children ? children : content}
|
||||
{props.children ? props.children : props.content}
|
||||
</div>
|
||||
<div
|
||||
className='fixed left-0 top-0 z-40 block h-full w-full hover:cursor-pointer'
|
||||
|
@ -3,7 +3,7 @@ import { Suspense } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import Loading from 'components/Loading'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import { getAccounts } from 'utils/api'
|
||||
|
||||
async function Content(props: PageProps) {
|
||||
|
75
src/components/Select/Option.tsx
Normal file
75
src/components/Select/Option.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import classNames from 'classnames'
|
||||
import Image from 'next/image'
|
||||
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Text from 'components/Text'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { formatValue } from 'utils/formatters'
|
||||
|
||||
interface Props extends Option {
|
||||
isSelected?: boolean
|
||||
isDisplay?: boolean
|
||||
onClick?: (value: string) => void
|
||||
}
|
||||
|
||||
export default function Option(props: Props) {
|
||||
const isCoin = !!props.denom
|
||||
|
||||
if (isCoin) {
|
||||
const currentAsset = ASSETS.find((asset) => asset.denom === props.denom)
|
||||
const symbol = currentAsset?.symbol ?? ASSETS[0].symbol
|
||||
const logo = currentAsset?.logo ?? ASSETS[0].logo
|
||||
const denom = currentAsset?.denom ?? ASSETS[0].denom
|
||||
const decimals = currentAsset?.decimals ?? ASSETS[0].decimals
|
||||
const balance = props.amount ?? '0'
|
||||
|
||||
if (props.isDisplay) {
|
||||
return (
|
||||
<div className={classNames('block bg-white/10 p-3 hover:cursor-pointer')}>{symbol}</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'grid grid-flow-row grid-cols-5 grid-rows-2 py-3.5 pr-4',
|
||||
'border-b border-b-white/20 last:border-none',
|
||||
'hover:cursor-pointer hover:bg-white/20',
|
||||
props.isSelected && 'bg-white/10',
|
||||
)}
|
||||
onClick={() => props?.onClick && props.onClick(denom)}
|
||||
>
|
||||
<div className='row-span-2 flex h-full items-center justify-center'>
|
||||
<Image src={logo} alt={`${symbol} token logo`} width={32} height={32} />
|
||||
</div>
|
||||
<Text className='col-span-2 pb-1'>{symbol}</Text>
|
||||
<Text size='sm' className='col-span-2 pb-1 text-right font-bold'>
|
||||
{formatValue(balance, { decimals, maxDecimals: 4, minDecimals: 0, rounded: true })}
|
||||
</Text>
|
||||
<Text size='sm' className='col-span-2 text-white/50'>
|
||||
{formatValue(5, { maxDecimals: 2, minDecimals: 0, prefix: 'APY ', suffix: '%' })}
|
||||
</Text>
|
||||
<Text size='sm' className='col-span-2 text-right text-white/50'>
|
||||
<DisplayCurrency coin={{ denom, amount: balance }} />
|
||||
</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const label = props.label
|
||||
if (props.isDisplay) {
|
||||
return <div className={classNames('block bg-white/10 p-3 hover:cursor-pointer')}>{label}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'block p-3 hover:cursor-pointer hover:bg-white/20',
|
||||
props.isSelected && 'bg-white/10',
|
||||
)}
|
||||
onClick={() => props?.onClick && props.onClick(props.value)}
|
||||
>
|
||||
{label}
|
||||
</div>
|
||||
)
|
||||
}
|
82
src/components/Select/Select.tsx
Normal file
82
src/components/Select/Select.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
'use client'
|
||||
|
||||
import classNames from 'classnames'
|
||||
import { useState } from 'react'
|
||||
|
||||
import Overlay from 'components/Overlay/Overlay'
|
||||
import Option from 'components/Select/Option'
|
||||
import Text from 'components/Text'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
|
||||
interface Props {
|
||||
options: Option[]
|
||||
defaultValue?: string
|
||||
onChange: (value: string) => void
|
||||
isParent?: boolean
|
||||
className?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
export default function Select(props: Props) {
|
||||
const [value, setValue] = useState(props.defaultValue)
|
||||
|
||||
const selectedOption = value
|
||||
? props.options.find((option) => option?.value || option?.denom === value)
|
||||
: null
|
||||
|
||||
const [selected, setSelected] = useState<Option>(selectedOption)
|
||||
|
||||
const [showDropdown, setShowDropdown] = useToggle()
|
||||
function handleChange(optionValue: string) {
|
||||
setValue(optionValue)
|
||||
setSelected(props.options.find((option) => option?.value || option?.denom === optionValue))
|
||||
setShowDropdown(false)
|
||||
props.onChange(optionValue)
|
||||
}
|
||||
|
||||
console.log(selected)
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
props.isParent && 'relative',
|
||||
'flex min-w-fit items-center gap-2',
|
||||
props.className,
|
||||
)}
|
||||
role='select'
|
||||
onClick={() => setShowDropdown(!showDropdown)}
|
||||
>
|
||||
{selectedOption ? (
|
||||
<Option {...selectedOption} isDisplay />
|
||||
) : (
|
||||
<Text className='w-full opacity-50 hover:cursor-pointer'>Select</Text>
|
||||
)}
|
||||
<Overlay
|
||||
show={showDropdown}
|
||||
className={classNames('left-0 top-[calc(100%+8px)] isolate w-full')}
|
||||
setShow={setShowDropdown}
|
||||
hasBackdropIsolation
|
||||
>
|
||||
<div className='relative isolate w-full overflow-hidden rounded-sm'>
|
||||
{props.title && (
|
||||
<Text size='lg' className='block bg-white/25 p-4 font-bold'>
|
||||
{props.title}
|
||||
</Text>
|
||||
)}
|
||||
{props.options.map((option: Option, index: number) => (
|
||||
<Option
|
||||
key={index}
|
||||
{...option}
|
||||
isSelected={
|
||||
option?.value
|
||||
? option?.value === selected?.value
|
||||
: option?.denom === selected?.denom
|
||||
}
|
||||
onClick={handleChange}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Overlay>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,23 +1,22 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import { Gear } from 'components/Icons'
|
||||
import { Overlay } from 'components/Overlay/Overlay'
|
||||
import Overlay from 'components/Overlay/Overlay'
|
||||
import Switch from 'components/Switch'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { DISPLAY_CURRENCY_KEY, ENABLE_ANIMATIONS_KEY } from 'constants/localStore'
|
||||
import { useAnimations } from 'hooks/useAnimations'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import useStore from 'store'
|
||||
import { getDisplayCurrencies } from 'utils/assets'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
|
||||
export default function Settings() {
|
||||
useAnimations()
|
||||
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
const [showMenu, setShowMenu] = useToggle()
|
||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||
const displayCurrency = useStore((s) => s.displayCurrency)
|
||||
const displayCurrencies = getDisplayCurrencies()
|
||||
@ -32,10 +31,10 @@ export default function Settings() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleReduceMotion(val: boolean) {
|
||||
useStore.setState({ enableAnimations: !val })
|
||||
function handleReduceMotion() {
|
||||
useStore.setState({ enableAnimations: !enableAnimations })
|
||||
if (typeof window !== 'undefined')
|
||||
window.localStorage.setItem(ENABLE_ANIMATIONS_KEY, val ? 'false' : 'true')
|
||||
window.localStorage.setItem(ENABLE_ANIMATIONS_KEY, enableAnimations ? 'false' : 'true')
|
||||
}
|
||||
|
||||
function handleCurrencyChange(e: React.ChangeEvent<HTMLSelectElement>) {
|
||||
|
@ -3,6 +3,7 @@ import { ChangeEvent, useRef, useState } from 'react'
|
||||
import Draggable from 'react-draggable'
|
||||
|
||||
import { OverlayMark } from 'components/Icons/index'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
|
||||
type Props = {
|
||||
value: number
|
||||
@ -12,11 +13,11 @@ type Props = {
|
||||
}
|
||||
|
||||
export default function Slider(props: Props) {
|
||||
const [showTooltip, setShowTooltip] = useState(false)
|
||||
const [showTooltip, setShowTooltip] = useToggle()
|
||||
const [sliderRect, setSliderRect] = useState({ width: 0, left: 0, right: 0 })
|
||||
const ref = useRef<HTMLDivElement>(null)
|
||||
const nodeRef = useRef(null)
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [isDragging, setIsDragging] = useToggle()
|
||||
|
||||
function handleSliderRect() {
|
||||
const leftCap = ref.current?.getBoundingClientRect().left ?? 0
|
||||
|
@ -3,7 +3,7 @@ import classNames from 'classnames'
|
||||
interface Props {
|
||||
name: string
|
||||
checked: boolean
|
||||
onChange: (checked: boolean) => void
|
||||
onChange: () => void
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
@ -23,7 +23,7 @@ export default function Switch(props: Props) {
|
||||
name={props.name}
|
||||
className={classNames('peer hidden')}
|
||||
checked={props.checked}
|
||||
onChange={(e) => props.onChange(e.target.checked)}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
<label
|
||||
htmlFor={props.name}
|
||||
|
@ -1,14 +1,14 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Switch from 'components/Switch'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
|
||||
interface Props {
|
||||
name: string
|
||||
label: string
|
||||
value: boolean
|
||||
onChange: (checked: boolean) => void
|
||||
onChange: () => void
|
||||
className?: string
|
||||
tooltip?: string
|
||||
disabled?: boolean
|
||||
|
@ -13,14 +13,10 @@ interface Props {
|
||||
const headlines = ['h1', 'h2', 'h3', 'h4']
|
||||
const headMap = ['6xl', '5xl', '4xl', '3xl']
|
||||
|
||||
export const Text = ({
|
||||
children,
|
||||
className,
|
||||
monospace = false,
|
||||
size = 'base',
|
||||
tag = 'p',
|
||||
uppercase = false,
|
||||
}: Props) => {
|
||||
export default function Text(props: Props) {
|
||||
const tag = props.tag ?? 'p'
|
||||
const size = props.size ?? 'base'
|
||||
|
||||
const tagIndex = headlines.indexOf(tag)
|
||||
const sizeClass = tagIndex > -1 ? headMap[tagIndex] : size
|
||||
const HtmlElement = tag as keyof JSX.IntrinsicElements
|
||||
@ -28,12 +24,12 @@ export const Text = ({
|
||||
return (
|
||||
<HtmlElement
|
||||
className={classNames(
|
||||
className,
|
||||
uppercase ? `text-${sizeClass}-caps` : `text-${sizeClass}`,
|
||||
monospace && 'number',
|
||||
props.className,
|
||||
props.uppercase ? `text-${sizeClass}-caps` : `text-${sizeClass}`,
|
||||
props.monospace && 'number',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{props.children}
|
||||
</HtmlElement>
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props {
|
||||
title: string | React.ReactNode
|
||||
|
@ -5,7 +5,7 @@ import { toast as createToast, Slide, ToastContainer } from 'react-toastify'
|
||||
|
||||
import { Button } from 'components/Button'
|
||||
import { CheckCircled, Cross, CrossCircled } from 'components/Icons'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
import useStore from 'store'
|
||||
|
||||
export default function Toaster() {
|
||||
@ -40,7 +40,7 @@ export default function Toaster() {
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<Text size='sm' className='text-bold text-white'>
|
||||
<Text size='sm' className='font-bold text-white'>
|
||||
{toast.message}
|
||||
</Text>
|
||||
<div className='absolute right-6 top-8 '>
|
||||
|
@ -1,19 +1,82 @@
|
||||
'use client'
|
||||
|
||||
import BigNumber from 'bignumber.js'
|
||||
import classNames from 'classnames'
|
||||
import Image from 'next/image'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import NumberInput from 'components/NumberInput'
|
||||
import { Text } from 'components/Text'
|
||||
import Select from 'components/Select/Select'
|
||||
import Text from 'components/Text'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import useStore from 'store'
|
||||
import { magnify } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
amount: number
|
||||
max: number
|
||||
asset: Asset
|
||||
onChange: (amount: number) => void
|
||||
amount: BigNumber
|
||||
onChange: (amount: BigNumber) => void
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export default function TokenInput(props: Props) {
|
||||
interface SingleProps extends Props {
|
||||
asset: Asset
|
||||
max: BigNumber
|
||||
hasSelect?: boolean
|
||||
onChangeAsset?: (asset: Asset, max: BigNumber) => void
|
||||
}
|
||||
|
||||
interface SelectProps extends Props {
|
||||
asset?: Asset
|
||||
max?: BigNumber
|
||||
hasSelect: boolean
|
||||
onChangeAsset: (asset: Asset, max: BigNumber) => void
|
||||
}
|
||||
|
||||
export default function TokenInput(props: SingleProps | SelectProps) {
|
||||
const balances = useStore((s) => s.balances)
|
||||
const baseCurrency = useStore((s) => s.baseCurrency)
|
||||
const [asset, setAsset] = useState<Asset>(props.asset ? props.asset : baseCurrency)
|
||||
const [coin, setCoin] = useState<Coin>({
|
||||
denom: props.asset ? props.asset.denom : baseCurrency.denom,
|
||||
amount: '0',
|
||||
})
|
||||
|
||||
const selectedAssetDenom = props.asset ? props.asset.denom : baseCurrency.denom
|
||||
|
||||
const updateAsset = useCallback(
|
||||
(coinDenom: string) => {
|
||||
const newAsset = ASSETS.find((asset) => asset.denom === coinDenom) ?? baseCurrency
|
||||
const newCoin = balances?.find((coin) => coin.denom === coinDenom)
|
||||
setAsset(newAsset)
|
||||
setCoin(newCoin ?? { denom: coinDenom, amount: '0' })
|
||||
},
|
||||
[balances, baseCurrency],
|
||||
)
|
||||
|
||||
function setDefaultAsset() {
|
||||
if (!balances || balances?.length === 0) return setAsset(baseCurrency)
|
||||
if (balances.length === 1)
|
||||
return setAsset(ASSETS.find((asset) => asset.denom === balances[0].denom) ?? baseCurrency)
|
||||
return setAsset(ASSETS.find((asset) => asset.denom === selectedAssetDenom) ?? baseCurrency)
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
setDefaultAsset()
|
||||
updateAsset(asset.denom)
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
props.onChangeAsset && props.onChangeAsset(asset, coin ? BN(coin.amount) : BN(0))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [coin, asset])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
@ -22,28 +85,49 @@ export default function TokenInput(props: Props) {
|
||||
props.disabled && 'pointer-events-none opacity-50',
|
||||
)}
|
||||
>
|
||||
<div className='box-content flex h-11 w-full rounded-sm border border-white/20 bg-white/5'>
|
||||
<div className='flex min-w-fit items-center gap-2 border-r border-white/20 bg-white/5 p-3'>
|
||||
<Image src={props.asset.logo} alt='token' width={20} height={20} />
|
||||
<Text>{props.asset.symbol}</Text>
|
||||
</div>
|
||||
<div className='relative isolate z-40 box-content flex h-11 w-full rounded-sm border border-white/20 bg-white/5'>
|
||||
{props.hasSelect && balances ? (
|
||||
<Select
|
||||
options={balances}
|
||||
defaultValue={coin.denom}
|
||||
onChange={(value) => updateAsset(value)}
|
||||
title='Your Wallet'
|
||||
className=' border-r border-white/20 bg-white/5'
|
||||
/>
|
||||
) : (
|
||||
<div className='flex min-w-fit items-center gap-2 border-r border-white/20 bg-white/5 p-3'>
|
||||
<Image src={asset.logo} alt='token' width={20} height={20} />
|
||||
<Text>{asset.symbol}</Text>
|
||||
</div>
|
||||
)}
|
||||
<NumberInput
|
||||
disabled={props.disabled}
|
||||
asset={props.asset}
|
||||
maxDecimals={props.asset.decimals}
|
||||
asset={asset}
|
||||
maxDecimals={asset.decimals}
|
||||
onChange={props.onChange}
|
||||
amount={props.amount}
|
||||
max={props.max}
|
||||
className='border-none p-3'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex justify-between'>
|
||||
<Text size='xs' className='text-white/50' monospace>
|
||||
1 OSMO = $0.9977
|
||||
</Text>
|
||||
<Text size='xs' monospace className='text-white/50'>
|
||||
~ $0.00
|
||||
</Text>
|
||||
|
||||
<div className='flex'>
|
||||
<div className='flex flex-1'>
|
||||
<Text size='xs' className='text-white/50' monospace>
|
||||
{`1 ${asset.symbol} =`}
|
||||
</Text>
|
||||
<DisplayCurrency
|
||||
className='inline pl-0.5 text-xs text-white/50'
|
||||
coin={{ denom: asset.denom, amount: String(magnify(1, asset)) }}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex'>
|
||||
<DisplayCurrency
|
||||
isApproximation
|
||||
className='inline pl-0.5 text-xs text-white/50'
|
||||
coin={{ denom: asset.denom, amount: props.amount.toString() }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -1,25 +1,41 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import Slider from './Slider'
|
||||
import TokenInput from './TokenInput'
|
||||
import { BN } from 'utils/helpers'
|
||||
import Slider from 'components/Slider'
|
||||
import TokenInput from 'components/TokenInput'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
|
||||
interface Props {
|
||||
amount: number
|
||||
max: number
|
||||
asset: Asset
|
||||
onChange: (amount: number) => void
|
||||
amount: BigNumber
|
||||
onChange: (amount: BigNumber) => void
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
export default function TokenInputWithSlider(props: Props) {
|
||||
interface SingleProps extends Props {
|
||||
max: BigNumber
|
||||
asset: Asset
|
||||
hasSelect?: boolean
|
||||
onChangeAsset?: (asset: Asset) => void
|
||||
}
|
||||
|
||||
interface SelectProps extends Props {
|
||||
max?: BigNumber
|
||||
asset?: Asset
|
||||
onChangeAsset: (asset: Asset) => void
|
||||
hasSelect: boolean
|
||||
}
|
||||
|
||||
export default function TokenInputWithSlider(props: SingleProps | SelectProps) {
|
||||
const [amount, setAmount] = useState(props.amount)
|
||||
const [percentage, setPercentage] = useState(0)
|
||||
const [asset, setAsset] = useState<Asset>(props.asset ? props.asset : ASSETS[0])
|
||||
const [max, setMax] = useState<BigNumber>(props.max ? props.max : BN(0))
|
||||
|
||||
const onSliderChange = useCallback(
|
||||
(percentage: number, liquidityAmount: number) => {
|
||||
const newAmount = new BigNumber(percentage).div(100).times(liquidityAmount).toNumber()
|
||||
(percentage: number, liquidityAmount: BigNumber) => {
|
||||
const newAmount = BN(percentage).div(100).times(liquidityAmount)
|
||||
setPercentage(percentage)
|
||||
setAmount(newAmount)
|
||||
props.onChange(newAmount)
|
||||
@ -28,27 +44,40 @@ export default function TokenInputWithSlider(props: Props) {
|
||||
)
|
||||
|
||||
const onInputChange = useCallback(
|
||||
(newAmount: number, liquidityAmount: number) => {
|
||||
(newAmount: BigNumber, liquidityAmount: BigNumber) => {
|
||||
setAmount(newAmount)
|
||||
setPercentage(new BigNumber(newAmount).div(liquidityAmount).times(100).toNumber())
|
||||
setPercentage(BN(newAmount).div(liquidityAmount).times(100).toNumber())
|
||||
props.onChange(newAmount)
|
||||
},
|
||||
[props],
|
||||
)
|
||||
|
||||
const onAssetChange = useCallback(
|
||||
(newAsset: Asset, liquidtyAmount: BigNumber) => {
|
||||
props.onChangeAsset && props.onChangeAsset(newAsset)
|
||||
setAsset(newAsset)
|
||||
setMax(liquidtyAmount)
|
||||
setPercentage(0)
|
||||
setAmount(BN(0))
|
||||
},
|
||||
[props],
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={props.className}>
|
||||
<TokenInput
|
||||
asset={props.asset}
|
||||
onChange={(amount) => onInputChange(amount, props.max)}
|
||||
asset={asset}
|
||||
onChange={(amount) => onInputChange(amount, max)}
|
||||
onChangeAsset={(asset: Asset, max: BigNumber) => onAssetChange(asset, max)}
|
||||
amount={amount}
|
||||
max={props.max}
|
||||
max={max}
|
||||
className='mb-4'
|
||||
disabled={props.disabled}
|
||||
hasSelect
|
||||
/>
|
||||
<Slider
|
||||
value={percentage}
|
||||
onChange={(value) => onSliderChange(value, props.max)}
|
||||
onChange={(value) => onSliderChange(value, max)}
|
||||
disabled={props.disabled}
|
||||
/>
|
||||
</div>
|
||||
|
@ -2,7 +2,7 @@ import { Suspense } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import Loading from 'components/Loading'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
|
||||
async function Content(props: PageProps) {
|
||||
const address = props.params.address
|
||||
|
@ -2,7 +2,7 @@ import { Suspense } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import Loading from 'components/Loading'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
|
||||
async function Content(props: PageProps) {
|
||||
const address = props.params.address
|
||||
|
@ -2,7 +2,7 @@ import { Suspense } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import Loading from 'components/Loading'
|
||||
import { Text } from 'components/Text'
|
||||
import Text from 'components/Text'
|
||||
|
||||
async function Content(props: PageProps) {
|
||||
return <Text size='sm'>Chart view</Text>
|
||||
|
@ -16,17 +16,19 @@ import { Button } from 'components/Button'
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { Check, Copy, ExternalLink, Osmo } from 'components/Icons'
|
||||
import { Overlay } from 'components/Overlay/Overlay'
|
||||
import { Text } from 'components/Text'
|
||||
import Overlay from 'components/Overlay/Overlay'
|
||||
import Text from 'components/Text'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import useStore from 'store'
|
||||
import { Endpoints, getEndpoint, getWalletBalancesSWR } from 'utils/api'
|
||||
import { getBaseAsset } from 'utils/assets'
|
||||
import { getBaseAsset, getMarketAssets } from 'utils/assets'
|
||||
import { formatValue, truncate } from 'utils/formatters'
|
||||
|
||||
export default function ConnectedButton() {
|
||||
// ---------------
|
||||
// EXTERNAL HOOKS
|
||||
// ---------------
|
||||
const marketAssets = getMarketAssets()
|
||||
const { disconnect } = useWallet()
|
||||
const { disconnect: terminate } = useWalletManager()
|
||||
const address = useStore((s) => s.client?.recentWallet.account?.address)
|
||||
@ -40,7 +42,7 @@ export default function ConnectedButton() {
|
||||
// ---------------
|
||||
// LOCAL STATE
|
||||
// ---------------
|
||||
const [showDetails, setShowDetails] = useState(false)
|
||||
const [showDetails, setShowDetails] = useToggle()
|
||||
const [walletAmount, setWalletAmount] = useState(0)
|
||||
const [isCopied, setCopied] = useClipboard(address || '', {
|
||||
successDuration: 1000 * 5,
|
||||
@ -59,7 +61,7 @@ export default function ConnectedButton() {
|
||||
const disconnectWallet = () => {
|
||||
disconnect()
|
||||
terminate()
|
||||
useStore.setState({ client: undefined })
|
||||
useStore.setState({ client: undefined, balances: null })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -69,7 +71,11 @@ export default function ConnectedButton() {
|
||||
.div(10 ** baseAsset.decimals)
|
||||
.toNumber(),
|
||||
)
|
||||
}, [data, baseAsset.denom, baseAsset.decimals])
|
||||
|
||||
const assetDenoms = marketAssets.map((asset) => asset.denom)
|
||||
const balances = data.filter((coin) => assetDenoms.includes(coin.denom))
|
||||
useStore.setState({ balances })
|
||||
}, [data, baseAsset.denom, baseAsset.decimals, marketAssets])
|
||||
|
||||
return (
|
||||
<div className={'relative'}>
|
||||
@ -102,7 +108,7 @@ export default function ConnectedButton() {
|
||||
{isLoading ? (
|
||||
<CircularProgress size={12} />
|
||||
) : (
|
||||
`${formatValue(walletAmount, { suffix: baseAsset.symbol })}`
|
||||
`${formatValue(walletAmount, { suffix: ` ${baseAsset.symbol}` })}`
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
|
@ -1,13 +1,13 @@
|
||||
import Card from 'components/Card'
|
||||
import Tab from 'components/Earn/Tab'
|
||||
import AvailableVaults from 'components/Earn/vault/AvailableVaults'
|
||||
import FeaturedVaults from 'components/Earn/vault/FeaturedVaults'
|
||||
|
||||
export default function Farmpage({ params }: { params: PageParams }) {
|
||||
return (
|
||||
<>
|
||||
<Tab params={params} isFarm />
|
||||
<Card title='Featured vaults'>
|
||||
<></>
|
||||
</Card>
|
||||
<FeaturedVaults />
|
||||
<AvailableVaults />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
14
src/hooks/useToggle.tsx
Normal file
14
src/hooks/useToggle.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function useToggle(
|
||||
defaultValue?: boolean,
|
||||
): [boolean, (isToggled?: boolean) => void] {
|
||||
const [toggle, setToggle] = useState<boolean>(defaultValue ?? false)
|
||||
|
||||
function handleToggle(isToggled?: boolean) {
|
||||
if (isToggled !== undefined) return setToggle(isToggled)
|
||||
return setToggle(!toggle)
|
||||
}
|
||||
|
||||
return [toggle, handleToggle]
|
||||
}
|
21
src/pages/api/accounts/[id]/vaults.ts
Normal file
21
src/pages/api/accounts/[id]/vaults.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import { ENV, ENV_MISSING_MESSAGE, VERCEL_BYPASS } from 'constants/env'
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!ENV.URL_RPC || !ENV.ADDRESS_CREDIT_MANAGER || !ENV.URL_API) {
|
||||
return res.status(404).json(ENV_MISSING_MESSAGE)
|
||||
}
|
||||
|
||||
const accountId = req.query.id
|
||||
|
||||
const account: Account = await (
|
||||
await fetch(`${ENV.URL_API}/accounts/${accountId}${VERCEL_BYPASS}`)
|
||||
).json()
|
||||
|
||||
if (account) {
|
||||
return res.status(200).json(account.vaults)
|
||||
}
|
||||
|
||||
return res.status(404)
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
import { ENV, ENV_MISSING_MESSAGE, VERCEL_BYPASS } from 'constants/env'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!ENV.URL_API) {
|
||||
@ -28,7 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
borrowRate: market.borrowRate ?? 0,
|
||||
liquidity: {
|
||||
amount: amount,
|
||||
value: new BigNumber(amount).times(price).toString(),
|
||||
value: BN(amount).times(price).toString(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||
import { WalletClient, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
|
||||
import { GetState, SetState } from 'zustand'
|
||||
|
||||
@ -7,6 +6,7 @@ export interface CommonSlice {
|
||||
address?: string
|
||||
enableAnimations: boolean
|
||||
isOpen: boolean
|
||||
balances: Coin[] | null
|
||||
selectedAccount: string | null
|
||||
client?: WalletClient
|
||||
status: WalletConnectionStatus
|
||||
@ -15,6 +15,7 @@ export interface CommonSlice {
|
||||
export function createCommonSlice(set: SetState<CommonSlice>, get: GetState<CommonSlice>) {
|
||||
return {
|
||||
accounts: null,
|
||||
balances: null,
|
||||
creditAccounts: null,
|
||||
enableAnimations: true,
|
||||
isOpen: true,
|
||||
|
@ -10,6 +10,9 @@ export interface ModalSlice {
|
||||
deleteAccountModal: boolean
|
||||
fundAccountModal: boolean
|
||||
withdrawModal: boolean
|
||||
vaultModal: {
|
||||
vault: Vault
|
||||
} | null
|
||||
}
|
||||
|
||||
export function createModalSlice(set: SetState<ModalSlice>, get: GetState<ModalSlice>) {
|
||||
@ -19,5 +22,6 @@ export function createModalSlice(set: SetState<ModalSlice>, get: GetState<ModalS
|
||||
deleteAccountModal: false,
|
||||
fundAccountModal: false,
|
||||
withdrawModal: false,
|
||||
vaultModal: null,
|
||||
}
|
||||
}
|
||||
|
1
src/types/interfaces/account.d.ts
vendored
1
src/types/interfaces/account.d.ts
vendored
@ -3,4 +3,5 @@ interface Account {
|
||||
deposits: Coin[]
|
||||
debts: Coin[]
|
||||
lends: Coin[]
|
||||
vaults: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
|
||||
}
|
||||
|
1
src/types/interfaces/option.d.ts
vendored
Normal file
1
src/types/interfaces/option.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
type Option = import('cosmjs/stargate').Coin | { value: string; label: string }
|
1
src/types/interfaces/responses.d.ts
vendored
1
src/types/interfaces/responses.d.ts
vendored
@ -3,6 +3,7 @@ interface AccountResponse {
|
||||
deposits: Coin[]
|
||||
debts: Coin[]
|
||||
lends: Coin[]
|
||||
vaults: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
|
||||
}
|
||||
|
||||
interface MarketResponse {
|
||||
|
13
src/types/interfaces/vaults.d.ts
vendored
13
src/types/interfaces/vaults.d.ts
vendored
@ -33,3 +33,16 @@ interface VaultConfig extends VaultMetaData {
|
||||
interface Vault extends VaultConfig {
|
||||
apy: number | null
|
||||
}
|
||||
|
||||
interface ActiveVault extends Vault {
|
||||
status: 'active' | 'unlocking' | 'unlocked'
|
||||
amounts: {
|
||||
primary: number
|
||||
secondary: number
|
||||
}
|
||||
values: {
|
||||
primary: number
|
||||
secondary: number
|
||||
}
|
||||
unlocksAt?: number
|
||||
}
|
||||
|
@ -1,17 +1,23 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import { BN } from './helpers'
|
||||
|
||||
export const calculateAccountBalance = (account: Account, prices: Coin[]) => {
|
||||
const totalDepositValue = account.deposits.reduce((acc, deposit) => {
|
||||
const price = prices.find((price) => price.denom === deposit.denom)?.amount ?? 0
|
||||
const depositValue = new BigNumber(deposit.amount).multipliedBy(price)
|
||||
const depositValue = BN(deposit.amount).multipliedBy(price)
|
||||
return acc.plus(depositValue)
|
||||
}, new BigNumber(0))
|
||||
}, BN(0))
|
||||
|
||||
const totalDebtValue = account.debts.reduce((acc, debt) => {
|
||||
const price = prices.find((price) => price.denom === debt.denom)?.amount ?? 0
|
||||
const debtValue = new BigNumber(debt.amount).multipliedBy(price)
|
||||
const debtValue = BN(debt.amount).multipliedBy(price)
|
||||
return acc.plus(debtValue)
|
||||
}, new BigNumber(0))
|
||||
}, BN(0))
|
||||
|
||||
return totalDepositValue.minus(totalDebtValue).toNumber()
|
||||
}
|
||||
|
||||
export function getAmount(denom: string, coins: Coin[]) {
|
||||
return BN(coins.find((asset) => asset.denom === denom)?.amount ?? 0)
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import { getMarketAssets } from './assets'
|
||||
import { BN } from './helpers'
|
||||
|
||||
export function truncate(text = '', [h, t]: [number, number] = [6, 6]): string {
|
||||
const head = text.slice(0, h)
|
||||
@ -33,7 +32,7 @@ export const formatValue = (amount: number | string, options?: FormatOptions): s
|
||||
numberOfZeroDecimals = decimals.length
|
||||
}
|
||||
}
|
||||
let convertedAmount: number | string = new BigNumber(amount)
|
||||
let convertedAmount: number | string = BN(amount)
|
||||
.dividedBy(10 ** (options?.decimals ?? 0))
|
||||
.toNumber()
|
||||
|
||||
@ -143,3 +142,15 @@ export const convertPercentage = (percent: number) => {
|
||||
if (percent !== 0 && percent < 0.01) percentage = 0.01
|
||||
return Number(formatValue(percentage, { minDecimals: 0, maxDecimals: 0 }))
|
||||
}
|
||||
|
||||
export function magnify(value: number, asset: Asset) {
|
||||
return value === 0 ? 0 : BN(value).shiftedBy(asset.decimals).toNumber()
|
||||
}
|
||||
|
||||
export function demagnify(amount: number, asset: Asset) {
|
||||
return amount === 0
|
||||
? 0
|
||||
: BN(amount)
|
||||
.shiftedBy(-1 * asset.decimals)
|
||||
.toNumber()
|
||||
}
|
||||
|
5
src/utils/helpers.ts
Normal file
5
src/utils/helpers.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
export function BN(n: BigNumber.Value) {
|
||||
return new BigNumber(n)
|
||||
}
|
@ -8,6 +8,7 @@ export function resolvePositionResponse(response: AccountResponse): Account {
|
||||
deposits: response.deposits,
|
||||
debts: response.debts,
|
||||
lends: response.lends,
|
||||
vaults: response.vaults,
|
||||
}
|
||||
}
|
||||
|
||||
|
201
yarn.lock
201
yarn.lock
@ -1901,6 +1901,11 @@
|
||||
resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.2.tgz"
|
||||
integrity sha512-9anpBMM9mEgZN4wr2v8wHJI2/u5TnnggewRN6OlvXTTnuVyoY19X6rOv9XTqKRw6dcGKwZsBi8n0kDE2I5i4VA==
|
||||
|
||||
"@graphql-typed-document-node/core@^3.2.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
|
||||
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
|
||||
|
||||
"@humanwhocodes/config-array@^0.11.8":
|
||||
version "0.11.8"
|
||||
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz"
|
||||
@ -2264,10 +2269,10 @@
|
||||
big-integer "^1.6.48"
|
||||
utility-types "^3.10.0"
|
||||
|
||||
"@marsprotocol/wallet-connector@^1.5.3":
|
||||
version "1.5.3"
|
||||
resolved "https://registry.yarnpkg.com/@marsprotocol/wallet-connector/-/wallet-connector-1.5.3.tgz#b77c814f7e5b824b9b8babf4f5a0a6879e90ffba"
|
||||
integrity sha512-XBAy+Kn0kVoE+l/jif7Q9Q/7uC3rTVuFaDMmL7ks2p85ImCO0HQFpvZKv901jguDM230vKI+Hs942CszaS21WA==
|
||||
"@marsprotocol/wallet-connector@^1.5.4":
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@marsprotocol/wallet-connector/-/wallet-connector-1.5.4.tgz#81d0e213e9e9c4fff78781cd49fd627b75076fe6"
|
||||
integrity sha512-RkMVyFkSEDrDcb8SnBnOA3wVWT/zaNdzpgbLpO+Zn8vsYGea8uugafSKCeodKNi83/Q2rBEbTUqMJeyLYI9OMQ==
|
||||
dependencies:
|
||||
"@cosmjs/cosmwasm-stargate" "^0.30.1"
|
||||
"@cosmjs/encoding" "^0.30.1"
|
||||
@ -2597,26 +2602,26 @@
|
||||
"@noble/hashes" "~1.2.0"
|
||||
"@scure/base" "~1.1.0"
|
||||
|
||||
"@sentry-internal/tracing@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.47.0.tgz#45e92eb4c8d049d93bd4fab961eaa38a4fb680f3"
|
||||
integrity sha512-udpHnCzF8DQsWf0gQwd0XFGp6Y8MOiwnl8vGt2ohqZGS3m1+IxoRLXsSkD8qmvN6KKDnwbaAvYnK0z0L+AW95g==
|
||||
"@sentry-internal/tracing@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.48.0.tgz#d0c1eac1c046fda5c79d16dc1c918fee3bae3e9d"
|
||||
integrity sha512-MFAPDTrvCtfSm0/Zbmx7HA0Q5uCfRadOUpN8Y8rP1ndz+329h2kA3mZRCuC+3/aXL11zs2CHUhcAkGjwH2vogg==
|
||||
dependencies:
|
||||
"@sentry/core" "7.47.0"
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/utils" "7.47.0"
|
||||
"@sentry/core" "7.48.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
"@sentry/utils" "7.48.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/browser@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.47.0.tgz#c0d10f348d1fb9336c3ef8fa2f6638f26d4c17a8"
|
||||
integrity sha512-L0t07kS/G1UGVZ9fpD6HLuaX8vVBqAGWgu+1uweXthYozu/N7ZAsakjU/Ozu6FSXj1mO3NOJZhOn/goIZLSj5A==
|
||||
"@sentry/browser@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.48.0.tgz#03f39bec6949ff48e343c5862c5d54dfd4a2f9ff"
|
||||
integrity sha512-tdx/2nhuiykncmXFlV4Dpp+Hxgt/v31LiyXE79IcM560wc+QmWKtzoW9azBWQ0xt5KOO3ERMib9qPE4/ql1/EQ==
|
||||
dependencies:
|
||||
"@sentry-internal/tracing" "7.47.0"
|
||||
"@sentry/core" "7.47.0"
|
||||
"@sentry/replay" "7.47.0"
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/utils" "7.47.0"
|
||||
"@sentry-internal/tracing" "7.48.0"
|
||||
"@sentry/core" "7.48.0"
|
||||
"@sentry/replay" "7.48.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
"@sentry/utils" "7.48.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/cli@^1.74.6":
|
||||
@ -2631,88 +2636,88 @@
|
||||
proxy-from-env "^1.1.0"
|
||||
which "^2.0.2"
|
||||
|
||||
"@sentry/core@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.47.0.tgz#6a723d96f64009a9c1b9bc44e259956b7eca0a3f"
|
||||
integrity sha512-EFhZhKdMu7wKmWYZwbgTi8FNZ7Fq+HdlXiZWNz51Bqe3pHmfAkdHtAEs0Buo0v623MKA0CA4EjXIazGUM34XTg==
|
||||
"@sentry/core@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.48.0.tgz#1a5ec347ab7212d73a99583c2e64989e34e3263a"
|
||||
integrity sha512-8FYuJTMpyuxRZvlen3gQ3rpOtVInSDmSyXqWEhCLuG/w34AtWoTiW7G516rsAAh6Hy1TP91GooMWbonP3XQNTQ==
|
||||
dependencies:
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/utils" "7.47.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
"@sentry/utils" "7.48.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/integrations@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.47.0.tgz#b952cc910e92e9235f42151f7260471b55b10cdf"
|
||||
integrity sha512-PUSeBYI3fCOswn+K+PLjtl2epr8/ceqebWqVcxHclczSY3EOZE+osznDFgZmeVgrHavsgfE4oFVqJeFvDJwCog==
|
||||
"@sentry/integrations@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.48.0.tgz#1f1c3fd0735b3c40944a42fb45e3e0ca40f77d6d"
|
||||
integrity sha512-yzbJopVu1UHFXRDv236o5hSEUtqeP45T9uSVbAhKnH5meKWunK7MKvhFvQjhcfvlUVibYrewoVztQP2hrpxgfw==
|
||||
dependencies:
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/utils" "7.47.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
"@sentry/utils" "7.48.0"
|
||||
localforage "^1.8.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/nextjs@^7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-7.47.0.tgz#72c0a3ecea87940db3eadeb94f6b47eb8f3ead64"
|
||||
integrity sha512-KcvN0l5N819LdX7iFUrZjYTX5ITm8lXmiOSyhiLTZBm68ZZbmX2TMrMMlGCLuc0qBZQolu11u6gVQSfTaZPQ9Q==
|
||||
"@sentry/nextjs@^7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-7.48.0.tgz#e1c606eb31a907cdc00f96baebffdba3560405b5"
|
||||
integrity sha512-SLWkd1ZB27uK21QkUiIBEUgvhaMFMx8V5MO2+IlGluJKUdd06IgYAOsS0kjwQc34Ow6D0qowy8iScmtHebgQew==
|
||||
dependencies:
|
||||
"@rollup/plugin-commonjs" "24.0.0"
|
||||
"@sentry/core" "7.47.0"
|
||||
"@sentry/integrations" "7.47.0"
|
||||
"@sentry/node" "7.47.0"
|
||||
"@sentry/react" "7.47.0"
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/utils" "7.47.0"
|
||||
"@sentry/core" "7.48.0"
|
||||
"@sentry/integrations" "7.48.0"
|
||||
"@sentry/node" "7.48.0"
|
||||
"@sentry/react" "7.48.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
"@sentry/utils" "7.48.0"
|
||||
"@sentry/webpack-plugin" "1.20.0"
|
||||
chalk "3.0.0"
|
||||
rollup "2.78.0"
|
||||
stacktrace-parser "^0.1.10"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/node@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.47.0.tgz#2c1a8c4777eaf20232fc16d3aa2f26f3fd04bfd1"
|
||||
integrity sha512-LTg2r5EV9yh4GLYDF+ViSltR9LLj/pcvk8YhANJcMO3Fp//xh8njcdU0FC2yNthUREawYDzAsVzLyCYJfV0H1A==
|
||||
"@sentry/node@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.48.0.tgz#b2f15502b77796bf7bcaa29f2e9ce1420f7c49d1"
|
||||
integrity sha512-DJyyZaVhv/pUzJPof7es6zYDHeWbNqE0T3tQfLCkShdyfR+Ew8In8W/x2s7S8vq0cfRq0rqv1E6B2/HpVdYO7g==
|
||||
dependencies:
|
||||
"@sentry-internal/tracing" "7.47.0"
|
||||
"@sentry/core" "7.47.0"
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/utils" "7.47.0"
|
||||
"@sentry-internal/tracing" "7.48.0"
|
||||
"@sentry/core" "7.48.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
"@sentry/utils" "7.48.0"
|
||||
cookie "^0.4.1"
|
||||
https-proxy-agent "^5.0.0"
|
||||
lru_map "^0.3.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.47.0.tgz#9b0b937465a4e7fc6c3bde90ef9d0be2ef708b78"
|
||||
integrity sha512-Qy6OnlE8FivKOLo0YE7tkr+G5fLmEOkpPxj179wbY/N8kp/ALkqbVdcOrZW7AL6HCc0lphhj+0SB+tpwoPEsiQ==
|
||||
"@sentry/react@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.48.0.tgz#3c160d7dcccc7581b57c430fccfe482c912c3f0d"
|
||||
integrity sha512-E2HF0njufOI/BWktXfIiPNIh0dh7la9uQmDlYiFAK8MnlW4OOjw4rRJV2qkxKQCYdO9WB+T460DVw102Z/MyUA==
|
||||
dependencies:
|
||||
"@sentry/browser" "7.47.0"
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/utils" "7.47.0"
|
||||
"@sentry/browser" "7.48.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
"@sentry/utils" "7.48.0"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/replay@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.47.0.tgz#d2fc8fd3be2360950497426035d1ba0bd8a97b8f"
|
||||
integrity sha512-BFpVZVmwlezZ83y0L43TCTJY142Fxh+z+qZSwTag5HlhmIpBKw/WKg06ajOhrYJbCBkhHmeOvyKkxX0jnc39ZA==
|
||||
"@sentry/replay@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.48.0.tgz#ca8f1543bad4717dcd65739bf1256a1933bba757"
|
||||
integrity sha512-8fRHMGJ0NJeIZi6UucxUTvfDPaBa7+jU1kCTLjCcuH3X/UVz5PtGLMtFSO5U8HP+mUDlPs97MP1uoDvMa4S2Ng==
|
||||
dependencies:
|
||||
"@sentry/core" "7.47.0"
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/utils" "7.47.0"
|
||||
"@sentry/core" "7.48.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
"@sentry/utils" "7.48.0"
|
||||
|
||||
"@sentry/types@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.47.0.tgz#fd07dbec11a26ae861532a9abe75bd31663ca09b"
|
||||
integrity sha512-GxXocplN0j1+uczovHrfkykl9wvkamDtWxlPUQgyGlbLGZn+UH1Y79D4D58COaFWGEZdSNKr62gZAjfEYu9nQA==
|
||||
"@sentry/types@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.48.0.tgz#57f3c9cf331a5621e82dda04eefcf8c19ee42bc9"
|
||||
integrity sha512-kkAszZwQ5/v4n7Yyw/DPNRWx7h724mVNRGZIJa9ggUMvTgMe7UKCZZ5wfQmYiKVlGbwd9pxXAcP8Oq15EbByFQ==
|
||||
|
||||
"@sentry/utils@7.47.0":
|
||||
version "7.47.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.47.0.tgz#e62fdede15e45387b40c9fa135feba48f0960826"
|
||||
integrity sha512-A89SaOLp6XeZfByeYo2C8Ecye/YAtk/gENuyOUhQEdMulI6mZdjqtHAp7pTMVgkBc/YNARVuoa+kR/IdRrTPkQ==
|
||||
"@sentry/utils@7.48.0":
|
||||
version "7.48.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.48.0.tgz#2866975ea8899aba35b083dd0558cbbe29ee8de1"
|
||||
integrity sha512-d977sghkFVMfld0LrEyyY2gYrfayLPdDEpUDT+hg5y79r7zZDCFyHtdB86699E5K89MwDZahW7Erk+a1nk4x5w==
|
||||
dependencies:
|
||||
"@sentry/types" "7.47.0"
|
||||
"@sentry/types" "7.48.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/webpack-plugin@1.20.0":
|
||||
@ -3032,10 +3037,10 @@
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/react@18.0.33":
|
||||
version "18.0.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.33.tgz#a1575160cb4376787c2f5fe0312302f824baa61e"
|
||||
integrity sha512-sHxzVxeanvQyQ1lr8NSHaj0kDzcNiGpILEVt69g9S31/7PfMvNCKLKcsHw4lYKjs3cGNJjXSP4mYzX43QlnjNA==
|
||||
"@types/react@18.0.35":
|
||||
version "18.0.35"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.35.tgz#192061cb1044fe01f2d3a94272cd35dd50502741"
|
||||
integrity sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
@ -4985,11 +4990,6 @@ exenv@^1.2.0:
|
||||
resolved "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz"
|
||||
integrity sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==
|
||||
|
||||
extract-files@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz"
|
||||
integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==
|
||||
|
||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
|
||||
@ -5093,15 +5093,6 @@ for-each@^0.3.3:
|
||||
dependencies:
|
||||
is-callable "^1.1.3"
|
||||
|
||||
form-data@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz"
|
||||
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
|
||||
dependencies:
|
||||
asynckit "^0.4.0"
|
||||
combined-stream "^1.0.8"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
form-data@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz"
|
||||
@ -5318,15 +5309,13 @@ grapheme-splitter@^1.0.4:
|
||||
resolved "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz"
|
||||
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
|
||||
|
||||
graphql-request@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.npmjs.org/graphql-request/-/graphql-request-5.2.0.tgz"
|
||||
integrity sha512-pLhKIvnMyBERL0dtFI3medKqWOz/RhHdcgbZ+hMMIb32mEPa5MJSzS4AuXxfI4sRAu6JVVk5tvXuGfCWl9JYWQ==
|
||||
graphql-request@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-6.0.0.tgz#9c8b6a0c341f289e049936d03cc9205300faae1c"
|
||||
integrity sha512-2BmHTuglonjZvmNVw6ZzCfFlW/qkIPds0f+Qdi/Lvjsl3whJg2uvHmSvHnLWhUTEw6zcxPYAHiZoPvSVKOZ7Jw==
|
||||
dependencies:
|
||||
"@graphql-typed-document-node/core" "^3.1.1"
|
||||
"@graphql-typed-document-node/core" "^3.2.0"
|
||||
cross-fetch "^3.1.5"
|
||||
extract-files "^9.0.0"
|
||||
form-data "^3.0.0"
|
||||
|
||||
graphql-tag@^2.12.6:
|
||||
version "2.12.6"
|
||||
@ -6546,10 +6535,10 @@ prelude-ls@^1.2.1:
|
||||
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
|
||||
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
|
||||
|
||||
prettier-plugin-tailwindcss@^0.2.6:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.6.tgz#d57e3d960f1a968bf16d2a5846e94f95d6b8add2"
|
||||
integrity sha512-F+7XCl9RLF/LPrGdUMHWpsT6TM31JraonAUyE6eBmpqymFvDwyl0ETHsKFHP1NG+sEfv8bmKqnTxEbWQbHPlBA==
|
||||
prettier-plugin-tailwindcss@^0.2.7:
|
||||
version "0.2.7"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.7.tgz#2314d728cce9c9699ced41a01253eb48b4218da5"
|
||||
integrity sha512-jQopIOgjLpX+y8HeD56XZw7onupRTC0cw7eKKUimI7vhjkPF5/1ltW5LyqaPtSyc8HvEpvNZsvvsGFa2qpa59w==
|
||||
|
||||
prettier@^2.8.7:
|
||||
version "2.8.7"
|
||||
@ -7351,10 +7340,10 @@ svgo@^3.0.2:
|
||||
csso "^5.0.5"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
swr@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/swr/-/swr-2.1.2.tgz#15841cf5bbb8b20f24e2408193f616a41b6734a0"
|
||||
integrity sha512-ocfaD2rnYZKqTDplCEX2bH5Z1++n2JSej9oYi7hVfXXWYm+0RP+H6fVrogWB0mtMclv1guk9kEnAzNLygOy9Hw==
|
||||
swr@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/swr/-/swr-2.1.3.tgz#ae3608d4dc19f75121e0b51d1982735fbf02f52e"
|
||||
integrity sha512-g3ApxIM4Fjbd6vvEAlW60hJlKcYxHb+wtehogTygrh6Jsw7wNagv9m4Oj5Gq6zvvZw0tcyhVGL9L0oISvl3sUw==
|
||||
dependencies:
|
||||
use-sync-external-store "^1.2.0"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user