Hls latest (#637)

* env: enable HLS

* feat: added background and orb fading

* tidy: updated the token logos (#629)

* tidy: updated the token logos

* feat: added dydx

* fix: increase load spead of trading charts

* feat: first version of the UI shift

* Deployment for HLS testing

*  add APY to HLS staking

*  add APY account overview and summary

* fix: fixed the intro component visibility

*  add warning messages HLS

* fix: menu order

*  implement live APRs

* auto-select first account, add no balance message

* enable tabs for hls, fix net APY for deposit

* fix button for hls, sorting apy and console warnings

* disable feature flag HLS

* fix slider

* update routing

---------

Co-authored-by: Linkie Link <linkielink.dev@gmail.com>
This commit is contained in:
Bob van der Helm 2023-11-16 11:16:16 +01:00 committed by GitHub
parent 8f5724c79d
commit 0f48e4bd27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
81 changed files with 1426 additions and 201 deletions

View File

@ -12,6 +12,7 @@ NEXT_PUBLIC_CHAIN_ID=osmosis-1
NEXT_PUBLIC_RPC=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-rpc-front/
NEXT_PUBLIC_GQL=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-hive-front/graphql
NEXT_PUBLIC_REST=https://osmosis-node.marsprotocol.io/GGSFGSFGFG34/osmosis-lcd-front/
NEXT_PUBLIC_STRIDE_APRS=https://edge.stride.zone/api/stake-stats
# COMMON #
@ -33,4 +34,4 @@ NEXT_PUBLIC_CANDLES_ENDPOINT_PYTH=https://benchmarks.pyth.network
NEXT_PUBLIC_WALLET_CONNECT_ID=d93fdffb159bae5ec87d8fee4cdbb045
CHARTING_LIBRARY_REPOSITORY=github.com/tradingview/charting_library
CHARTING_LIBRARY_ACCESS_TOKEN=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
CHARTING_LIBRARY_USERNAME=mars-git-demo
CHARTING_LIBRARY_USERNAME=mars-git-demo

View File

@ -1,4 +1,4 @@
import { fireEvent, render, screen } from '@testing-library/react'
import { fireEvent, render } from '@testing-library/react'
import BigNumber from 'bignumber.js'
import TokenInput from 'components/TokenInput'
@ -27,59 +27,70 @@ describe('<TokenInput />', () => {
})
it('should render', () => {
const { container } = render(<TokenInput {...defaultProps} />)
const { container } = render(<TokenInput warningMessages={[]} {...defaultProps} />)
expect(container).toBeInTheDocument()
})
it('should handle `className` prop correctly', () => {
const testClass = 'test-class'
const { getByTestId } = render(<TokenInput {...defaultProps} className={testClass} />)
const { getByTestId } = render(
<TokenInput warningMessages={[]} {...defaultProps} className={testClass} />,
)
expect(getByTestId('token-input-component')).toHaveClass(testClass)
})
it('should handle `disabled` prop correctly', () => {
const { getByTestId } = render(<TokenInput {...defaultProps} disabled={true} />)
const { getByTestId } = render(
<TokenInput warningMessages={[]} {...defaultProps} disabled={true} />,
)
expect(getByTestId('token-input-component')).toHaveClass('pointer-events-none', 'opacity-50')
})
it('should handle `maxText` prop correctly', () => {
const { getByTestId } = render(<TokenInput {...defaultProps} maxText='Max' />)
const { getByTestId } = render(
<TokenInput warningMessages={[]} {...defaultProps} maxText='Max' />,
)
expect(getByTestId('token-input-max-button')).toBeInTheDocument()
})
it('should handle `warning` prop correctly', () => {
const { getByTestId } = render(<TokenInput {...defaultProps} warning='Warning' />)
expect(getByTestId('token-input-wrapper')).toHaveClass('border-warning')
})
describe('should render the max button', () => {
it('when `maxText` prop is defined', () => {
const { getByTestId } = render(<TokenInput {...defaultProps} maxText='Max' />)
const { getByTestId } = render(
<TokenInput warningMessages={[]} {...defaultProps} maxText='Max' />,
)
expect(getByTestId('token-input-max-button')).toBeInTheDocument()
})
it('not when `maxText` prop is undefined', () => {
const { queryByTestId } = render(<TokenInput {...defaultProps} />)
const { queryByTestId } = render(<TokenInput warningMessages={[]} {...defaultProps} />)
expect(queryByTestId('token-input-max-button')).not.toBeInTheDocument()
})
})
describe('should render <Select />', () => {
it('when `hasSelect` prop is true and balances is defined', () => {
const { getByTestId } = render(<TokenInput {...defaultProps} balances={[]} hasSelect />)
const { getByTestId } = render(
<TokenInput warningMessages={[]} {...defaultProps} balances={[]} hasSelect />,
)
expect(getByTestId('select-component')).toBeInTheDocument()
})
it('not when `hasSelect` prop is true and balances is not defined', () => {
const { queryByTestId } = render(<TokenInput {...defaultProps} hasSelect />)
const { queryByTestId } = render(
<TokenInput warningMessages={[]} {...defaultProps} hasSelect />,
)
expect(queryByTestId('select-component')).not.toBeInTheDocument()
})
it('not when `hasSelect` prop is false and balances is defined', () => {
const { queryByTestId } = render(<TokenInput {...defaultProps} balances={[]} />)
const { queryByTestId } = render(
<TokenInput warningMessages={[]} {...defaultProps} balances={[]} />,
)
expect(queryByTestId('select-component')).not.toBeInTheDocument()
})
})
it('should call onMaxBtnClick when the user clicks on max button', () => {
const { getByTestId } = render(<TokenInput {...defaultProps} maxText='max' />)
const { getByTestId } = render(
<TokenInput warningMessages={[]} {...defaultProps} maxText='max' />,
)
const maxBtn = getByTestId('token-input-max-button')
fireEvent.click(maxBtn)
expect(defaultProps.onChange).toBeCalledWith(defaultProps.max)

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -60,3 +60,4 @@ export const totalDepositCache: Cache<TotalDepositResponse> = new Map()
export const allParamsCache: Cache<AssetParamsBaseForAddr[]> = new Map()
export const underlyingDebtCache: Cache<string> = new Map()
export const previewDepositCache: Cache<{ vaultAddress: string; amount: string }> = new Map()
export const stakingAprCache: Cache<StakingApr[]> = new Map()

22
src/api/hls/getAprs.ts Normal file
View File

@ -0,0 +1,22 @@
import { cacheFn, stakingAprCache } from 'api/cache'
import { ENV } from 'constants/env'
export default async function getStakingAprs() {
try {
return cacheFn(() => fetchAprs(), stakingAprCache, `stakingAprs`)
} catch (error) {
throw error
}
}
interface StakingAprResponse {
stats: StakingApr[]
}
async function fetchAprs(): Promise<StakingApr[]> {
const url = ENV.STRIDE_APRS
const response = await fetch(url)
const body = (await response.json()) as StakingAprResponse
return body.stats
}

View File

@ -1,6 +1,7 @@
import { getParamsQueryClient } from 'api/cosmwasm-client'
import getStakingAprs from 'api/hls/getAprs'
import getAssetParams from 'api/params/getAssetParams'
import { getStakingAssets } from 'utils/assets'
import { getAssetByDenom, getStakingAssets } from 'utils/assets'
import { BN } from 'utils/helpers'
import { resolveHLSStrategies } from 'utils/resolvers'
@ -17,8 +18,11 @@ export default async function getHLSStakingAssets() {
client.totalDeposit({ denom: strategy.denoms.deposit }),
)
const aprs = await getStakingAprs()
return Promise.all(depositCaps$).then((depositCaps) => {
return depositCaps.map((depositCap, index) => {
const borrowSymbol = getAssetByDenom(strategies[index].denoms.borrow)?.symbol
return {
...strategies[index],
depositCap: {
@ -26,7 +30,7 @@ export default async function getHLSStakingAssets() {
used: BN(depositCap.amount),
max: BN(depositCap.cap),
},
apy: 18, // TODO: Actually implement the APY here!
apy: (aprs.find((stakingApr) => stakingApr.denom === borrowSymbol)?.strideYield || 0) * 100,
} as HLSStrategy
})
})

View File

@ -13,7 +13,7 @@ interface Props {
export default function Apr(props: Props) {
const { markets, type, denom, apy } = props
if (type === 'deposits') return <p className='w-full text-xs text-right number'>&ndash;</p>
if (apy === 0) return <p className='w-full text-xs text-right number'>&ndash;</p>
const isEnabled = markets.find(byDenom(denom))?.borrowEnabled ?? false
return (

View File

@ -14,6 +14,7 @@ import { getPage, getRoute } from 'utils/route'
interface Props {
account: Account
isHls?: boolean
lendingData: LendingMarketTableData[]
borrowingData: BorrowMarketTableData[]
hideCard?: boolean
@ -32,6 +33,7 @@ export default function AccountBalancesTable(props: Props) {
updatedAccount,
lendingData,
borrowingData,
isHls: props.isHls,
})
const columns = useAccountBalancesColumns()
@ -72,13 +74,13 @@ export default function AccountBalancesTable(props: Props) {
return (
<Table
title='Balances'
isBalancesTable={true}
columns={columns}
data={accountBalanceData}
tableBodyClassName={classNames(tableBodyClassName, 'text-white/60')}
initialSorting={[]}
spacingClassName='p-2'
hideCard={hideCard}
isBalancesTable
/>
)
}

View File

@ -5,6 +5,7 @@ import {
getVaultAccountBalanceRow,
} from 'components/Account/AccountBalancesTable/functions'
import { ASSETS } from 'constants/assets'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import { byDenom } from 'utils/array'
import { convertLiquidityRateToAPR } from 'utils/formatters'
@ -12,6 +13,7 @@ import { convertAprToApy } from 'utils/parsers'
interface Props {
account: Account
isHls?: boolean
updatedAccount?: Account
lendingData: LendingMarketTableData[]
borrowingData: BorrowMarketTableData[]
@ -20,6 +22,7 @@ interface Props {
export default function useAccountBalanceData(props: Props) {
const { account, updatedAccount, lendingData, borrowingData } = props
const { data: hlsStrategies } = useHLSStakingAssets()
const { data: prices } = usePrices()
return useMemo<AccountBalanceRow[]>(() => {
@ -33,7 +36,9 @@ export default function useAccountBalanceData(props: Props) {
accountDeposits.forEach((deposit) => {
const asset = ASSETS.find(byDenom(deposit.denom))
if (!asset) return
const apy = 0
const apy = props.isHls
? hlsStrategies.find((strategy) => strategy.denoms.deposit === asset.denom)?.apy ?? 0
: 0
const prevDeposit = updatedAccount ? account?.deposits.find(byDenom(deposit.denom)) : deposit
deposits.push(getAssetAccountBalanceRow('deposits', asset, prices, deposit, apy, prevDeposit))
})
@ -68,5 +73,5 @@ export default function useAccountBalanceData(props: Props) {
return getAssetAccountBalanceRow('borrowing', asset, prices, debt, apy, prevDebt)
})
return [...deposits, ...lends, ...vaults, ...debts]
}, [prices, account, updatedAccount, borrowingData, lendingData])
}, [updatedAccount, account, props.isHls, hlsStrategies, prices, lendingData, borrowingData])
}

View File

@ -9,6 +9,7 @@ import Text from 'components/Text'
import { BN_ZERO, MAX_AMOUNT_DECIMALS } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
@ -21,6 +22,7 @@ import {
interface Props {
account: Account
isHls?: boolean
}
interface ItemProps {
@ -37,6 +39,7 @@ export default function AccountComposition(props: Props) {
const { account } = props
const hasChanged = !!updatedAccount
const { data: prices } = usePrices()
const { data: hlsStrategies } = useHLSStakingAssets()
const { data } = useBorrowMarketAssetsTableData(false)
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
@ -72,15 +75,30 @@ export default function AccountComposition(props: Props) {
)
const apr = useMemo(
() => calculateAccountApr(account, borrowAssetsData, lendingAssetsData, prices),
[account, borrowAssetsData, lendingAssetsData, prices],
() =>
calculateAccountApr(
account,
borrowAssetsData,
lendingAssetsData,
prices,
hlsStrategies,
props.isHls,
),
[account, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, props.isHls],
)
const updatedApr = useMemo(
() =>
updatedAccount
? calculateAccountApr(updatedAccount, borrowAssetsData, lendingAssetsData, prices)
? calculateAccountApr(
updatedAccount,
borrowAssetsData,
lendingAssetsData,
prices,
hlsStrategies,
props.isHls,
)
: BN_ZERO,
[updatedAccount, borrowAssetsData, lendingAssetsData, prices],
[updatedAccount, borrowAssetsData, lendingAssetsData, prices, hlsStrategies, props.isHls],
)
return (

View File

@ -23,6 +23,7 @@ import useAccounts from 'hooks/useAccounts'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
@ -36,14 +37,16 @@ import {
export default function AccountDetailsController() {
const address = useStore((s) => s.address)
const isHLS = useStore((s) => s.isHLS)
const { data: accounts, isLoading } = useAccounts('default', address)
const { data: accountIds } = useAccountIds(address, false)
const accountId = useAccountId()
const account = useCurrentAccount()
const focusComponent = useStore((s) => s.focusComponent)
const isOwnAccount = accountId && accountIds?.includes(accountId)
if (!address || focusComponent || !isOwnAccount) return null
if (!address || focusComponent || !isOwnAccount || isHLS) return null
if ((isLoading && accountId && !focusComponent) || !account) return <Skeleton />
@ -57,6 +60,7 @@ interface Props {
function AccountDetails(props: Props) {
const { account } = props
const location = useLocation()
const { data: hlsStrategies } = useHLSStakingAssets()
const [reduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
DEFAULT_SETTINGS.reduceMotion,
@ -94,8 +98,15 @@ function AccountDetails(props: Props) {
)
const apr = useMemo(
() =>
calculateAccountApr(updatedAccount ?? account, borrowAssetsData, lendingAssetsData, prices),
[account, borrowAssetsData, lendingAssetsData, prices, updatedAccount],
calculateAccountApr(
updatedAccount ?? account,
borrowAssetsData,
lendingAssetsData,
prices,
hlsStrategies,
account.kind === 'high_levered_strategy',
),
[account, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, updatedAccount],
)
const isFullWidth = location.pathname.includes('trade') || location.pathname === '/'

View File

@ -181,6 +181,7 @@ export default function AccountFundContent(props: Props) {
balances={balances}
maxText='Max'
disabled={isConfirming}
warningMessages={[]}
/>
</div>
)

View File

@ -8,6 +8,7 @@ import SwitchAutoLend from 'components/Switch/SwitchAutoLend'
import useAccount from 'hooks/useAccount'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
@ -23,6 +24,8 @@ export default function AccountStats(props: Props) {
const { accountId, isActive, setShowMenu } = props
const { data: account } = useAccount(accountId)
const { data: prices } = usePrices()
const { data: hlsStrategies } = useHLSStakingAssets()
const positionBalance = useMemo(
() => (!account ? null : calculateAccountBalanceValue(account, prices)),
[account, prices],
@ -38,8 +41,17 @@ export default function AccountStats(props: Props) {
)
const apr = useMemo(
() =>
!account ? null : calculateAccountApr(account, borrowAssetsData, lendingAssetsData, prices),
[account, borrowAssetsData, lendingAssetsData, prices],
!account
? null
: calculateAccountApr(
account,
borrowAssetsData,
lendingAssetsData,
prices,
hlsStrategies,
account.kind === 'high_levered_strategy',
),
[account, borrowAssetsData, hlsStrategies, lendingAssetsData, prices],
)
const deleteAccountHandler = useCallback(() => {

View File

@ -27,7 +27,7 @@ export default function AccountList(props: Props) {
const { pathname } = useLocation()
const currentAccountId = useAccountId()
const address = useStore((s) => s.address)
const { data: accountIds } = useAccountIds(address)
const { data: accountIds } = useAccountIds(address, true, true)
useEffect(() => {
if (!currentAccountId) return

View File

@ -25,6 +25,7 @@ import { calculateAccountBalanceValue, calculateAccountLeverage } from 'utils/ac
interface Props {
account: Account
isHls?: boolean
}
export default function AccountSummary(props: Props) {
@ -124,7 +125,9 @@ export default function AccountSummary(props: Props) {
{
title: `Credit Account ${props.account.id} Composition`,
renderContent: () =>
props.account ? <AccountComposition account={props.account} /> : null,
props.account ? (
<AccountComposition account={props.account} isHls={props.isHls} />
) : null,
isOpen: accountSummaryTabs[0],
toggleOpen: (index: number) => handleToggle(index),
renderSubTitle: () => <></>,
@ -138,6 +141,7 @@ export default function AccountSummary(props: Props) {
borrowingData={borrowAssetsData}
lendingData={lendingAssetsData}
hideCard
isHls={props.isHls}
/>
) : null,
isOpen: accountSummaryTabs[1],

View File

@ -1,17 +1,36 @@
import classNames from 'classnames'
import { useLocation } from 'react-router-dom'
import { useEffect, useMemo } from 'react'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/useLocalStorage'
import useStore from 'store'
import { getPage } from 'utils/route'
export default function Background() {
const [reduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
DEFAULT_SETTINGS.reduceMotion,
)
const { pathname } = useLocation()
const page = getPage(pathname)
const isHLS = useMemo(() => page.split('-')[0] === 'hls', [page])
useEffect(() => {
useStore.setState({ isHLS })
}, [isHLS])
return (
<div className='background pointer-events-none fixed inset-0 h-full w-full overflow-hidden bg-body'>
<div
className={classNames(
'fixed inset-0',
'w-full h-full',
'overflow-hidden pointer-events-none background ',
isHLS ? 'bg-body-hls' : 'bg-body',
!reduceMotion && 'transition-bg duration-1000 delay-300',
)}
>
<div
className={classNames(
'fixed',
@ -19,9 +38,11 @@ export default function Background() {
'min-h-[150px] min-w-[150px]',
'max-h-[500px] max-w-[500px]',
'left-[-10vw] top-[-10vw]',
'bg-orb-primary blur-orb-primary ',
'blur-orb-primary',
isHLS ? ' bg-orb-primary-hls' : 'bg-orb-primary',
'translate-x-0 translate-y-0 rounded-full opacity-20',
!reduceMotion && 'animate-[float_120s_ease-in-out_infinite_2s]',
!reduceMotion && 'transition-bg duration-1000 delay-300',
)}
/>
<div
@ -30,10 +51,11 @@ export default function Background() {
'h-[40vw] w-[40vw]',
'min-h-[400px] min-w-[400px]',
'max-h-[1000px] max-w-[1000px]',
'bottom-[-10vw] right-[-8vw]',
'bg-orb-secondary blur-orb-secondary',
'bottom-[-20vw] right-[-10vw]',
'blur-orb-secondary',
isHLS ? ' bg-orb-secondary-hls' : 'bg-orb-secondary',
'translate-x-0 translate-y-0 rounded-full opacity-30',
!reduceMotion && 'animate-[float_150s_ease-in-out_infinite_1s]',
!reduceMotion && 'transition-bg duration-1000 delay-300',
)}
/>
<div
@ -43,9 +65,11 @@ export default function Background() {
'min-h-[120px] min-w-[120px]',
'max-h-[600px] max-w-[600px]',
'right-[-4vw] top-[-10vw]',
'bg-orb-tertiary blur-orb-tertiary ',
'blur-orb-tertiary ',
isHLS ? ' bg-orb-tertiary-hls' : 'bg-orb-tertiary',
'translate-x-0 translate-y-0 rounded-full opacity-20',
!reduceMotion && 'animate-[float_180s_ease-in_infinite]',
!reduceMotion && 'transition-bg duration-1000 delay-300',
)}
/>
</div>

View File

@ -8,7 +8,7 @@ import { DocURL } from 'types/enums/docURL'
export default function HlsFarmIntro() {
return (
<Intro
bg='farm'
bg='hls-farm'
text={
<>
<span className='text-white'>Leveraged farming</span> is a strategy where users borrow

View File

@ -1,7 +1,7 @@
import classNames from 'classnames'
import { useCallback } from 'react'
import ActionButton from 'components/Button/ActionButton'
import Button from 'components/Button'
import { Circle, Enter, TrashBin, Wallet } from 'components/Icons'
import Loading from 'components/Loading'
import Text from 'components/Text'
@ -84,7 +84,7 @@ export default function Deposit(props: Props) {
return (
<div className='flex items-center justify-end'>
<ActionButton onClick={handleOnClick} color='tertiary' text='Deposit' />
<Button onClick={handleOnClick} color='tertiary' text='Deposit' />
</div>
)
}

View File

@ -1,5 +1,3 @@
import React from 'react'
import Button from 'components/Button'
import { PlusSquared } from 'components/Icons'
import Intro from 'components/Intro'
@ -8,7 +6,7 @@ import { DocURL } from 'types/enums/docURL'
export default function HLSStakingIntro() {
return (
<Intro
bg='borrow'
bg='hls-staking'
text={
<>
<span className='text-white'>Leverage staking</span> is a strategy where users borrow

View File

@ -1,9 +1,15 @@
import { Row } from '@tanstack/react-table'
import React from 'react'
import React, { useMemo } from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading'
import TitleAndSubCell from 'components/TitleAndSubCell'
import useBorrowAsset from 'hooks/useBorrowAsset'
import usePrices from 'hooks/usePrices'
import { calculateAccountLeverage } from 'utils/accounts'
import { getLeveragedApy } from 'utils/math'
export const ACTIVE_APY_META = { header: 'APY', accessorKey: 'strategy' }
export const ACTIVE_APY_META = { header: 'APY', accessorKey: 'strategy.apy' }
export const activeApySortingFn = (
a: Row<HLSAccountWithStrategy>,
@ -18,5 +24,27 @@ interface Props {
}
export default function ActiveAPY(props: Props) {
return <TitleAndSubCell title={'-'} sub={'-%/day'} />
const { data: prices } = usePrices()
const borrowRate = useBorrowAsset(props.account.strategy.denoms.borrow)?.borrowRate
const leverage = useMemo(
() => calculateAccountLeverage(props.account, prices),
[prices, props.account],
)
const leveragedApy = useMemo(() => {
if (props.account.strategy.apy === null || !borrowRate) return null
return getLeveragedApy(props.account.strategy.apy, borrowRate, leverage.toNumber())
}, [borrowRate, leverage, props.account.strategy.apy])
if (!leveragedApy) {
return <Loading />
}
return (
<TitleAndSubCell
title={<FormattedNumber amount={leveragedApy} options={{ suffix: '%' }} />}
sub={<FormattedNumber amount={leveragedApy / 365} options={{ suffix: '%/day' }} />}
/>
)
}

View File

@ -0,0 +1,54 @@
import { Row } from '@tanstack/react-table'
import React from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import Loading from 'components/Loading'
import TitleAndSubCell from 'components/TitleAndSubCell'
import useBorrowAsset from 'hooks/useBorrowAsset'
import { getLeveragedApy } from 'utils/math'
export const APY_RANGE_META = { header: 'APY range', accessorKey: 'apy' }
export const apyRangeSortingFn = (a: Row<HLSStrategy>, b: Row<HLSStrategy>): number => {
return (a.original.apy || 0) - (b.original.apy || 0)
}
interface Props {
isLoading?: boolean
strategy: HLSStrategy
}
export default function ApyRange(props: Props) {
const baseApy = props.strategy.apy
const borrowRate = useBorrowAsset(props.strategy.denoms.borrow)?.borrowRate
if (!borrowRate || props.isLoading || !baseApy) {
return <Loading />
}
const maxLevApy = getLeveragedApy(baseApy, borrowRate, props.strategy.maxLeverage)
const minApy = Math.min(baseApy, maxLevApy)
const maxApy = Math.max(baseApy, maxLevApy)
return (
<TitleAndSubCell
title={
<>
<FormattedNumber amount={minApy} options={{ suffix: ' - ' }} className='inline' />
<FormattedNumber amount={maxApy} options={{ suffix: '%' }} className='inline' />
</>
}
sub={
<>
<FormattedNumber amount={minApy / 365} options={{ suffix: '-' }} className='inline' />
<FormattedNumber
amount={maxApy / 365}
options={{ suffix: '% daily' }}
className='inline'
/>
</>
}
/>
)
}

View File

@ -5,7 +5,7 @@ import Loading from 'components/Loading'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { getAssetByDenom } from 'utils/assets'
export const NAME_META = { id: 'name', header: 'Strategy', accessorKey: 'strategy.denoms.deposit' }
export const NAME_META = { id: 'name', header: 'Strategy' }
interface Props {
strategy: HLSStrategy
}

View File

@ -2,6 +2,10 @@ import { ColumnDef } from '@tanstack/react-table'
import React, { useMemo } from 'react'
import Deposit, { DEPOSIT_META } from 'components/HLS/Farm/Table/Columns/Deposit'
import ApyRange, {
APY_RANGE_META,
apyRangeSortingFn,
} from 'components/HLS/Staking/Table/Columns/ApyRange'
import MaxLeverage, { MAX_LEV_META } from 'components/HLS/Staking/Table/Columns/MaxLeverage'
import MaxLTV, { LTV_MAX_META } from 'components/HLS/Staking/Table/Columns/MaxLTV'
import Name, { NAME_META } from 'components/HLS/Staking/Table/Columns/Name'
@ -27,6 +31,13 @@ export default function useAvailableColumns(props: Props) {
<MaxLTV strategy={row.original as HLSStrategy} isLoading={props.isLoading} />
),
},
{
...APY_RANGE_META,
cell: ({ row }) => (
<ApyRange strategy={row.original as HLSStrategy} isLoading={props.isLoading} />
),
sortingFn: apyRangeSortingFn,
},
{
...DEPOSIT_META,
cell: ({ row }) => (

View File

@ -16,14 +16,15 @@ export const menuTree: { pages: Page[]; label: string }[] = [
{ pages: ['trade'], label: 'Trade' },
{ pages: ['lend', 'farm'], label: 'Earn' },
{ pages: ['borrow'], label: 'Borrow' },
{ pages: ['portfolio'], label: 'Portfolio' },
...(ENABLE_HLS ? [{ pages: ['hls-staking'] as Page[], label: 'High Leverage' }] : []),
{ pages: ['portfolio'], label: 'Portfolio' },
]
export default function DesktopHeader() {
const address = useStore((s) => s.address)
const focusComponent = useStore((s) => s.focusComponent)
const isOracleStale = useStore((s) => s.isOracleStale)
const isHLS = useStore((s) => s.isHLS)
const accountId = useAccountId()
function handleCloseFocusMode() {
@ -59,7 +60,7 @@ export default function DesktopHeader() {
<div className='flex gap-4'>
{isOracleStale && <OracleResyncButton />}
{accountId && <RewardsCenter />}
{address && <AccountMenu />}
{address && !isHLS && <AccountMenu />}
<Wallet />
<Settings />
</div>

View File

@ -1,8 +1,4 @@
<svg viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M10.9998 8.00023V12.0002M10.9998 16.0002H11.0098M9.61507 2.89195L1.39019 17.0986C0.933982 17.8866 0.70588 18.2806 0.739593 18.6039C0.768998 18.886 0.916769 19.1423 1.14613 19.309C1.40908 19.5002 1.86435 19.5002 2.77487 19.5002H19.2246C20.1352 19.5002 20.5904 19.5002 20.8534 19.309C21.0827 19.1423 21.2305 18.886 21.2599 18.6039C21.2936 18.2806 21.0655 17.8866 20.6093 17.0986L12.3844 2.89195C11.9299 2.10679 11.7026 1.71421 11.4061 1.58235C11.1474 1.46734 10.8521 1.46734 10.5935 1.58235C10.2969 1.71421 10.0696 2.10679 9.61507 2.89195Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
/>
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.9998 9.00023V13.0002M11.9998 17.0002H12.0098M10.6151 3.89195L2.39019 18.0986C1.93398 18.8866 1.70588 19.2806 1.73959 19.6039C1.769 19.886 1.91677 20.1423 2.14613 20.309C2.40908 20.5002 2.86435 20.5002 3.77487 20.5002H20.2246C21.1352 20.5002 21.5904 20.5002 21.8534 20.309C22.0827 20.1423 22.2305 19.886 22.2599 19.6039C22.2936 19.2806 22.0655 18.8866 21.6093 18.0986L13.3844 3.89195C12.9299 3.10679 12.7026 2.71421 12.4061 2.58235C12.1474 2.46734 11.8521 2.46734 11.5935 2.58235C11.2969 2.71421 11.0696 3.10679 10.6151 3.89195Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 9V13M12 17H12.01" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 720 B

After

Width:  |  Height:  |  Size: 816 B

View File

@ -0,0 +1,283 @@
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
viewBox="0 0 444 225"
style="enable-background: new 0 0 444 225"
>
<defs>
<rect id="GridPlanetRect" x="0.2" y="-0.8" width="444.1" height="225.3" />
</defs>
<clipPath id="GridPlanet">
<use href="#GridPlanetRect" style="overflow: visible" />
</clipPath>
<g style="clip-path: url(#GridPlanet)">
<path
fill="currentColor"
d="M475.5,299.1l0.8-2.5c5.7-17.5,8.5-35.8,8.3-54.2c-8.3-5.3-21.5-7.9-39-7.5h-0.6l0.1-0.6c7.7-51.3,2-101.9-16.4-146.5
l-0.3-0.7h0.8c33.4-0.8,54.8,6.7,62,21.7c6.9,14.6-0.3,35.1-21,59.5c9.8,23.3,15.1,48.3,15.6,73.6c3.4,2.2,6.2,5.3,8,8.9
c5.4,11.4-0.3,27.4-16.5,46.4L475.5,299.1z M485.8,243.1c0.1,17.4-2.4,34.7-7.6,51.3c14.6-17.7,19.6-32.6,14.6-43.2
C491.2,247.9,488.8,245.1,485.8,243.1z M449.2,233.8c15.6,0,27.6,2.5,35.5,7.3c-0.6-25-5.9-49.7-15.6-72.8L469,168l0.2-0.3
c20.6-24.1,27.8-44.3,21.1-58.6c-6.7-14.2-27.7-21.7-60.2-21.1c18.2,44.4,23.8,94.8,16.2,145.8L449.2,233.8z"
/>
<path
fill="currentColor"
d="M482.8,212.3l-0.8-0.7c19.9-23.3,26.9-42.9,20.4-56.6c-6.7-14.1-27.1-21.1-59-20.4v-1.1c32.3-0.7,53.1,6.5,59.9,21
C510.1,168.7,503,188.7,482.8,212.3z"
/>
<path
fill="currentColor"
d="M243.5,302.8l-0.3-0.4c-28.5-40.3-53.7-82.8-75.2-127.2l-0.2-0.4l0.4-0.2c89.6-50.8,194.5-86,261.1-87.5h0.4l0.1,0.3
c18.5,44.8,24.2,95.6,16.4,147.1l-0.1,0.5h-0.4c-51.4,1.2-132.5,28.4-201.8,67.7L243.5,302.8z M169.1,175.2
c21.4,44,46.4,86.2,74.7,126.2c69.1-39.1,149.9-66.2,201.4-67.5c7.6-51,2-101.4-16.3-145.8C362.6,89.7,258.3,124.7,169.1,175.2z"
/>
<path
fill="currentColor"
d="M216.6,261.8l-0.5-0.9c79.9-45.3,173.4-76.7,232.8-78v1.1C389.7,185.3,296.4,216.6,216.6,261.8z"
/>
<path
fill="currentColor"
d="M191.4,219.1l-0.5-0.9C277.5,169,379,135,443.4,133.5v1.1C379.2,136.1,277.9,170,191.4,219.1z"
/>
<path
fill="currentColor"
d="M385.2,243.7c-2.8-49.6-14.3-98.3-33.7-144l1-0.4c19.6,45.8,31,94.6,33.9,144.3L385.2,243.7z"
/>
<path
fill="currentColor"
d="M315,267.4c-15.7-46.6-33.8-92.4-54.3-137.2l1-0.4c20.4,44.8,38.6,90.7,54.3,137.3L315,267.4z"
/>
<path
fill="currentColor"
d="M132.3,413.3l-0.8-0.3c-47.9-20-85.3-52.9-108-95.2v-0.2c-1.6-17.2,11.4-39.8,37.4-65.5s63.1-52.4,107.3-77.5l0.5-0.3
l0.2,0.5c21.5,44.3,46.7,86.8,75.2,127l0.3,0.5l-0.5,0.3c-69.2,39.2-114,83.4-111.7,109.9L132.3,413.3z M24.4,317.3
c22.5,41.9,59.4,74.5,106.7,94.3c-1.4-27.3,42.2-70.2,111.8-109.7c-28.3-40-53.3-82.2-74.7-126.2
C79.3,226.3,21.5,283.1,24.4,317.3z"
/>
<path
fill="currentColor"
d="M50.8,356.5C49.3,339.9,61.9,318,87,293.1c25.2-24.8,61-50.7,103.8-75l0.5,0.9C105,268.1,48.9,323.3,51.9,356.4
L50.8,356.5z"
/>
<path
fill="currentColor"
d="M469.4,169.1l-0.3-0.8c-22.8-55.5-67.9-103.7-126.9-135.5l-0.6-0.3L342,32c11.4-13.4,17.2-25.2,17.1-34.6
c-14.1-10-29.7-17.9-46.2-23.2l-3-1l3.1-0.1c24.4-0.6,40,4.9,45.2,15.9c1.1,2.4,1.8,5.1,1.9,7.8c29.7,21.1,53.6,52,69.5,90.2
c33.2-0.7,54.5,6.8,61.6,21.7c6.9,14.6-0.4,35.3-21.2,59.7L469.4,169.1z M343.2,32.2c58.7,31.8,103.6,79.7,126.5,135
c20.1-23.8,27.2-43.8,20.5-57.9c-6.9-14.6-28-21.9-61-21.1H429l-0.1-0.3c-15.6-38-39.3-68.5-68.6-89.6
C360,7.6,354.3,19.1,343.2,32.2z M316.2-25.8c15.2,5.3,29.7,12.6,42.8,21.9c-0.2-2.3-0.7-4.5-1.7-6.6c-4.8-10-18.9-15.3-40.9-15.3
L316.2-25.8z"
/>
<path
fill="currentColor"
d="M440,115.6l-0.8-0.7c20.1-23.5,27.2-43.3,20.6-57.2c-6.7-14.2-27.3-21.4-59.6-20.6V36c32.7-0.7,53.6,6.6,60.6,21.2
C467.6,71.5,460.4,91.7,440,115.6z"
/>
<path
fill="currentColor"
d="M397,69.6l-0.8-0.7c18.2-21.3,24.6-39.2,18.7-51.8c-6.1-12.9-24.8-19.3-53.9-18.6v-1.1c29.6-0.7,48.6,6,54.9,19.2
C422,29.7,415.5,48,397,69.6z"
/>
<path
fill="currentColor"
d="M492.5,124.8C470,68.7,422.7,24.8,359.3,1.2l0.4-1c63.7,23.7,111.2,67.8,133.8,124.2L492.5,124.8z"
/>
<path
fill="currentColor"
d="M478.5,96.8c-22.7-56.5-68.6-97.7-129.3-116l0.3-1c61.1,18.4,107.3,59.9,130,116.7L478.5,96.8z"
/>
<path
fill="currentColor"
d="M168.2,175.7l-0.2-0.5C141,119.6,125,70.6,122.7,37.4v-0.3L123,37c65.2-37.1,141.6-62.7,190.1-63.8h0.1h0.1
c51.7,16.7,93.1,57.3,116.5,114.1L430,88h-0.7c-66.4,1.5-171.2,36.6-260.6,87.4L168.2,175.7z M123.8,37.6
c2.3,33,18.3,81.5,44.9,136.6c89.1-50.5,193.3-85.4,259.8-87.2C405.1,30.9,364.1-9.1,313-25.7C264.7-24.6,188.8,0.8,123.8,37.6z"
/>
<path
fill="currentColor"
d="M145.5,122.4l-0.5-0.9c87.5-49.7,190.2-84,255.2-85.5v1.1C335.3,38.5,232.9,72.9,145.5,122.4z"
/>
<path
fill="currentColor"
d="M130.2,75.9l-0.5-0.9C209,30,301.9-1.1,360.9-2.5v1.1C302.1-0.1,209.4,31,130.2,75.9z"
/>
<path
fill="currentColor"
d="M351.4,99.8c-24.4-56.5-58.1-98-94.9-116.8L257-18c37,19,70.9,60.7,95.4,117.4L351.4,99.8z"
/>
<path
fill="currentColor"
d="M260.7,130.2C235.1,74,210.1,29.5,190.3,5l0.8-0.7C211,29,236,73.6,261.7,129.8L260.7,130.2z"
/>
<path
fill="currentColor"
d="M24.7,320l-1.2-2.3c-28.9-53.8-31-116.5-6-176.8c-2.1-25.7,39.2-66.5,105.4-104l0.7-0.4v0.8
c2.2,33.1,18.3,81.9,45.2,137.4l0.2,0.5l-0.4,0.2c-89.3,50.6-147.3,107.7-144.3,142L24.7,320z M122.7,38.2
C58.2,75,16.3,116.2,18.6,141v0.1v0.1c-24.7,59.2-22.9,120.8,4.8,173.9C23.1,298.3,36,276.5,60.9,252
c25.9-25.5,62.8-52.2,106.8-77.2C141.1,119.8,125.2,71.4,122.7,38.2z"
/>
<path
fill="currentColor"
d="M3.5,261.3c-1.5-16.8,11.1-38.9,36.6-64c25.5-25.1,61.6-51.2,104.9-75.7l0.5,0.9C58.2,172,1.5,227.7,4.5,261.2L3.5,261.3z
"
/>
<path
fill="currentColor"
d="M1.5,201.6c-1.4-15.2,10.1-35.3,33.2-58.1c23.1-22.7,55.9-46.4,95-68.6l0.5,0.9C51.2,120.7-0.2,171.2,2.5,201.5L1.5,201.6
z"
/>
<path
fill="currentColor"
d="M89.8,227.2c-28-55-36.6-109-23.9-152.2l1,0.3c-12.5,42.9-4,96.6,23.9,151.4L89.8,227.2z"
/>
<path
fill="currentColor"
d="M39.5,276.7C10.8,222.4,7.2,163.5,29.2,111l1,0.4C8.3,163.6,11.9,222.2,40.5,276.2L39.5,276.7z"
/>
<path
fill="currentColor"
d="M83.2,348.4c-20.9,0-37.2-3.5-47.6-10.7c-7.4-5-11.5-11.8-12.3-20.1c-28.9-53.8-30.9-116.6-5.8-176.8l0.8-2l0.2,2.1
c0.2,2.8,1.1,5.6,2.6,8c1.5,2.4,3.5,4.5,5.9,6c16.7,11.4,56.3,9.7,106.1-4.6l0.6-0.2v0.6c4.9,62.6,22.3,126.5,48.9,180l0.3,0.6
l-0.6,0.2C143.6,342.7,109.7,348.4,83.2,348.4z M17.8,143.1c-24.3,59.4-21.9,121.1,6.7,174.1v0.2c0.7,8,4.7,14.5,11.8,19.4
c22.9,15.7,77.2,13.4,145.3-6.1c-26.5-53.2-43.8-116.8-48.7-179.1c-50.5,14.4-89.3,16-106.4,4.3c-2.3-1.4-4.2-3.3-5.7-5.5
C19.3,148.2,18.3,145.7,17.8,143.1L17.8,143.1z"
/>
<path
fill="currentColor"
d="M54.5,229c-18.5,0-32.9-3.1-42.2-9.5c-3.1-1.9-5.7-4.6-7.5-7.7c-1.9-3.1-3-6.6-3.4-10.2l1.1-0.1c0.3,3.5,1.4,6.8,3.2,9.8
c1.8,3,4.3,5.5,7.2,7.3c20.3,13.9,68.6,11.8,129.2-5.6l0.3,1C108,224,77.9,229,54.5,229z"
/>
<path
fill="currentColor"
d="M45.4,342.5C16.8,289.1,12.6,223.9,33.5,159l1,0.3C13.6,224,17.9,288.8,46.3,342L45.4,342.5z"
/>
<path
fill="currentColor"
d="M100.5,347.2c-27.9-53.4-37.4-119-26.8-184.8l1,0.2c-10.5,65.6-1,131,26.8,184.2L100.5,347.2z"
/>
<path
fill="currentColor"
d="M342.6,33l-0.4-0.2c-59-31.8-124.9-43.6-185.6-33l-2.4,0.4l2.1-1.4c49.6-33.2,105.3-42.3,157-25.6
c24.4-0.5,39.9,4.9,45.1,15.9c5.1,10.7-0.3,25.8-15.5,43.6L342.6,33z M200.9-5c47.5,0,96.4,12.5,141.4,36.7
c14.7-17.3,19.9-31.9,15-42.2c-5-10.5-20.3-15.8-44.3-15.3H313h-0.1c-50.6-16.4-105.2-7.8-154.1,24.1C172.8-3.9,186.8-5,200.9-5
L200.9-5z"
/>
<path
fill="currentColor"
d="M281.9,7.6l-0.8-0.7c10.6-12.4,14.3-22.8,10.9-30c-3.4-7.3-14.4-11.2-31.3-10.8V-35c17.4-0.4,28.6,3.5,32.3,11.4
S292.7-5.1,281.9,7.6z"
/>
<path
fill="currentColor"
d="M359.3,1.2c-63.4-23.6-135.4-24.1-202.6-1.4l-0.3-1C223.8-24,296-23.5,359.6,0.2L359.3,1.2z"
/>
<path
fill="currentColor"
d="M122.7,38.2v-0.8c-1.3-19.1,2-31.8,9.7-37.6c5.8-4.3,13.8-4.7,24-1.1C206-34.4,261.7-43.5,313.3-26.8l3,1h-3.1
c-48.3,1.1-124.5,26.7-189.6,63.6L122.7,38.2z M143.8-2.7c-3.9-0.1-7.7,1.1-10.8,3.3c-7.4,5.5-10.5,17.6-9.4,35.9
c63.7-36,137.9-61.1,186.3-63.2c-50.5-15.3-104.8-6-153.2,26.4l-0.2,0.2l-0.3-0.1c-3.9-1.5-8.1-2.4-12.2-2.5H143.8z"
/>
<path
fill="currentColor"
d="M125.8,11.3l-0.5-0.9C171.7-16,226.1-34.2,260.6-35v1.1C226.3-33.1,172.1-15,125.8,11.3z"
/>
<path
fill="currentColor"
d="M190.3,5C178.8-9.5,169.7-16.2,164-14.6c-3.7,1.1-6.1,5.8-7,14l-1-0.1c0.9-8.6,3.5-13.7,7.7-14.9
c6.2-1.8,15.5,4.9,27.5,19.9L190.3,5z"
/>
<path
fill="currentColor"
d="M17.7,143.2l-0.2-2.1c-1.1-12.2,7.6-27.7,24.1-44.8c21.7-31.6,49.4-58.4,81.7-79c1.4-8.2,4.5-14.1,9.1-17.6
c5.8-4.4,14-4.7,24.3-1l1.2,0.4l-1.1,0.5c-11.2,5.4-22,11.6-32.4,18.3c-0.9,6.4-1.1,12.8-0.6,19.3v0.3l-0.3,0.2
C88.6,57.6,60.3,78.7,41.9,97.9c-9.2,13.7-17,28.2-23.4,43.5L17.7,143.2z M37.6,102.5c-12.1,13.6-18.9,26.1-19.1,36.1
C23.9,126.1,30.2,114,37.6,102.5L37.6,102.5z M123.1,18.7C93,38.1,66.7,63.1,45.8,92.2c18.3-17.7,44.6-36.8,76.8-55.1
C122.2,31,122.3,24.8,123.1,18.7z M143.9-2.7c-3.9-0.1-7.7,1.1-10.8,3.3c-4.2,3.2-7,8.5-8.4,16c9.8-6.3,20-12.1,30.5-17.2
C151.6-2,147.8-2.7,143.9-2.7z"
/>
<path
fill="currentColor"
d="M50.2,84.6c-1.6-18.3,27.8-47.5,75.1-74.3l0.5,0.9C79.7,37.5,49.8,66.9,51.3,84.5L50.2,84.6z"
/>
<path
fill="currentColor"
d="M97.1,36.7c-0.9-9.7,14.6-25.1,39.5-39.2l0.5,0.9c-24.1,13.7-39.8,29-39,38.2L97.1,36.7z"
/>
<path
fill="currentColor"
d="M66.8,75.3l-1-0.3c12.7-43.2,44.8-70.3,90.6-76.3l0.1,1C111.1,5.7,79.3,32.5,66.8,75.3z"
/>
<path
fill="currentColor"
d="M30.2,111.4l-1-0.4C51.3,58.5,96.4,18.7,156.3-1.2l0.3,1C97.1,19.6,52.2,59.2,30.2,111.4z"
/>
<path
fill="currentColor"
d="M61.2,163.7c-15.3,0-27.1-2.6-34.7-7.8c-2.5-1.6-4.7-3.8-6.2-6.3c-1.6-2.6-2.5-5.5-2.8-8.4V141v-0.1
C42.6,80.7,91.9,30.3,156.3-1.2l1.5-0.7L157-0.5c-19.7,35-27.9,88.8-23.1,151.3v0.4l-0.4,0.1C105.1,159.6,80.5,163.7,61.2,163.7z
M18.6,141.2c0.3,2.8,1.2,5.5,2.6,7.9c1.5,2.4,3.5,4.4,5.9,5.9c16.6,11.4,56.1,9.7,105.7-4.5C128,88.9,136,35.7,155.3,0.5
C91.9,31.8,43.4,81.7,18.6,141.2L18.6,141.2z"
/>
<path
fill="currentColor"
d="M81.5,100.8c-10.9,0-19.4-1.8-24.8-5.6c-1.8-1.2-3.4-2.7-4.5-4.6c-1.1-1.8-1.8-3.9-2-6.1l1.1-0.1c0.2,2,0.8,3.9,1.8,5.6
c1,1.7,2.5,3.2,4.2,4.2c11.8,8.1,40,6.8,75.3-3.3l0.3,1C112.8,97.9,95.2,100.8,81.5,100.8z"
/>
<path
fill="currentColor"
d="M113.7,45.4c-5.8,0-10.3-1-13.1-3c-1-0.6-1.8-1.5-2.4-2.5c-0.6-1-1-2.1-1.1-3.3l1.1-0.1c0.1,1,0.4,2,1,2.8
c0.5,0.9,1.2,1.6,2.1,2.1c6.1,4.2,20.8,3.5,39.4-1.8l0.3,1C130.2,43.9,120.9,45.4,113.7,45.4z"
/>
<path
fill="currentColor"
d="M34.5,159.3l-1-0.3C54.4,94.1,98,37.3,156.2-1.2l0.6,0.9C98.8,38,55.4,94.6,34.5,159.3z"
/>
<path
fill="currentColor"
d="M74.6,162.6l-1-0.2C84.2,96.6,113.5,38.6,156.2-1.1l0.7,0.8C114.3,39.2,85.1,97.1,74.6,162.6z"
/>
<path
fill="currentColor"
d="M177.5,436.4c-11.6,0-21.3-1.4-28.8-4.1l-2.4-0.9c-1.8-0.7-3.8-1.4-5.3-2.1c-2.6-1.1-6-2.5-8.3-3.6
c-7.6-3.4-13.4-6.5-15.3-7.5c-3.3-1.8-8.4-4.7-14.5-8.4c-7.8-5-13.6-9.3-13.6-9.3c-6.4-4.9-8.1-6.2-12.7-10.2
c-7-6-11.8-11-11.9-11l-1.7-1.8c-1.3-1.3-2.7-2.7-3.8-3.9c-1.9-2-4-4.3-5.5-6.1c-3.1-3.7-7-8.6-9.8-12.3c-1.1-1.5-2.5-3.4-3.7-5.1
l-1.1-1.5c-1.1-1.6-5.3-7.8-8.7-13.6c-2.7-4.7-4.4-7.5-8.6-16.1c-0.6-1.3-2.8-5.9-4.3-9.5c-0.4-0.9-0.7-1.9-1.2-2.8
c-0.9-2.2-1.8-4.4-2.5-6.3c-0.5-1.4-2.2-6.1-3.4-9.8c-2.1-6.7-4.2-15.5-5.1-19c-0.7-3-1.5-7-2-10.2c-0.9-6-1.9-12.9-2.4-18.2
C0,231.5,0,224.2,0,222.2c0.1-7.8,0.2-12.1,1-20.8c0.4-4.7,1.3-11.2,2.7-20c0.5-3.3,1.4-7.5,2.2-10.9c0.5-2.1,1.1-4.5,1.7-6.9
c0.3-1.1,0.5-2.1,0.8-3c0.8-3.3,2.2-7.6,3.2-11c3.7-11.4,8.1-22.6,13.3-33.4c2.8-5.8,6.7-13.1,9.6-18.2
c3.7-6.4,8.7-14.1,10.2-16.3c2.9-4.4,7.4-10.6,11.4-15.6c1.9-2.4,4.2-5.3,6-7.3c2.1-2.5,5-5.7,6.9-7.9c2.7-3.1,6.7-7.2,10-10.4
l0.5-0.5c1.6-1.6,3.6-3.5,5.2-5c3.4-3.1,6-5.5,7.9-7c6-5.1,8.4-7.2,17-13.5c1.8-1.3,4-2.8,5.6-4c2.8-1.9,6.7-4.4,9.5-6.2
c1.8-1.1,8.4-5.1,13.9-8.1c3.4-1.8,9.2-4.9,17.2-8.4c5.6-2.5,10.3-4.3,13.2-5.5c6.2-2.3,9.4-3.4,14.7-5.1c2-0.7,7.5-2.2,11.3-3.3
c5-1.3,5-1.3,13.1-3.1c5.9-1.3,16.6-3.1,23.5-3.9c4-0.5,13.4-1.5,26.1-1.9c10.2-0.2,18.7,0.2,22.8,0.4c4.1,0.3,9.7,0.8,14.6,1.3
c2.8,0.3,6.9,0.8,10,1.4l1.5,0.3c3.7,0.6,8.2,1.4,11.7,2.1c1.8,0.4,6.5,1.3,10.1,2.3l3.1,0.8c3,0.8,6.5,1.7,9.1,2.5
c5.6,1.8,13.3,4.4,19.6,6.7c0.1,0,9.8,3.7,20.4,8.9c11.9,5.8,19.8,10.9,19.9,11l1.7,1.1c2.2,1.4,4.8,3,6.7,4.4
c3,2,6.5,4.4,8.9,6.3c4.8,3.8,11,8.8,15.5,12.5c2.5,2.1,5.6,5.1,7.8,7.3c1.4,1.4,4.9,4.8,7.4,7.6c3.8,4.2,8.9,10.3,12.8,15
c1.9,2.4,4.2,5.6,6.2,8.4c1.2,1.7,2.7,3.9,3.9,5.8l1.5,2.3c0,0,5,7.6,10.4,18.2c4.7,9.2,7.9,17.1,8.4,18.6
c1.8,4.5,4.2,10.8,5.4,15.2c1.4,4.6,3.1,10.7,4.2,15.2c0.5,1.9,1.1,4.5,1.8,8c0.5,2.5,1.1,5.6,1.4,8.1c0.7,4.7,1.4,11,2,15.6
c0.2,1.6,0.8,9.4,0.9,16.5c0,4.8-0.2,11.3-0.4,15.9c-0.1,1.7-0.2,3.8-0.4,5.9c0,0.9-0.2,1.8-0.2,2.5c0,0.1-0.7,7.9-2.2,16.5
c-0.4,2.3-1,5.9-1.6,8.5c-1.1,4.9-2.6,11.2-3.8,15.9c0,0.1-1.9,7.2-5.5,17.2c-2.8,7.8-8.3,16.5-16.3,25.9l-1.7,2l0.8-2.5
c13.2-40.2,10.7-84.6-7.3-128.3c-22.8-55.7-67.8-103.9-126.9-135.7s-124.7-43.5-185.3-33c-19.7,35.1-27.8,88.7-23,151.1
c4.9,62.4,22.3,126.5,48.9,180c21,42,45.9,73.8,71.9,91.7l0.9,0.6l-1.1,0.3C224.8,432,198.3,436.4,177.5,436.4z M264.1-33.9
c-2,0-4.1,0-6.3,0.1c-12.7,0.3-22,1.4-26,1.8c-6.8,0.8-17.5,2.5-23.4,3.8c-8.1,1.8-8.1,1.8-13.1,3.1c-3.9,1-9.2,2.6-11.2,3.2
c-5.3,1.7-8.5,2.8-14.7,5.1c-2.9,1.1-7.6,3-13.2,5.4c-7.9,3.5-13.7,6.6-17.1,8.4c-5.5,2.9-12,6.9-13.8,8c-2.8,1.8-6.6,4.2-9.4,6.2
c-1.6,1.1-3.9,2.7-5.6,4c-8.4,6.3-11,8.4-17,13.4c-1.9,1.6-4.5,3.9-7.9,7c-1.6,1.4-3.6,3.4-5.2,4.9l-0.5,0.5
c-3.2,3.2-7.2,7.4-9.9,10.4c-1.9,2.2-4.8,5.4-6.9,7.9c-1.7,2-4,4.9-5.9,7.3c-4,5-8.4,11.2-11.4,15.6c-1.4,2.2-6.5,9.8-10.2,16.2
c-2.9,5-6.7,12.3-9.5,18.1c-5.2,10.8-9.6,21.9-13.3,33.3c-1.1,3.4-2.4,7.7-3.2,10.9c-0.2,0.9-0.5,2-0.8,3
c-0.6,2.4-1.2,4.8-1.7,6.9c-0.8,3.3-1.7,7.6-2.2,10.8c-1.4,8.7-2.3,15.2-2.7,19.9c-0.8,8.6-0.8,12.9-1,20.7c0,2,0,9.2,0.9,20.4
c0.4,5.2,1.4,12.1,2.4,18.1c0.5,3.1,1.3,7.2,2,10.2c0.9,3.5,3.1,12.3,5.1,19c1.2,3.8,2.9,8.4,3.4,9.8c0.7,1.9,1.6,4.1,2.5,6.3
c0.4,1,0.8,2,1.2,2.8c1,2.5,2.6,6,4.2,9.5c4.2,8.5,5.8,11.3,8.5,16c3.3,5.7,7.5,11.9,8.6,13.5l1.1,1.5c1.2,1.7,2.5,3.6,3.7,5.1
c2.8,3.6,6.8,8.5,9.8,12.2c1.5,1.8,3.6,4.1,5.5,6.1c1.1,1.2,2.5,2.6,3.8,3.9l1.7,1.8c0,0,4.9,5,11.8,10.9c4.6,4,6.3,5.3,12.7,10.1
c0.1,0.1,5.8,4.4,13.6,9.3c5.9,3.8,11.1,6.6,14.4,8.4c1.9,1,7.6,4.1,15.2,7.5c2.4,1.1,5.7,2.5,8.3,3.6c1.6,0.6,3.5,1.4,5.3,2.1
l2.4,0.9c20.6,7.6,58.5,4.5,104-8.5c-25.8-18.2-50.5-49.8-71.3-91.5C155,277.7,137.6,213.7,132.7,151
C127.8,88.2,136.1,34.3,155.9-1l0.1-0.2h0.3c60.9-10.6,127,1.2,186.3,33.1c59.2,31.9,104.5,80.2,127.4,136
c17.7,43,20.5,86.7,8.1,126.5c7-8.4,11.8-16.4,14.4-23.5c3.5-9.9,5.4-17,5.4-17.1c1.2-4.7,2.8-11,3.8-15.9
c0.6-2.6,1.2-6.1,1.6-8.4c1.4-8.5,2.1-16.3,2.1-16.4c0.1-0.8,0.1-1.6,0.2-2.5c0.2-2,0.4-4.1,0.4-5.8c0.2-4.6,0.4-11,0.4-15.9
c-0.1-7.1-0.7-14.9-0.8-16.4c-0.5-4.6-1.3-10.9-2-15.6c-0.4-2.4-1-5.5-1.4-8c-0.7-3.5-1.3-6.1-1.7-7.9c-1.2-4.5-2.8-10.6-4.2-15.2
c-1.3-4.3-3.7-10.6-5.4-15.1c-0.6-1.5-3.7-9.4-8.4-18.5c-5.4-10.5-10.3-18-10.3-18.1c-0.5-0.7-1-1.5-1.5-2.3
c-1.3-1.9-2.7-4.1-3.9-5.8c-2-2.8-4.2-6-6.2-8.4c-3.8-4.7-8.9-10.7-12.7-14.9c-2.5-2.8-6-6.2-7.3-7.5c-2.2-2.2-5.3-5.1-7.8-7.2
c-4.4-3.7-10.6-8.8-15.4-12.5c-2.4-1.9-5.8-4.2-8.9-6.3c-2-1.3-4.5-2.9-6.7-4.3l-1.7-1.2c0,0-8-5.1-19.8-10.9
c-10.5-5.2-20.2-8.8-20.3-8.9c-6.3-2.3-14.1-4.9-19.6-6.7c-2.7-0.8-6.1-1.7-9.1-2.5l-3.1-0.8c-3.5-0.9-8.2-1.9-10-2.2
c-3.4-0.7-8-1.4-11.6-2.1l-1.5-0.3c-3.1-0.5-7.1-1-9.9-1.3c-4.9-0.5-10.6-1-14.6-1.3C277.2-33.7,271.4-33.9,264.1-33.9z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,231 @@
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
x="0px"
y="0px"
viewBox="0 0 525 165"
style="enable-background: new 0 0 525 165"
>
<defs>
<rect id="GridWebRect" x="-0.5" y="0.6" width="526.1" height="164.7" />
</defs>
<clipPath id="GridWeb">
<use href="#GridWebRect" style="overflow: visible" />
</clipPath>
<path
style="clip-path: url(#GridWeb)"
fill="currentColor"
d="M589.8,99.3
c-0.9-5.8-5.8-10.4-14-14.8c-5.1-2.9-10.5-5.2-16.3-7c-8.3-3.1-18-6.2-28.9-9.7l-3.7-1.2c-10.1-3.3-22.5-7.4-35.6-11.7
c-28.5-9.5-63.5-21.1-96.7-31.1l-0.1-0.1c0,0-0.1,0-0.1,0.1C353,11.3,314.4,1.2,295,0.9c-17.5-0.3-58.8,7.3-95.2,13.9
c-5.4,1-10.7,2-15.8,2.9c0,0-0.1,0-0.1,0l0,0.1c-4.6,0.8-9.1,1.6-13.4,2.4c-43,7.5-87.7,18.5-127.1,28.2c-9,2.2-17.7,4.4-26,6.4
c-8.5,2.1-13.6,5-15.9,9.2c-3.2,5.7-0.7,13.1,2.1,21.6c0.8,2.4,1.6,4.9,2.4,7.5c2,6.9,12.3,21.4,22.3,35.4
c7.8,11,15.2,21.3,18.8,28.1c24.1,46,30.2,46.2,77.5,47.2c53.2,1.1,119.3,2.6,169.7,10.8c15.3,2.5,28.8,6,41.9,9.4
c18.8,4.8,35.7,9.2,53.1,9.2c20.3,0,41.3-6,66.6-24c15.1-10.8,30.5-21,45.4-30.8c14.9-9.8,30.3-20,45.3-30.8
C578.7,124.6,591.5,110.2,589.8,99.3z M293.9,213.2c-49.7-8.1-114.2-9.5-166.9-10.7c6-4.3,11.9-9,17.8-13.9
c16.5,1.8,34.7-0.1,51-1.9c11.5-1.2,22.3-2.4,30.6-2c22.6,0.6,43.3,1.9,67.6,5.5V213.2z M226.4,183.6c-1.2-0.1-2.5-0.1-3.8-0.1
c-7.7,0-17.1,1-27,2.1c-15.9,1.7-33.5,3.6-49.6,2c7.4-6.3,14.6-12.9,21.7-19.5c0.4,0,0.8,0,1.2,0c15.2-2,27.3-1.8,42.6-1.5
c5.6,0.1,11.4,0.2,18.1,0.2c26.4-1.6,48-0.6,64.2,2.9v19.4C269.7,185.5,248.9,184.2,226.4,183.6z M430.7,40.5
c-2.8,0.8-5.3,1.7-7.7,2.6c-9.1-2.3-17.5-4.1-24.6-5c-5.7-0.7-11.5-1.8-17.5-3.2c3.2-2.2,6.5-4.4,9.8-6.8
C404.2,32,417.8,36.3,430.7,40.5z M391.8,27.2c0.9-0.7,1.9-1.4,2.8-2.2c14.3,4.3,28.9,8.9,43.2,13.5c-1.8,0.4-3.5,0.9-5.2,1.3
C419.5,35.6,405.6,31.3,391.8,27.2z M547.9,76.1c-4.2,17-20.3,29-38.5,39c-13-3.9-26.4-7.2-40-9.8c14.1-9.1,28.4-18.8,40.7-30.9
l0.7-0.7c12.2-0.3,23.6,0.1,34.2,1.8C546,75.6,547,75.8,547.9,76.1z M507.8,74.5c0.1,0,0.3,0,0.4,0c-12.3,11.8-26.5,21.3-40.5,30.4
c-5.8-1.1-11.7-2.2-17.7-3.1c5.5-5.9,9.9-12.8,12.3-20.7c0.4-1.4,0.3-2.7-0.2-3.9c0.9-0.1,1.9-0.2,2.8-0.3
c12.4-1.1,24.4-2.2,35.8-2.9C503.4,74.4,505.7,74.5,507.8,74.5z M97.2,66.4c8.6-0.5,17.1-0.7,25.6-0.8
c4.8,11.1,35.6,26.5,57.5,36.3c-16.2,4.4-32.3,9.3-48.6,14.3C119.5,104.1,103,82.1,97.2,66.4z M461.3,80.7
c-2.4,7.9-6.8,14.8-12.7,20.8c-14.1-2.2-28.4-4-42.7-5.6c8.9-5.2,15.3-10.4,17.3-15.5c0.1-0.1,0.1-0.3,0.1-0.4
c12.8-0.7,25.3-1.7,37.6-2.9C461.5,78.3,461.7,79.5,461.3,80.7z M374.4,134.1c-4.7-7.1-7.8-11.2-16.4-17.6
c16.9-5.8,33.8-12.6,46.3-19.6c14.6,1.6,29.1,3.4,43.4,5.6C430.9,119.2,403,128.9,374.4,134.1z M123.3,62.6
c0.3-5.4,5.2-9.9,13.2-13.7c10.9,1.1,21.7,2.5,32.5,4.2c-6.8,3.2-11.1,7.2-11.7,12.1c-11-0.5-22.3-0.8-33.8-0.8
C123.3,63.8,123.2,63.2,123.3,62.6z M266.6,31.5c1.2,3,2.4,6.1,3.7,9.3c-12.3-0.1-25.9,0.2-39.2,1c-4.5-2.7-9.1-5.4-13.8-8
C233.9,32.5,250.8,31.8,266.6,31.5z M422.6,79c-2.7,0.1-5.4,0.3-8.2,0.3c-5.3,0.2-10.2,0.3-14.9,0.4c0.2-0.3,0.4-0.7,0.6-1
c1.4-2.8,1.3-5.5-0.4-7.8c-2.1-2.8-5.5-5.3-9.9-7.3c5.2-0.7,10.4-1.2,15.7-1.7c5.8,2.7,10.8,5.9,14.5,9.5
C422.5,73.7,423.3,76.2,422.6,79z M398.1,79.7c-7.4,0.1-14,0.1-20.2,0.1c-0.5,0-1,0-1.5,0c1.8-3,2.5-5.9,2.2-8.7
c-0.2-2-1.7-3.8-4.3-5.3c4.5-0.7,8.9-1.4,13.3-2c5,2.1,8.8,4.7,11.1,7.7c1.5,2,1.6,4.1,0.3,6.6C398.8,78.7,398.5,79.2,398.1,79.7z
M211,64c0.1-0.9,0.6-1.7,1.3-2.5c6.3,1.4,12.5,3,18.6,4.5c-0.1,0.2-0.2,0.4-0.3,0.7c-0.6,1.7-0.7,3.6-0.2,5.8
c-6.1-0.9-12.4-1.8-18.9-2.6C210.9,68,210.7,66,211,64z M356.4,71.4c0.9,2.3,0.5,5.3-1.1,8.6c-6.4,0.2-13.1,0.5-20.5,1
c1.5-2.7,2.2-5.1,1.8-7c6.4-1.7,12.6-3.2,18.7-4.5C355.8,70.1,356.2,70.7,356.4,71.4z M347.1,90.4c-6.2-0.6-12.5-1.1-18.7-1.7
c1.8-1.6,3.3-3.2,4.6-4.9c0.4-0.5,0.8-1,1.1-1.5c7.4-0.6,14.2-0.9,20.6-1c-0.4,0.8-0.9,1.5-1.5,2.3
C351.6,85.9,349.6,88.2,347.1,90.4z M235.3,80.3c-1.5-2.2-2.6-4.3-3.2-6.3c7.4,1.1,14.5,2.3,21.1,3.5c0.8,1.5,1.8,3.2,3.1,4.8
c1.1,1.5,2.4,2.9,3.9,4.4c-5.9,0.5-11.8,1.2-17.5,2C239.7,86,237.2,83.2,235.3,80.3z M231.7,67.2c0.1-0.3,0.2-0.6,0.4-0.8
c6.7,1.7,13.1,3.5,19.5,5.4c0,1.3,0.4,2.8,1.1,4.5c-6.6-1.2-13.6-2.3-20.9-3.5C231.1,70.7,231.1,68.7,231.7,67.2z M332.3,70.9
c-5.5,1.8-11.7,4.1-19.2,7.5c-1.2-0.4-2.8-0.7-4.7-0.8c4.3-2.8,8.5-5.5,12.7-8C325.8,69.5,329.7,69.8,332.3,70.9z M335.2,73.3
c-6.5,1.8-13.2,3.7-20.2,5.9l0.1,0.4c-0.2-0.2-0.4-0.4-0.7-0.6c7.5-3.3,13.7-5.7,19.3-7.4C334.3,72.1,334.8,72.6,335.2,73.3z
M333.4,81.2c-5.4,0.4-11.3,1-17.7,1.7c0.2-0.6,0.3-1.1,0.2-1.6c0-0.4-0.2-0.7-0.4-1c6.9-2.2,13.5-4.1,20-5.8
C335.8,76.1,335.1,78.5,333.4,81.2z M257.2,81.7c-1-1.3-1.8-2.6-2.5-3.8c6.5,1.2,12.6,2.4,18.2,3.5c0.3,1.1,1,2.4,2.2,3.7
c0.2,0.2,0.4,0.4,0.6,0.7c-4.7,0.2-9.4,0.5-14,1C260,85,258.5,83.3,257.2,81.7z M254.5,68.6c7.3,2.8,14.4,5.7,21.3,8.7
c-0.6,0.2-1.2,0.4-1.6,0.6l0.1-0.2c-6.9-2.3-14.1-4.6-21.5-6.8c0-0.1,0-0.1,0-0.2C253,69.8,253.6,69.1,254.5,68.6z M281.8,76.9
c-1.4,0-2.7,0-3.8,0.1c-7.1-3.1-14.4-6.1-21.8-9c0.9-0.2,2-0.3,3.3-0.3c2.9,0,6.6,0.5,10.9,1.1C275.1,72,279,74.8,281.8,76.9z
M289.1,78.4l0.1-0.1c1.6,0.1,3.2,0.1,4.9,0.1c1.5,0,3,0,4.4,0c6.1,0,11.6,0,14.4,1.1l0.1,0.1c0,0,0.1,0,0.1-0.1
c1,0.4,1.6,1,1.7,1.7c0.1,0.9-0.5,2.2-1.6,3.5c-3.2,3.7-10.2,7.4-18.7,7.4c-8,0-15-4-18.5-7.9c-1.4-1.6-2.2-3.2-2-4.2
c0.2-1.2,1.6-1.8,3.8-2c0.1,0,0.1,0.1,0.2,0.1l0.1-0.1c0.8-0.1,1.8-0.1,2.8-0.1c1.8,0,3.8,0.1,6,0.2
C287.5,78.2,288.3,78.3,289.1,78.4C289,78.3,289.1,78.3,289.1,78.4z M273.4,78.6c-0.3,0.4-0.5,0.8-0.6,1.4c0,0.1,0,0.1,0,0.2
c-5.8-1.2-12.1-2.4-18.8-3.6c-0.8-1.7-1.2-3.2-1.3-4.4C259.8,74.2,266.7,76.4,273.4,78.6z M276.9,86.8c1.7,1.5,3.9,3,6.5,4.1
c-3.1,1.5-6.3,2.9-9.5,4.2c-4.2-2.1-7.8-4.7-10.9-7.4C267.5,87.3,272.1,87,276.9,86.8z M284.8,91.6c2.7,1.1,5.8,1.8,9.1,1.8v6.8
c-6.9-0.1-13.2-1.8-18.7-4.4C278.4,94.5,281.6,93.1,284.8,91.6z M295,93.4c3.4-0.1,6.6-0.7,9.4-1.7c3.2,1.5,6.6,3,10,4.4
c-5.7,2.5-12.3,4-19.4,4.1V93.4z M305.9,91.1c2-0.9,3.8-1.9,5.3-3l0,0c4.7,0.5,9.5,1,14.3,1.5c-2.8,2.2-6,4.2-9.7,5.9
C312.5,94,309.1,92.6,305.9,91.1z M312.5,87.1c0.5-0.5,1-1,1.4-1.4c0.5-0.5,0.8-1.1,1.1-1.6c6.3-0.7,12.1-1.3,17.5-1.7
c-0.2,0.2-0.3,0.5-0.5,0.7c-1.4,1.8-3.2,3.7-5.3,5.4C322,88.1,317.2,87.6,312.5,87.1z M306.4,77.4c-2.1-0.1-4.4-0.1-6.9-0.1
c1.5-2.4,2.9-4.7,4.2-7.1c1.6-0.1,3.1-0.2,4.7-0.2c3.7-0.2,7.2-0.4,10.4-0.5C314.6,72,310.5,74.6,306.4,77.4z M298.1,77.3
c-1.3,0-2.6,0-3.9,0c-1.5,0-3-0.1-4.5-0.1c-1.4-2.3-2.8-4.5-4.1-6.8c2.6,0.2,5.3,0.3,8,0.2c3,0,5.9-0.2,8.7-0.3
C301,72.6,299.6,75,298.1,77.3z M288.3,77.1c-0.5,0-0.9,0-1.4-0.1c-1.3-0.1-2.6-0.1-3.8-0.1l0.2-0.3c-2.5-1.8-6.1-4.4-10.5-7.5
c3.5,0.5,7.3,0.9,11.4,1.2C285.5,72.6,286.8,74.8,288.3,77.1z M271,67.6c-0.1,0-0.2,0-0.3,0c-2.4-1.6-4.9-3.4-7.6-5.3
c0.8,0.1,1.5,0.1,2.3,0.2c4.8,0.4,9.9,0.9,15.2,1.2c0.9,1.8,1.8,3.5,2.8,5.3C279,68.7,274.7,68.1,271,67.6z M268.3,67.3
c-6.1-0.8-10.9-1.2-13.8,0c-4.9-1.9-9.9-3.7-14.9-5.4c2.1-0.4,4.6-0.6,7.4-0.6c4,0,8.7,0.3,13.8,0.8
C263.4,63.9,265.9,65.7,268.3,67.3z M253.1,68.1c-0.7,0.6-1.2,1.4-1.5,2.4c0,0.1,0,0.1,0,0.2c-6.1-1.8-12.4-3.6-18.9-5.2
c1.1-1.4,2.7-2.3,4.7-2.9C242.7,64.2,248,66.1,253.1,68.1z M261.3,87.9c3.1,2.8,6.8,5.6,11.1,7.9c-3.2,1.3-6.4,2.5-9.5,3.7
c-1,0.4-2,0.8-3,1.2c-6.2-3.1-11.6-6.8-16-10.7C249.6,89.1,255.4,88.4,261.3,87.9z M263.3,100.5c3.4-1.3,6.9-2.6,10.4-4.1
c5.9,2.9,12.7,4.9,20.1,5v7.5c-12.1-0.2-23.2-3.2-32.6-7.6C262,101,262.6,100.7,263.3,100.5z M295,101.4c7.6-0.1,14.7-1.9,20.8-4.7
c1.8,0.8,3.7,1.5,5.5,2.3c2.4,1,4.9,2,7.3,3.1c-9.5,4.1-20.8,6.8-33.4,6.8c-0.1,0-0.1,0-0.2,0V101.4z M321.8,97.9
c-1.5-0.6-3-1.2-4.5-1.9c3.7-1.8,7.1-4,9.9-6.3c6.2,0.6,12.5,1.2,18.8,1.8c-4.2,3.6-9.6,7.1-15.8,9.9
C327.3,100.2,324.5,99.1,321.8,97.9z M336.3,73c-0.3-0.7-0.7-1.2-1.3-1.7c6.2-1.9,11.6-3.1,17.4-4.2c0.8,0.5,1.5,1,2.1,1.5
C348.5,69.8,342.5,71.3,336.3,73z M333.9,70.4c-2.4-1.4-6.1-1.9-10.9-2c2.8-1.7,5.6-3.3,8.3-4.8c8.2,0.1,14.9,0.7,19.4,2.6
C345.2,67.3,339.9,68.5,333.9,70.4z M320.7,68.3c-3.7,0-7.8,0.2-12.4,0.5c-1.3,0.1-2.6,0.1-4,0.2c0.9-1.7,1.8-3.3,2.7-5
c2.3-0.1,4.5-0.1,6.8-0.2c5.4-0.2,10.5-0.3,15.2-0.3C326.2,65.1,323.5,66.7,320.7,68.3z M303,69.1c-3.1,0.1-6.2,0.3-9.4,0.3
c-3,0-5.9-0.1-8.7-0.3c-1-1.8-2-3.6-2.9-5.3c3.7,0.2,7.5,0.4,11.4,0.4c4.2,0,8.3-0.1,12.3-0.2C304.8,65.7,303.9,67.4,303,69.1z
M293.4,63.1c-4.1,0-8.1-0.2-12-0.4c-0.9-1.7-1.7-3.4-2.5-5.1c5,0.1,10.1,0.2,15.2,0.2c5.1,0,10,0.1,14.7,0.2
c-0.8,1.6-1.6,3.3-2.5,5C302.1,63,297.8,63.1,293.4,63.1z M280.1,62.6c-5.1-0.3-10-0.8-14.5-1.2c-1.5-0.1-3-0.3-4.4-0.4
c-2-1.3-4-2.7-6.1-4.1c3.6,0.1,7.4,0.2,11.2,0.3c3.7,0.1,7.6,0.2,11.4,0.3C278.4,59.2,279.2,60.9,280.1,62.6z M258.8,60.8
c-8.9-0.7-16.3-1-21.4,0.4c-3.9-1.3-7.8-2.6-11.7-3.9c7.2-0.8,16.7-0.8,27.1-0.5C254.9,58.2,256.9,59.5,258.8,60.8z M235.6,61.8
c-1.8,0.8-3.2,1.8-4.1,3.2c-5.9-1.5-12-3-18.1-4.4c2.1-1.4,5.3-2.3,9.5-2.9C227.2,59,231.4,60.4,235.6,61.8z M230.7,73.9
c0.7,2.2,1.9,4.6,3.6,7.1c1.8,2.7,4.1,5.4,6.9,8.1c-3.8,0.5-7.5,1.1-11.2,1.8c-5.3-3.7-9.9-7.6-13.3-11.6c-2.3-2.7-3.9-5.4-4.8-8
C218.4,72.1,224.7,72.9,230.7,73.9z M242.3,90.1c4.4,4,9.9,7.9,16.1,11.1c-1.9,0.7-3.7,1.5-5.6,2.2c-7.6-3.4-15-7.3-21.4-11.6
C235,91.1,238.7,90.6,242.3,90.1z M259.8,101.8c9.7,4.8,21.4,8,34.1,8.2v5.1c-9.5-0.3-24.7-4.6-39.6-11.1
C256.1,103.2,258,102.5,259.8,101.8z M295,110c0.1,0,0.1,0,0.2,0c13.2,0,25.1-2.9,34.9-7.3c1.9,0.8,3.8,1.7,5.7,2.5
c-15.4,6-31.4,9.9-40.8,9.9V110z M331.5,102c6.3-3,11.7-6.6,15.9-10.4c3.8,0.3,7.5,0.7,11.2,1c0.7,0.1,1.4,0.1,2,0.2
c-6.8,4.4-15,8.4-23.4,11.8C335.4,103.7,333.5,102.9,331.5,102z M358.8,91.5c-3.4-0.3-6.8-0.6-10.2-0.9c2.2-2.1,4.1-4.2,5.5-6.3
c0.7-1,1.3-2,1.9-3c6.2-0.1,12.1-0.2,18.3-0.2c-0.6,0.8-1.3,1.7-2,2.5c-2.6,2.8-6,5.6-10,8.3C361.1,91.7,360,91.6,358.8,91.5z
M356.6,80c1.5-3.4,1.9-6.5,0.9-9v0c-0.2-0.6-0.6-1.2-0.9-1.8c5.4-1.2,10.8-2.2,16.1-3.1c3,1.5,4.6,3.3,4.8,5.2
c0.3,2.7-0.5,5.6-2.4,8.5C368.9,79.9,362.8,79.9,356.6,80z M355.8,68.2c-0.5-0.6-1.1-1.1-1.8-1.6c0.6-0.1,1.2-0.2,1.8-0.4
c3.5-0.7,7.2-1.4,11.3-2.3c1.4,0.4,2.6,0.9,3.7,1.3C365.9,66.1,360.9,67.1,355.8,68.2z M355.6,65.1c-1,0.2-2,0.4-3,0.6
c-4.4-2.3-11.1-3.2-19.3-3.3c1.8-1,3.7-2,5.5-3c10.9,0.9,19.6,2.2,26,3.9C361.5,64,358.4,64.6,355.6,65.1z M331,62.4
c-5.3,0-11.1,0.1-17.2,0.3c-2,0.1-4.1,0.1-6.1,0.2c0.8-1.7,1.6-3.3,2.4-4.9c9.9,0.2,18.8,0.6,26.6,1.3
C334.8,60.2,332.9,61.3,331,62.4z M310.6,56.8c0.7-1.5,1.4-3,2.1-4.5c10.1,0.5,21.2,1.3,32.2,2.4c-2.1,1.1-4.2,2.3-6.3,3.5
C329.7,57.5,320.1,57,310.6,56.8z M309.3,56.8c-5.1-0.1-10.3-0.2-15.3-0.2c-5.2,0-10.5-0.1-15.7-0.2c-0.9-1.8-1.7-3.7-2.5-5.5
c6.7,0,13.2,0.2,19,0.5c2,0.1,4.1,0.2,6.3,0.3c3.2,0.2,6.7,0.3,10.3,0.5C310.7,53.7,310.1,55.2,309.3,56.8z M277,56.3
c-3.6-0.1-7.2-0.2-10.8-0.3c-4.5-0.1-8.9-0.3-13-0.4c-2.1-1.4-4.3-2.8-6.5-4.2c8.4-0.4,16.9-0.6,25.2-0.6c0.9,0,1.8,0,2.7,0
C275.3,52.7,276.2,54.5,277,56.3z M251,55.6c-11.1-0.2-20.8-0.1-28,0.9c-2.7-0.8-5.4-1.7-8.1-2.5c8.8-1.2,19.1-2,29.8-2.5
C246.9,52.9,249,54.3,251,55.6z M220.5,57c-3.8,0.7-6.7,1.8-8.5,3.4c-4.6-1.1-9.3-2.1-14-3c3.8-1.1,8.6-2,14.2-2.8
C215,55.3,217.7,56.1,220.5,57z M211,61.3c-0.6,0.8-1,1.6-1.2,2.6c-0.3,2-0.2,4,0.3,6c-6.3-0.8-12.8-1.5-19.4-2.2
c-0.7-1-1.2-1.9-1.5-2.8c-0.6-1.8-0.3-3.2,1-4.2c1.2-1,3.1-1.9,5.5-2.7C200.9,59,206,60.1,211,61.3z M210.6,71.1
c1,2.9,2.7,5.8,5.2,8.9c3.2,3.8,7.6,7.6,12.6,11.1c-1.6,0.3-3.2,0.6-4.9,0.9c-14.3-8.1-26.5-16.5-31.8-23
C198.2,69.6,204.5,70.3,210.6,71.1z M229.8,92c6.5,4.4,13.9,8.4,21.6,11.9c-0.9,0.4-1.8,0.7-2.7,1.1c-7.9-3.8-16-8-23.6-12.2
C226.7,92.6,228.2,92.3,229.8,92z M252.8,104.6c15.4,6.8,31.3,11.3,41.1,11.6v5.4c-7.3-0.4-25-7-43.8-16
C251,105.3,251.9,104.9,252.8,104.6z M295,116.3C295.1,116.3,295.1,116.3,295,116.3c9.8,0,26.4-4.1,42.2-10.4
c2.2,1,4.3,2.1,6.4,3.1c-20.5,7.3-40.5,12.5-48.6,12.7V116.3z M338.7,105.2c8.7-3.5,17.1-7.7,23.9-12.3c5.1,0.5,10.3,0.9,15.4,1.4
c-9.6,5-21.2,9.8-32.9,14.1C343,107.3,340.9,106.3,338.7,105.2z M364.1,92c3.6-2.5,6.6-5.1,9-7.7c1-1.1,1.9-2.2,2.6-3.3
c0.7,0,1.5,0,2.2,0c5.9,0,12.2,0,19.3-0.1c-3.4,4-9.6,8.3-17.4,12.5C374.6,92.9,369.3,92.4,364.1,92z M372.7,64.9
c-1.1-0.5-2.3-1-3.6-1.5c3.2-0.7,6.6-1.6,10.4-2.5c2.1,0.6,4.1,1.3,6,2C381.3,63.5,377,64.2,372.7,64.9z M367,62.8
c-6.8-2-16-3.4-26.4-4.4c1.7-0.9,3.3-1.8,5-2.7c0.4-0.2,0.9-0.5,1.3-0.7c11,1.2,21.6,2.9,30.5,5.4C373.6,61.3,370.1,62.1,367,62.8z
M348.6,54c3.1-1.7,6.2-3.4,9.3-5.1c11.6,1.9,23,4.3,33.2,7.5c-2.7,0.9-5.6,1.8-8.7,2.7c-1,0.3-1.9,0.5-2.8,0.8
C370.5,57.1,359.8,55.3,348.6,54z M346.6,53.8c-11.3-1.3-22.9-2-33.4-2.6c1.1-2.4,2.1-4.8,3-7.1c12,0.9,26,2.3,39.8,4.4
C352.9,50.3,349.7,52,346.6,53.8z M311.9,51.1c-3.8-0.2-7.4-0.4-10.8-0.5c-2.2-0.1-4.3-0.2-6.3-0.3c-5.7-0.3-12.4-0.5-19.6-0.5
c-1.2-2.7-2.3-5.3-3.3-7.8c8.5,0.1,16.4,0.4,22.9,0.7c5.5,0.3,12.3,0.7,20,1.3C314,46.3,313,48.7,311.9,51.1z M274.1,49.8
c-9.2,0-19.2,0.2-29.1,0.6c-3.9-2.5-7.9-5-12-7.5c11.2-0.7,22.5-0.9,33-0.9c1.6,0,3.3,0,4.9,0C271.8,44.5,272.9,47.1,274.1,49.8z
M243,50.5c-11.1,0.6-21.8,1.5-30.9,2.8c-6.8-2-13.7-3.9-20.4-5.7c11.6-2.3,25.3-3.7,39.2-4.6C235,45.5,239.1,48,243,50.5z
M209.4,53.6c-5.4,0.9-10.2,1.9-13.8,3.2c-7.5-1.5-15-2.9-22.6-4.2c4.6-1.8,10.1-3.3,16.1-4.5C195.9,49.8,202.6,51.7,209.4,53.6z
M193.6,57.6c-1.7,0.7-3.1,1.4-4.1,2.3c-1.7,1.4-2.1,3.3-1.3,5.5c0.3,0.7,0.6,1.5,1.1,2.3c-10-1-20.3-1.7-30.9-2.3
c0.7-4.8,5.3-8.7,12.5-11.8C178.6,54.7,186.1,56.1,193.6,57.6z M190.2,68.8c4.9,6.6,17,15.1,31.5,23.5c-7.6,1.4-15,3.1-22.4,4.8
c-16.7-8.6-30.7-17.4-37.2-24.4c-2.3-2.5-3.5-4.6-3.6-6.3C169.3,67,179.9,67.8,190.2,68.8z M223.2,93.2c7.6,4.4,15.8,8.6,23.9,12.5
c-4.5,2-9,4.2-13.3,6.7c-0.3,0.1-0.5,0.3-0.8,0.5c-11-4.6-22.1-9.8-32.3-15C208.2,96.2,215.6,94.6,223.2,93.2z M234.4,113.4
c4.5-2.7,9.3-5,14.1-7c19.2,9.2,37.5,16.1,45.4,16.4v8.9C282.7,131.4,258.9,123.7,234.4,113.4C234.4,113.4,234.4,113.4,234.4,113.4
z M295,122.8c8.4-0.2,29.2-5.5,50.1-13c3.4,1.8,6.5,3.7,9.4,5.8c0.3,0.2,0.7,0.5,1,0.7c-26.6,8.9-52.7,15.2-60.5,15.4V122.8z
M355.2,114.6c-2.7-1.9-5.6-3.7-8.6-5.4c12-4.4,24-9.4,33.6-14.7c7.4,0.7,14.9,1.4,22.3,2.2c-12.4,6.8-29,13.4-45.6,19
C356.4,115.4,355.8,115,355.2,114.6z M382,93.6c7.6-4.3,13.6-8.6,16.7-12.7c4.9-0.1,10.1-0.2,15.7-0.4c2.6-0.1,5.2-0.2,7.7-0.3
c-2.1,5.1-8.7,10.4-17.9,15.6C396.8,95,389.4,94.3,382,93.6z M387.8,62.7c-1.9-0.8-4-1.6-6.2-2.3c0.4-0.1,0.7-0.2,1.1-0.3
c3.8-1,7.1-2.1,10.2-3.2c3.7,1.2,7.2,2.5,10.5,4C398.1,61.4,392.9,62,387.8,62.7z M394.6,56.3c2.5-0.9,4.7-1.8,6.9-2.7
c6.6,2,13,4,18.9,6c-5,0.3-9.9,0.6-14.7,1.1C402.3,59.1,398.6,57.6,394.6,56.3z M392.9,55.7c-10-3.2-21.4-5.7-33.5-7.7
c2.4-1.3,4.7-2.7,7.1-4.1c11.3,2.8,22.7,5.9,33.4,9.1C397.6,53.9,395.3,54.8,392.9,55.7z M357.6,47.7c-13.2-2.1-27.2-3.6-40.9-4.7
c1.3-3.1,2.4-6.1,3.5-8.9c13.4,2.2,29,5.5,44.7,9.4C362.4,44.9,360,46.3,357.6,47.7z M315.4,42.9c-6.9-0.5-13.7-1-20.4-1.4
c-6.9-0.4-14.9-0.7-23.5-0.8c-1.3-3.2-2.5-6.4-3.7-9.3c6.3-0.1,12.5-0.2,18.3-0.2c2.5,0,4.9,0,7.2,0c6.7,0.1,15.5,1,25.6,2.6
C317.9,36.8,316.7,39.8,315.4,42.9z M229.1,41.9c-14.3,0.9-28.2,2.5-39.9,5c-8.8-2.3-17.4-4.3-25.7-6.1c15.1-3.1,33.1-5.3,51.8-6.8
C219.9,36.5,224.5,39.2,229.1,41.9z M186.7,47.4c-6,1.4-11.3,3-15.7,4.9c-10.7-1.7-21.5-3.2-32.3-4.3c6-2.6,13.6-4.8,22.1-6.6
C169.1,43.1,177.8,45.2,186.7,47.4z M157.2,66.4c0.1,2,1.4,4.4,3.9,7.1c6.5,7,20,15.6,36.1,24.1c-5.2,1.2-10.3,2.6-15.4,3.9
c-29.4-13-53.5-26.7-57.9-35.9C135.2,65.6,146.4,65.8,157.2,66.4z M198.8,98.4c10.3,5.3,21.6,10.6,32.8,15.4
c-2.5,1.5-5,3.1-7.4,4.8c-13.7-5-27.6-10.6-40.6-16.3C188.6,100.9,193.7,99.6,198.8,98.4z M233,114.3
c24.9,10.4,49.2,18.2,60.9,18.6v5.2c-14.9-1.6-41-9.1-68.3-19C228,117.4,230.5,115.8,233,114.3z M295,132.9
c7.9-0.3,34.5-6.7,61.6-15.8c8.8,6.4,11.9,10.5,16.5,17.3c-29.1,5.2-58.8,5.6-78.1,3.8V132.9z M423.8,78.9c0.7-3-0.3-5.9-3-8.4
c-3.5-3.3-7.9-6.2-13.1-8.8c5.2-0.4,10.3-0.8,15.6-1.1c7,2.4,13.3,4.8,18.5,7.1c1.2,0.5,3.1,1.4,5.3,2.4c3.7,1.7,8.3,3.8,11.6,5.2
c0.6,0.2,1,0.6,1.4,0.9C448.3,77.2,436.1,78.3,423.8,78.9z M459.2,74.1c-3.3-1.4-7.9-3.5-11.6-5.2c-2.2-1-4.1-1.9-5.3-2.4
c-4.6-2-10-4-15.9-6.1c6.6-0.3,13.4-0.5,20.3-0.7c17.9,5.7,35.5,11,48.7,13.4c-9.8,0.6-20,1.6-30.5,2.5c-1.1,0.1-2.2,0.2-3.3,0.3
C461,75.2,460.2,74.5,459.2,74.1z M423.5,59.4c-6.3-2.2-13.1-4.4-20.3-6.6c2.5-1.1,4.9-2.2,7.3-3.2c0.5-0.2,1-0.5,1.6-0.7
c7.9,2.3,16.7,5.1,25.7,8c1.8,0.6,3.7,1.2,5.5,1.7C436.5,58.8,430,59.1,423.5,59.4z M410,48.6c-2.8,1.2-5.6,2.5-8.5,3.8
c-10.7-3.2-22.2-6.4-33.6-9.3c1.9-1.1,3.7-2.2,5.6-3.4c6.3,1.6,12.4,3,18.4,4.1c5,0.9,11.4,2.6,18.6,4.7
C410.3,48.5,410.1,48.5,410,48.6z M366.3,42.7c-16.1-4-32-7.4-45.7-9.7c0.2-0.6,0.5-1.2,0.7-1.8c0.7-1.7,1.3-3.3,1.9-4.9
c9.8,2.2,19.9,5,29.8,7.8c6.3,1.8,12.6,3.5,18.9,5.2C370,40.4,368.1,41.6,366.3,42.7z M320.2,30.8c-0.3,0.7-0.5,1.3-0.8,2
c-10.3-1.7-19.2-2.6-26-2.7c-1.6,0-11.6-0.1-25.9,0.3c-1-2.6-1.9-5-2.9-7.3c9.7-0.4,19.1-0.7,28-0.8c9-0.1,19,1.5,29.5,3.8
C321.4,27.6,320.8,29.2,320.2,30.8z M266.2,30.4c-14,0.3-32,1-50.4,2.5c-3.4-1.9-6.7-3.7-10.1-5.4c19.2-2,38.9-3.5,57.6-4.4
C264.3,25.4,265.2,27.9,266.2,30.4z M213.7,33.1c-18.3,1.5-36.9,3.8-52.6,7.1c-6-1.3-11.8-2.5-17.4-3.6c17.9-3.6,38.5-6.6,59.9-8.9
C207,29.5,210.3,31.2,213.7,33.1z M158.4,40.8c-8.4,1.9-15.9,4.2-21.9,6.9c-9.4-0.9-18.7-1.6-28-2c8.8-3.1,19.8-5.9,32.1-8.5
C146.4,38.3,152.3,39.5,158.4,40.8z M134.6,48.7c-7.5,3.8-12.1,8.3-12.5,13.8c0,0.6,0,1.3,0.2,2c-8.5,0.1-17,0.3-25.6,0.8
c-1.3-3.9-2-7.3-1.7-10.1c0.3-2.7,1.5-4.5,3.6-5.5c2.1-1,4.4-2,7-2.9C115.2,47.1,124.9,47.8,134.6,48.7z M181.9,102.7
c4.1,1.8,7.9,3.4,11,4.8c10,4.2,20.2,8.2,30.2,11.8c-10.1,7-19.8,14.9-29.3,23.3c-20.6-2.6-40.2-8.3-56.2-20.9
c-1.5-1.2-3.2-2.8-5.1-4.5C149.2,112.1,165.5,107.1,181.9,102.7z M224.5,119.8c28.6,10.4,55.1,18,69.4,19.5v7.8
c-14.1-2.5-30.9-2.5-48.7-2.4c-16.2,0.1-33.3,0.1-49.8-1.8C204.8,134.5,214.5,126.6,224.5,119.8z M295,139.3
c6.4,0.6,13.9,0.9,22.2,0.9c17.1,0,37.2-1.4,56.6-4.8c3.9,7.5,8.5,14.6,13.9,21.3c-21.5,7.5-38.9,3.2-62.3-2.6
c-8.9-2.2-18.9-4.7-30.3-6.9V139.3z M375,135.2c4.1-0.7,8.2-1.6,12.2-2.5c21.1-4.9,46.1-13.8,61.9-30c5.8,0.9,11.5,1.9,17.2,3
c-1.3,0.8-2.5,1.6-3.8,2.4c-11.8,7.6-24.1,15.5-35.3,24.5c-14.9,12-27.2,19.4-38.4,23.5C383.5,149.7,378.9,142.7,375,135.2z
M501,72.8c-12.9-1.7-31.9-7.3-50.6-13.1c7.1-0.1,14.4-0.1,21.9-0.1c14.3,5.1,26.7,9.9,33.9,13C504.5,72.7,502.7,72.7,501,72.8z
M446.9,58.6c-2.9-0.9-5.9-1.9-8.8-2.8c-8.6-2.7-16.9-5.4-24.4-7.6c3-1.4,6.1-2.7,9.4-3.9c14.8,3.8,31.2,9,45.9,14.1
C461.3,58.4,454,58.5,446.9,58.6z M412,47.7c-7.8-2.3-14.6-4.1-20-5.1c-5.6-1-11.3-2.3-17.2-3.8c1.6-1,3.2-2,4.8-3.1
c6.4,1.5,12.6,2.7,18.6,3.5c6.6,0.8,14.5,2.5,22.9,4.6C418,45.1,415,46.4,412,47.7z M373.3,38.4c-6.6-1.7-13.4-3.6-20-5.4
c-9.9-2.8-20-5.6-29.7-7.8c0.6-1.6,1.2-3.1,1.8-4.5c7.5,1.9,15.3,4.2,23.3,6.6c9.7,2.9,19.6,5.8,29.3,8.2
C376.4,36.4,374.9,37.5,373.3,38.4z M322.4,24.9c-10.6-2.3-20.7-3.9-29.9-3.8c-9.1,0.1-18.6,0.4-28.4,0.8c-0.7-1.6-1.3-3.1-1.9-4.6
c12-0.9,23.1-1.4,32.8-1.5c0.1,0,0.3,0,0.4,0c8.5,0,18.3,1.8,28.9,4.5C323.7,21.8,323.1,23.3,322.4,24.9z M262.9,22
c-19.2,0.9-39.4,2.5-59.1,4.6c-1.6-0.8-3.1-1.6-4.7-2.3c21.6-3,42.8-5.4,61.8-6.8C261.6,18.9,262.2,20.4,262.9,22z M201.7,26.8
c-21.8,2.4-42.9,5.6-61,9.3c-2.3-0.4-4.6-0.8-6.8-1.2c20.1-3.8,41.8-7.3,63.2-10.3C198.6,25.3,200.1,26.1,201.7,26.8z M137.7,36.7
c-12.6,2.7-23.6,5.7-32.2,8.9c-7.2-0.2-14.3-0.3-21.4-0.2c13.6-3.3,29.4-6.7,46.5-10C132.9,35.9,135.3,36.3,137.7,36.7z
M102.8,46.7c-1.6,0.7-3.1,1.3-4.6,2c-2.5,1.2-3.9,3.3-4.3,6.4c-0.3,2.8,0.3,6.3,1.6,10.3C84,66,72.4,67.1,60.9,68.6
c-2.1-4.8-3-9-2.1-12.2c0.9-3,3.2-5,7.1-6.1c4-1.2,8.4-2.4,13-3.6C86.8,46.5,94.7,46.5,102.8,46.7z M95.9,66.5
c5.7,15.8,22,37.7,34.5,50.2c-1.4,0.4-2.9,0.9-4.3,1.3c-7.3,2.2-14.6,4.5-22,6.8c-2.8-3.3-5.9-6.8-9-10.5
C82.4,99.8,67.6,82.7,61.4,69.7C72.9,68.2,84.4,67.1,95.9,66.5z M126.4,119.1c1.6-0.5,3.3-1,4.9-1.5c2,2,3.9,3.7,5.6,5
c15.9,12.6,35.4,18.4,55.7,21c-6.7,6-13.3,12.1-19.8,18.1c-1.9,1.7-3.7,3.5-5.6,5.2c-32.2,0.5-36.5-5.5-53.4-29.6l-1.4-2
c-1.9-2.8-4.6-6-7.6-9.6C112.1,123.5,119.3,121.3,126.4,119.1z M173.7,162.6c6.8-6.3,13.6-12.6,20.6-18.8c17,2.1,34.5,2,51,2
c17.8-0.1,34.6-0.1,48.7,2.4v20.3c-16.3-3.5-37.8-4.4-64.2-2.8c-6.7,0-12.5-0.1-18.1-0.2c-15.3-0.3-27.4-0.5-42.6,1.5
C170.6,165.5,172.1,164,173.7,162.6z M295,148.4c11.3,2.1,21.3,4.6,30.1,6.8c14.7,3.7,27,6.7,39.4,6.7c7.8,0,15.6-1.2,24-4.2
c7.3,8.9,15.8,16.9,25,23.7c-31.2,10.6-74.2-0.9-102.7-8.6c-5.7-1.5-10.7-2.9-14.9-3.8c-0.3-0.1-0.6-0.1-0.9-0.2V148.4z
M389.7,157.3c11.2-4.2,23.5-11.6,38.3-23.6c11.2-9,23.4-16.9,35.2-24.5c1.6-1,3.2-2.1,4.8-3.1c13.6,2.6,27,5.8,40,9.7
c-6.6,3.6-13.5,6.9-20.1,10.1c-12.7,6.2-24.8,12-33.6,18.6c-7.5,6.7-13.6,13.1-19.1,18.8c-8,8.3-14.3,14.9-20.4,17.6
C405.5,174.2,397,166.2,389.7,157.3z M508.9,72.5c-6.2-2.8-18.5-7.7-33.1-12.9c3.2,0,6.5,0,9.9,0.1c5.4,2,10.2,3.9,14.1,5.6
c3.3,1.4,11.7,3.1,22.3,5.2c3.7,0.8,7.7,1.6,11.8,2.4C525.8,72.3,517.5,72.2,508.9,72.5z M472.5,58.4c-15-5.3-32.1-10.8-47.5-14.8
c2.4-0.9,5-1.7,7.7-2.5c18.7,6.1,36.1,12.2,49.8,17.4C479.1,58.5,475.8,58.4,472.5,58.4z M379.4,34.5c-10-2.4-20.3-5.4-30.3-8.4
c-7.9-2.4-15.7-4.7-23.1-6.6c1.1-2.7,2.3-5.1,3.4-7.1c17.8,3.6,38.7,9,60,15.2C386,30.1,382.7,32.4,379.4,34.5z M324.7,19.3
c-10.7-2.7-20.7-4.6-29.4-4.6H295c-9.9,0.1-21.1,0.6-33.3,1.5c-0.9-2-1.8-3.7-2.6-5.3c14.6-1.8,27-2.8,36-2.8c0.1,0,0.1,0,0.2,0
c8.4,0,19.8,1.5,32.9,4.1C327,14.3,325.9,16.7,324.7,19.3z M260.5,16.3c-19.5,1.5-41.2,4-63.2,7.1c-1.7-0.8-3.4-1.6-5.1-2.3
c23.8-4.2,46.5-7.7,65.6-10C258.7,12.6,259.6,14.4,260.5,16.3z M195.1,23.7c-21.9,3.1-44.1,6.8-64.5,10.6c-1.5-0.3-3-0.5-4.4-0.7
c20.8-4.2,42.8-8.4,63.9-12.1C191.8,22.2,193.4,22.9,195.1,23.7z M127.3,34.9c-18.1,3.5-34.7,7.1-48.5,10.6
c-3.6,0.1-7.2,0.3-10.8,0.6c16.6-3.9,36.2-8.1,56.9-12.3l-0.1,0.7C125.6,34.7,126.5,34.8,127.3,34.9z M73.5,47
c-2.7,0.7-5.4,1.5-7.8,2.2c-4.3,1.3-6.9,3.6-7.9,6.9c-1,3.4-0.1,7.8,2,12.7c-9.7,1.3-19.3,3-28.8,5c-2.2-4.9-3.1-9.3-1.9-12.8
c1.2-3.7,4.5-6.3,10.2-7.8c6.1-1.6,13.3-3.4,21.3-5.3C64.8,47.5,69.1,47.2,73.5,47z M60.2,69.9c6.2,13.3,21.2,30.5,33.9,45.2
c3,3.5,6,6.9,8.7,10.1c-8.9,2.7-18,5.4-27.4,8c-4.6-6.4-10.6-13.6-17-21.2C48,99.5,36.6,86,31.4,74.9
C40.9,72.9,50.6,71.2,60.2,69.9z M103.7,126.1c3.1,3.7,5.8,7.1,7.8,9.9l1.4,2c17,24.2,21.5,30.5,53.1,30.2
c-7.1,6.5-14.3,13.1-21.6,19.3c-2.8-0.3-5.5-0.7-8.1-1.3c-10.6-2.2-20.4-7.8-28.1-16.1c-3-3.2-6.3-6.5-9.8-10
c-7.7-7.8-15.7-15.8-19.9-22.4c-0.7-1.1-1.4-2.2-2.2-3.3C85.6,131.5,94.7,128.8,103.7,126.1z M295,170c0.2,0,0.4,0.1,0.6,0.1
c4.2,1,9.2,2.3,14.9,3.8c20.4,5.5,48.1,12.9,73.7,12.9c10.6,0,20.9-1.3,30.2-4.5c0.1,0,0.2-0.1,0.2-0.1c5.7,4.1,11.6,7.7,17.7,10.8
c-1,0.5-1.9,1.1-2.9,1.6c-36.2,20.1-60.4,13-88.5,4.9c-10-2.9-20.4-5.9-32.1-8c-4.8-0.9-9.4-1.6-13.9-2.3V170z M415.9,181.7
c6.1-2.9,12.4-9.5,20.1-17.5c5.4-5.6,11.5-12,19-18.7c8.7-6.6,20.7-12.4,33.4-18.5c7-3.4,14.2-6.9,21.1-10.7
c8,2.4,15.9,5.2,23.7,8.2c0,0,0,0,0,0c-5.1,2.5-9.4,4.7-12,6.4c-11.9,7.8-24.5,17.2-36.7,26.2c-16.7,12.5-34,25.3-50.8,35.1
C427.5,189.2,421.6,185.7,415.9,181.7z M510.9,115.5c18.1-10,33.9-22.1,38.1-39.2c6.6,1.6,12.7,3.4,17.6,5.2c2.1,0.9,4,1.7,5.8,2.6
c1.7,0.9,3.1,1.9,3.9,2.9c1.6,14.5-24,27.8-41.9,36.8C526.9,120.7,519,118,510.9,115.5z M530.2,68.9c6.3,2,12.3,4,17.8,5.8
c-0.9-0.2-1.9-0.3-2.9-0.5c-7.7-1.8-15.8-3.4-22.9-4.9c-10.1-2.1-18.8-3.8-22-5.2c-3.2-1.3-7-2.8-11.2-4.5c4.3,0,8.7,0.1,13.2,0.2
l0-0.1c8.9,2.9,17.2,5.7,24.4,8L530.2,68.9z M499,58.7c-4.5-0.1-8.8-0.1-13.1-0.1c-13.8-5.2-31.7-11.5-51.2-17.9
c1.5-0.4,3.1-0.8,4.7-1.1l-0.1-0.4c18.3,5.9,35.9,11.8,51.7,17C493.7,56.9,496.3,57.8,499,58.7z M390.4,26.8
C369,20.6,347.9,15,329.9,11.4c0.8-1.3,1.6-2.5,2.4-3.4c17.9,4.1,39,10,61.1,16.7C392.4,25.4,391.4,26.1,390.4,26.8z M294.1,2.1
c0.3,0,0.6,0,0.9,0c8.6,0.1,21.1,2.2,36,5.6c-0.8,1-1.6,2.2-2.3,3.5c-13.4-2.7-25-4.2-33.5-4.2H295c-9.2,0-21.8,1.1-36.6,2.8
c-0.8-1.3-1.6-2.5-2.4-3.5C271.9,3.7,285.6,2.1,294.1,2.1z M200,15.9c18.2-3.3,37.6-6.8,54.7-9.5c0.9,1,1.7,2.1,2.5,3.5
c-19.6,2.4-42.8,6-67.1,10.3c-1.4-0.6-2.7-1.2-4.1-1.7C190.5,17.7,195.2,16.8,200,15.9z M170.7,21.2c4.2-0.7,8.6-1.5,13.1-2.3
c1.4,0.5,2.8,1.1,4.1,1.7c-45.3,8-93.7,18.1-127.8,26.3c-3.8,0.4-7.5,0.8-11.2,1.3C87.1,38.7,129.7,28.3,170.7,21.2z M2.5,64.4
c2.1-3.8,7.1-6.7,15.2-8.6c5.8-1.4,11.8-2.9,18-4.4l0,0.1c5.1-1,10.4-1.8,15.8-2.5c-4.6,1.1-8.8,2.2-12.6,3.2
c-6,1.6-9.7,4.5-11,8.6c-1.2,3.7-0.3,8.3,1.9,13.4c-8.9,2-17.7,4.2-26.3,6.9C1.3,74.5,0.1,68.8,2.5,64.4z M7.1,92.6
c-0.8-2.6-1.6-5.1-2.4-7.5c-0.3-1-0.7-2-1-2.9c8.7-2.7,17.5-5,26.5-6.9c5.1,11,16.4,24.4,27.4,37.5c6.2,7.4,12.1,14.5,16.7,20.8
c-11.1,3.1-22.6,6.2-34.5,9c-3.2-4.6-6.8-9.7-10.5-14.9C19.3,113.7,9,99.3,7.1,92.6z M124.5,202.4c-46.6-1-52.6-1.1-76.4-46.6
c-1.8-3.4-4.5-7.6-7.7-12.4c12-2.9,23.4-5.9,34.6-9c0.9,1.2,1.7,2.5,2.4,3.6c4.3,6.7,12.3,14.8,20.1,22.6c3.5,3.5,6.8,6.8,9.8,10
c7.9,8.5,17.8,14.2,28.7,16.5c2.3,0.5,4.7,0.9,7.2,1.2c-5.9,4.9-11.8,9.6-17.8,13.9l0.1,0.2C125.2,202.5,124.8,202.5,124.5,202.4z
M455.3,208c-46.5,33.3-78.4,25.1-118.8,14.7c-12.9-3.3-26.3-6.8-41.5-9.3v-22.9c4.5,0.7,9,1.4,13.7,2.3c11.7,2.1,22,5.1,32,8
c14.6,4.2,28.2,8.2,43,8.2c13.8,0,28.6-3.4,46.3-13.2c1.2-0.7,2.4-1.3,3.5-2c9.1,4.5,18.6,7.8,28.2,9.8
C459.7,204.9,457.5,206.5,455.3,208z M546.1,146.5c-15,10.8-30.4,20.9-45.3,30.7c-12.3,8.1-25,16.5-37.6,25.3
c-9.7-1.9-19.2-5.2-28.3-9.6c16.7-9.8,33.8-22.5,50.3-34.8c12.7-9.5,24.7-18.4,36.6-26.2c2.5-1.7,6.9-3.8,11.9-6.4
c0.3-0.2,0.6-0.3,1-0.5c8.5,3.4,16.8,7.2,24.8,11.4C555.5,139.6,551,143,546.1,146.5z M560.5,135.7c-7.9-4.2-16.1-8-24.5-11.3
c18.1-9.1,43.2-22.4,41.6-37.4c1.1,0.7,2.1,1.4,3.1,2.1l0.1-0.2c4.5,3.2,7.2,6.6,7.8,10.6C590,108.1,581.5,119.3,560.5,135.7z"
/>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -28,7 +28,9 @@ export { default as Gear } from 'components/Icons/Gear.svg'
export { default as GridGlobe } from 'components/Icons/GridGlobe.svg'
export { default as GridHole } from 'components/Icons/GridHole.svg'
export { default as GridLandscape } from 'components/Icons/GridLandscape.svg'
export { default as GridPlanet } from 'components/Icons/GridPlanet.svg'
export { default as GridTire } from 'components/Icons/GridTire.svg'
export { default as GridWeb } from 'components/Icons/GridWeb.svg'
export { default as HandCoins } from 'components/Icons/HandCoins.svg'
export { default as Heart } from 'components/Icons/Heart.svg'
export { default as InfoCircle } from 'components/Icons/InfoCircle.svg'

View File

@ -1,7 +1,8 @@
import classNames from 'classnames'
import { ReactNode } from 'react'
import Card from 'components/Card'
import { GridGlobe, GridHole, GridLandscape, GridTire } from 'components/Icons'
import { GridGlobe, GridHole, GridLandscape, GridPlanet, GridTire, GridWeb } from 'components/Icons'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import useStore from 'store'
@ -9,31 +10,39 @@ import useStore from 'store'
interface Props {
text: string | ReactNode
children?: ReactNode
bg: 'borrow' | 'lend' | 'farm' | 'portfolio'
bg: 'borrow' | 'lend' | 'farm' | 'portfolio' | 'hls-farm' | 'hls-staking'
}
function IntroBackground(props: { bg: Props['bg'] }) {
switch (props.bg) {
case 'borrow':
return <GridHole className='h-55' />
return <GridHole className='h-55 opacity-5' />
case 'lend':
return <GridTire className='h-55' />
return <GridTire className='h-55 opacity-5' />
case 'farm':
return <GridLandscape className='h-55' />
return <GridLandscape className='h-55 opacity-5' />
case 'hls-farm':
return <GridWeb className='h-45 opacity-5' />
case 'hls-staking':
return <GridPlanet className='h-55 opacity-10' />
default:
return <GridGlobe className='h-50' />
return <GridGlobe className='h-50 opacity-5' />
}
}
export default function Intro(props: Props) {
const showTutorial = useStore((s) => s.tutorial)
const isHLS = useStore((s) => s.isHLS)
if (!showTutorial) return null
return (
<Card
className={`relative w-full p-8 bg-intro bg-cover h-55`}
className={classNames(
'relative w-full p-8 bg-cover h-55',
isHLS ? 'bg-intro-hls' : 'bg-intro',
)}
contentClassName='flex w-full h-full flex-col justify-between'
>
<div className='absolute inset-0 flex items-end justify-end w-full h-full text-white opacity-5'>
<div className='absolute inset-0 flex items-end justify-end w-full h-full text-white'>
<IntroBackground bg={props.bg} />
</div>
<Tooltip

View File

@ -75,6 +75,7 @@ export default function AssetAmountSelectActionModal(props: Props) {
max={maxAmount}
hasSelect
maxText='Max'
warningMessages={[]}
/>
<Divider />
<Button

View File

@ -267,6 +267,7 @@ function BorrowModal(props: Props) {
disabled={max.isZero()}
className='w-full'
maxText='Max'
warningMessages={[]}
/>
{isRepay && maxRepayAmount.isZero() && <RepayNotAvailable asset={asset} />}
{!isRepay && (

View File

@ -112,6 +112,7 @@ export default function WithdrawFromAccount(props: Props) {
accountId={account.id}
hasSelect
maxText='Max'
warningMessages={[]}
/>
<Divider className='my-6' />
<div className='flex flex-wrap w-full'>

View File

@ -24,10 +24,9 @@ export default function CreateAccount() {
}
return (
<div className='p-4 flex-col flex'>
<div id='item-2' className='p-4 flex-col flex'>
<Text size='sm' className='text-white/50 mb-4 mt-2'>
Creating a HLS position mandates the creation of a, single-use HLS account. This account is
deleted once you close your position.
Depositing into a HLS strategy mandates the creation of a HLS credit account.
</Text>
<Button
onClick={handleBtnClick}

View File

@ -1,24 +1,32 @@
import React from 'react'
import React, { useMemo } from 'react'
import Button from 'components/Button'
import { ArrowRight } from 'components/Icons'
import LeverageSummary from 'components/Modals/HLS/Deposit/LeverageSummary'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import { getLeveragedApy } from 'utils/math'
interface Props {
amount: BigNumber
asset: Asset
asset: BorrowAsset
max: BigNumber
onChangeAmount: (amount: BigNumber) => void
onClickBtn: () => void
positionValue: BigNumber
leverage: number
maxLeverage: number
baseApy: number
warningMessages: string[]
}
export default function Leverage(props: Props) {
const apy = useMemo(() => {
if (!props.asset.borrowRate) return 0
return getLeveragedApy(props.baseApy, props.asset.borrowRate, props.leverage)
}, [props.asset.borrowRate, props.baseApy, props.leverage])
return (
<div className='flex-col gap-6 flex justify-between h-full p-4'>
<div id='item-1' className='flex-col gap-6 flex justify-between h-full p-4'>
<TokenInputWithSlider
amount={props.amount}
asset={props.asset}
@ -29,9 +37,10 @@ export default function Leverage(props: Props) {
current: props.leverage,
max: props.maxLeverage,
}}
warningMessages={props.warningMessages}
/>
<div className='flex flex-col gap-6'>
<LeverageSummary asset={props.asset} positionValue={props.positionValue} />
<LeverageSummary asset={props.asset} positionValue={props.positionValue} apy={apy} />
<Button onClick={props.onClickBtn} text='Continue' rightIcon={<ArrowRight />} />
</div>
</div>

View File

@ -6,6 +6,7 @@ import useBorrowAsset from 'hooks/useBorrowAsset'
interface Props {
asset: Asset
positionValue: BigNumber
apy: number
}
export default function LeverageSummary(props: Props) {
@ -13,11 +14,10 @@ export default function LeverageSummary(props: Props) {
const items: SummaryItem[] = useMemo(() => {
return [
// TODO: Get APY numbers
{
title: 'APY',
amount: 0,
options: { suffix: '%', minDecimals: 1, maxDecimals: 1 },
amount: props.apy,
options: { suffix: '%', minDecimals: 2, maxDecimals: 2 },
},
{
title: `Borrow APR ${props.asset.symbol}`,
@ -30,7 +30,7 @@ export default function LeverageSummary(props: Props) {
options: { prefix: '$' },
},
]
}, [borrowAsset?.borrowRate, props.asset.symbol, props.positionValue])
}, [borrowAsset?.borrowRate, props.apy, props.asset.symbol, props.positionValue])
return <SummaryItems items={items} />
}

View File

@ -10,17 +10,20 @@ interface Props {
max: BigNumber
onChangeAmount: (amount: BigNumber) => void
onClickBtn: () => void
depositCapLeft: BigNumber
warningMessages: string[]
}
export default function ProvideCollateral(props: Props) {
return (
<div className='p-4 flex-col gap-6 flex'>
<div id='item-0' className='p-4 flex-col gap-6 flex'>
<TokenInputWithSlider
maxText='In wallet'
amount={props.amount}
asset={props.asset}
max={props.max}
onChange={props.onChangeAmount}
warningMessages={props.warningMessages}
/>
<Button onClick={props.onClickBtn} text='Continue' rightIcon={<ArrowRight />} />
</div>

View File

@ -18,7 +18,7 @@ export default function CreateAccount(props: Props) {
}
return (
<div className='flex-col flex'>
<div id='item-2' className='flex-col flex'>
{props.hlsAccounts.map((account, index) => (
<div
key={account.id}

View File

@ -1,17 +1,25 @@
import classNames from 'classnames'
import React from 'react'
import DisplayCurrency from 'components/DisplayCurrency'
import { ExclamationMarkTriangle } from 'components/Icons'
import Text from 'components/Text'
import WarningMessages from 'components/WarningMessages'
import { BNCoin } from 'types/classes/BNCoin'
import { formatAmountWithSymbol, formatLeverage } from 'utils/formatters'
interface SubTitleProps {
text: string
color?: string
}
export function SubTitle(props: SubTitleProps) {
return (
<Text className='text-white/60 mt-1' size='xs' tag='span'>
<Text
className={classNames(props.color ? props.color : 'text-white/60', 'mt-1')}
size='xs'
tag='span'
>
{props.text}
</Text>
)
@ -21,18 +29,34 @@ interface CollateralSubTitleProps {
amount: BigNumber
denom: string
isOpen: boolean
warningMessages: string[]
}
export function CollateralSubTitle(props: CollateralSubTitleProps) {
if (props.isOpen || props.amount.isZero()) return null
if (props.isOpen) return null
if (!props.isOpen && props.amount.isZero()) {
return (
<div className='flex gap-2'>
<ExclamationMarkTriangle width={24} className='text-warning' />
<Text className='text-warning mt-1' size='xs' tag='span'>
You have not provided any collateral.
</Text>
</div>
)
}
return (
<SubTitle
text={formatAmountWithSymbol({
denom: props.denom,
amount: props.amount.toString(),
})}
/>
<div className='flex items-center gap-1'>
<WarningMessages messages={props.warningMessages} />
<SubTitle
text={formatAmountWithSymbol({
denom: props.denom,
amount: props.amount.toString(),
})}
color={props.warningMessages.length > 0 ? 'text-warning' : ''}
/>
</div>
)
}
@ -40,17 +64,55 @@ interface LeveragedSubTitleProps {
isOpen: boolean
leverage: number
positionValue: BigNumber
warningMessages: string[]
}
export function LeverageSubTitle(props: LeveragedSubTitleProps) {
if (props.isOpen || props.leverage <= 1) return null
return (
<>
<SubTitle text={`${formatLeverage(props.leverage)} • Total Position Value `} />
<div className='flex items-center gap-1'>
<WarningMessages messages={props.warningMessages} />
<SubTitle
text={`${formatLeverage(props.leverage)} • Total Position Value `}
color={props.warningMessages.length > 0 ? 'text-warning' : ''}
/>
<DisplayCurrency
coin={BNCoin.fromDenomAndBigNumber('usd', props.positionValue)}
className='text-white/60 text-xs inline'
className={classNames(
'text-xs inline mt-1',
props.warningMessages.length ? 'text-warning' : 'text-white/60',
)}
/>
</>
</div>
)
}
interface SelectAccountSubTitleProps {
isOpen: boolean
isSummaryOpen: boolean
selectedAccountId?: string
type: 'create' | 'select'
}
export function SelectAccountSubTitle(props: SelectAccountSubTitleProps) {
if (!props.isOpen && props.selectedAccountId) {
return (
<Text className='mt-1 text-white/60' size='xs' tag='span'>
Account {props.selectedAccountId}
</Text>
)
}
if (!props.selectedAccountId && props.isSummaryOpen) {
return (
<div className='flex gap-2'>
<ExclamationMarkTriangle width={24} className='text-warning' />
<Text className='text-warning mt-1' size='xs' tag='span'>
You need to {props.type} an account
</Text>
</div>
)
}
return ''
}

View File

@ -18,7 +18,7 @@ interface Props {
export default function YourPosition(props: Props) {
const netApy = useMemo(
() => props.baseApy * props.leverage - props.borrowRate,
() => props.baseApy * props.leverage - props.borrowRate * (props.leverage - 1),
[props.baseApy, props.borrowRate, props.leverage],
)
const apyItems = useMemo(
@ -33,7 +33,7 @@ export default function YourPosition(props: Props) {
},
{
title: 'Borrow Rate',
amount: props.borrowRate,
amount: props.borrowRate * (props.leverage - 1),
},
],
[props.baseApy, props.borrowRate, props.leverage],

View File

@ -16,6 +16,7 @@ interface Props {
leverage: number
onClickBtn: () => void
positionValue: BigNumber
disabled: boolean
}
export default function Summary(props: Props) {
@ -24,7 +25,7 @@ export default function Summary(props: Props) {
if (!borrowAsset) return null
return (
<div className='p-4 flex flex-col gap-4'>
<div id='item-3' className='p-4 flex flex-col gap-4'>
<AssetSummary asset={props.collateralAsset} amount={props.depositAmount} />
<AssetSummary asset={borrowAsset} amount={props.borrowAmount} isBorrow />
<YourPosition
@ -38,6 +39,7 @@ export default function Summary(props: Props) {
text='Approve Funding Transaction'
rightIcon={<ArrowRight />}
className='mt-1'
disabled={props.disabled}
/>
</div>
)

View File

@ -13,7 +13,7 @@ import useStore from 'store'
import { isAccountEmpty } from 'utils/accounts'
interface Props {
borrowAsset: Asset
borrowAsset: BorrowAsset
collateralAsset: Asset
vaultAddress: string | null
strategy?: HLSStrategy
@ -24,10 +24,15 @@ export default function Controller(props: Props) {
const [isOpen, toggleIsOpen] = useIsOpenArray(4, false)
const address = useStore((s) => s.address)
const { data: hlsAccounts } = useAccounts('high_levered_strategy', address)
const emptyHlsAccounts = useMemo(
() => hlsAccounts.filter((account) => isAccountEmpty(account)),
[hlsAccounts],
)
const emptyHlsAccounts = useMemo(() => {
const emptyAccounts = hlsAccounts.filter((account) => isAccountEmpty(account))
if (emptyAccounts.length > 0 && selectedAccount.id === 'default') {
setSelectedAccount(emptyAccounts[0])
}
return emptyAccounts
}, [hlsAccounts, selectedAccount])
const walletCollateralAsset = useCurrentWalletBalance(props.collateralAsset.denom)
const vault = useVault(props.vaultAddress || '')
@ -68,7 +73,7 @@ export default function Controller(props: Props) {
}
interface ContentProps {
borrowAsset: Asset
borrowAsset: BorrowAsset
collateralAsset: Asset
emptyHlsAccounts: Account[]
hlsAccounts: Account[]
@ -166,7 +171,7 @@ function StakingContent(props: StakingContentProps) {
toggleIsOpen: props.toggleIsOpen,
updatedAccount,
maxBorrowAmount,
apy: props.strategy.apy || 0, // TODO: Implement APY
apy: props.strategy.apy || 0,
walletCollateralAsset: props.walletCollateralAsset,
})

View File

@ -1,4 +1,4 @@
import React, { useMemo } from 'react'
import React, { useEffect, useMemo } from 'react'
import CreateAccount from 'components/Modals/HLS/Deposit/CreateAccount'
import Leverage from 'components/Modals/HLS/Deposit/Leverage'
@ -7,15 +7,25 @@ import SelectAccount from 'components/Modals/HLS/Deposit/SelectAccount'
import {
CollateralSubTitle,
LeverageSubTitle,
SubTitle,
SelectAccountSubTitle,
} from 'components/Modals/HLS/Deposit/SubTitles'
import Summary from 'components/Modals/HLS/Deposit/Summary'
import { BN_ZERO } from 'constants/math'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { getCoinAmount, getCoinValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
import {
getDepositCapMessage,
getHealthFactorMessage,
getLiquidityMessage,
getNoBalanceMessage,
} from 'utils/messages'
interface Props {
apy: number
borrowAmount: BigNumber
borrowAsset: Asset
borrowAsset: BorrowAsset
collateralAsset: Asset
depositAmount: BigNumber
emptyHlsAccounts: Account[]
@ -36,6 +46,80 @@ interface Props {
}
export default function useAccordionItems(props: Props) {
const { data: prices } = usePrices()
const depositCapLeft = useMemo(() => {
if (!props.strategy) return BN_ZERO
return props.strategy?.depositCap.max.minus(props.strategy.depositCap.used)
}, [props.strategy])
const borrowLiquidity = useMemo(
() => props.borrowAsset.liquidity?.amount || BN_ZERO,
[props.borrowAsset.liquidity?.amount],
)
const additionalDepositFromSwap = useMemo(() => {
const value = getCoinValue(
BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, props.borrowAmount),
prices,
)
return getCoinAmount(props.collateralAsset.denom, value, prices)
}, [prices, props.borrowAmount, props.borrowAsset.denom, props.collateralAsset])
const collateralWarningMessages = useMemo(() => {
const messages: string[] = []
if (!props.walletCollateralAsset?.amount) {
messages.push(getNoBalanceMessage(props.collateralAsset.symbol))
}
if (props.depositAmount.isGreaterThan(depositCapLeft)) {
messages.push(getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'deposit'))
}
return messages
}, [
depositCapLeft,
props.collateralAsset.denom,
props.collateralAsset.symbol,
props.depositAmount,
props.walletCollateralAsset?.amount,
])
const borrowWarningMessages = useMemo(() => {
const messages: string[] = []
if (props.borrowAmount.isGreaterThan(props.maxBorrowAmount)) {
messages.push(
getHealthFactorMessage(props.borrowAsset.denom, props.maxBorrowAmount, 'borrow'),
)
}
if (props.borrowAmount.isGreaterThan(borrowLiquidity)) {
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
}
if (additionalDepositFromSwap.plus(props.depositAmount).isGreaterThan(props.maxBorrowAmount)) {
messages.push(getDepositCapMessage(props.borrowAsset.denom, depositCapLeft, 'borrow'))
}
return messages
}, [
additionalDepositFromSwap,
borrowLiquidity,
depositCapLeft,
props.borrowAmount,
props.borrowAsset.denom,
props.depositAmount,
props.maxBorrowAmount,
])
useEffect(() => {
const element = document.getElementById(`item-${props.isOpen.findIndex((v) => v)}`)
if (element) {
element.scrollIntoView({ behavior: 'smooth' })
}
}, [props.isOpen])
return useMemo(() => {
return [
{
@ -46,8 +130,9 @@ export default function useAccordionItems(props: Props) {
onChangeAmount={props.onChangeCollateral}
asset={props.collateralAsset}
onClickBtn={() => props.toggleIsOpen(1)}
// TODO: Add check for deposit cap
max={BN(props.walletCollateralAsset?.amount || 0)}
depositCapLeft={depositCapLeft}
warningMessages={collateralWarningMessages}
/>
),
renderSubTitle: () => (
@ -55,6 +140,7 @@ export default function useAccordionItems(props: Props) {
isOpen={props.isOpen[0]}
amount={props.depositAmount}
denom={props.collateralAsset.denom}
warningMessages={collateralWarningMessages}
/>
),
isOpen: props.isOpen[0],
@ -73,6 +159,8 @@ export default function useAccordionItems(props: Props) {
max={props.maxBorrowAmount}
positionValue={props.positionValue}
maxLeverage={props.strategy?.maxLeverage || 1}
baseApy={props.strategy?.apy || 0}
warningMessages={borrowWarningMessages}
/>
),
renderSubTitle: () => (
@ -80,6 +168,7 @@ export default function useAccordionItems(props: Props) {
leverage={props.leverage}
isOpen={props.isOpen[1]}
positionValue={props.positionValue}
warningMessages={borrowWarningMessages}
/>
),
isOpen: props.isOpen[1],
@ -97,17 +186,28 @@ export default function useAccordionItems(props: Props) {
onClickBtn={() => props.toggleIsOpen(3)}
/>
),
renderSubTitle: () =>
props.selectedAccount && !props.isOpen[2] ? (
<SubTitle text={`Account ${props.selectedAccount.id}`} />
) : null,
renderSubTitle: () => (
<SelectAccountSubTitle
isOpen={props.isOpen[2]}
isSummaryOpen={props.isOpen[3] || props.isOpen.every((i) => !i)}
selectedAccountId={props.selectedAccount?.id}
type='select'
/>
),
isOpen: props.isOpen[2],
toggleOpen: props.toggleIsOpen,
}
: {
title: 'Create HLS Account',
renderContent: () => <CreateAccount />,
renderSubTitle: () => null,
renderSubTitle: () => (
<SelectAccountSubTitle
isOpen={props.isOpen[2]}
isSummaryOpen={props.isOpen[3] || props.isOpen.every((i) => !i)}
selectedAccountId={props.selectedAccount?.id}
type='create'
/>
),
isOpen: props.isOpen[2],
toggleOpen: props.toggleIsOpen,
},
@ -124,6 +224,12 @@ export default function useAccordionItems(props: Props) {
borrowAsset={props.borrowAsset}
apy={props.apy}
onClickBtn={props.execute}
disabled={
collateralWarningMessages.length > 0 ||
borrowWarningMessages.length > 0 ||
props.depositAmount.isZero() ||
!props.selectedAccount
}
/>
),
renderSubTitle: () => null,
@ -131,5 +237,5 @@ export default function useAccordionItems(props: Props) {
toggleOpen: props.toggleIsOpen,
},
]
}, [props])
}, [borrowWarningMessages, collateralWarningMessages, depositCapLeft, props])
}

View File

@ -42,17 +42,11 @@ export default function useVaultController(props: Props) {
const { updatedAccount, simulateVaultDeposit } = useUpdatedAccount(selectedAccount)
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
const maxBorrowAmount = useMemo(
// TODO: Check that the amount is actually the HLS amount
// TODO: Add check for market liquidity
// TODO: Add check for deposit cap
() => {
return computeMaxBorrowAmount(props.borrowAsset.denom, {
vault: { address: props.vault?.address },
}).plus(borrowAmount)
},
[borrowAmount, computeMaxBorrowAmount, props.borrowAsset.denom, props.vault?.address],
)
const maxBorrowAmount = useMemo(() => {
return computeMaxBorrowAmount(props.borrowAsset.denom, {
vault: { address: props.vault?.address },
}).plus(borrowAmount)
}, [borrowAmount, computeMaxBorrowAmount, props.borrowAsset.denom, props.vault?.address])
const execute = useCallback(() => {
depositIntoVault({

View File

@ -15,19 +15,26 @@ import { BNCoin } from 'types/classes/BNCoin'
import { getAccountPositionValues } from 'utils/accounts'
import { getHlsStakingChangeLevActions } from 'utils/actions'
import { byDenom } from 'utils/array'
import { getLeveragedApy } from 'utils/math'
import { getDepositCapMessage, getHealthFactorMessage, getLiquidityMessage } from 'utils/messages'
interface Props {
account: HLSAccountWithStrategy
action: HlsStakingManageAction
borrowAsset: Asset
borrowAsset: BorrowAsset
collateralAsset: Asset
}
export default function ChangeLeverage(props: Props) {
const { data: prices } = usePrices()
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
const { updatedAccount, simulateHlsStakingDeposit, simulateHlsStakingWithdraw, leverage } =
useUpdatedAccount(props.account)
const {
updatedAccount,
simulateHlsStakingDeposit,
simulateHlsStakingWithdraw,
leverage,
addedTrades,
} = useUpdatedAccount(props.account)
const changeHlsStakingLeverage = useStore((s) => s.changeHlsStakingLeverage)
const { computeMaxBorrowAmount } = useHealthComputer(props.account)
@ -98,6 +105,50 @@ export default function ChangeLeverage(props: Props) {
slippage,
])
const addedDepositAmount = useMemo(
() => addedTrades.find(byDenom(props.collateralAsset.denom))?.amount || BN_ZERO,
[addedTrades, props.collateralAsset.denom],
)
const depositCapLeft = useMemo(
() => props.account.strategy.depositCap.max.minus(props.account.strategy.depositCap.used),
[props.account.strategy.depositCap.max, props.account.strategy.depositCap.used],
)
const apy = useMemo(() => {
if (!props.borrowAsset.borrowRate || !props.account.strategy.apy) return 0
return getLeveragedApy(props.account.strategy.apy, props.borrowAsset.borrowRate, leverage)
}, [leverage, props.account.strategy.apy, props.borrowAsset.borrowRate])
const warningMessages = useMemo(() => {
const messages: string[] = []
const borrowLiquidity = props.borrowAsset.liquidity?.amount || BN_ZERO
if (borrowLiquidity.isLessThan(currentDebt.minus(previousDebt))) {
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
}
if (maxBorrowAmount.isLessThan(currentDebt)) {
messages.push(getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow'))
}
if (addedDepositAmount.isGreaterThan(depositCapLeft)) {
messages.push(getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'borrow'))
}
return messages
}, [
addedDepositAmount,
currentDebt,
depositCapLeft,
maxBorrowAmount,
previousDebt,
props.borrowAsset.denom,
props.borrowAsset.liquidity?.amount,
props.collateralAsset.denom,
])
return (
<>
<TokenInputWithSlider
@ -110,10 +161,15 @@ export default function ChangeLeverage(props: Props) {
current: leverage,
max: props.account.strategy.maxLeverage,
}}
warningMessages={warningMessages}
/>
<div className='flex flex-col gap-6'>
<LeverageSummary asset={props.borrowAsset} positionValue={positionValue} />
<Button onClick={handleOnClick} text='Confirm' />
<LeverageSummary asset={props.borrowAsset} positionValue={positionValue} apy={apy} />
<Button
onClick={handleOnClick}
text='Confirm'
disabled={currentDebt.isEqualTo(previousDebt) || warningMessages.length !== 0}
/>
</div>
</>
)

View File

@ -1,5 +1,5 @@
import BigNumber from 'bignumber.js'
import React, { useCallback, useEffect, useMemo } from 'react'
import React, { useCallback, useMemo } from 'react'
import Button from 'components/Button'
import Divider from 'components/Divider'
@ -21,18 +21,25 @@ import { calculateAccountLeverage } from 'utils/accounts'
import { byDenom } from 'utils/array'
import { getCoinAmount, getCoinValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
import {
getDepositCapMessage,
getHealthFactorMessage,
getLiquidityMessage,
getNoBalanceMessage,
} from 'utils/messages'
interface Props {
account: Account
account: HLSAccountWithStrategy
action: HlsStakingManageAction
borrowAsset: Asset
borrowAsset: BorrowAsset
collateralAsset: Asset
}
export default function Deposit(props: Props) {
const { addedDeposits, addedDebts, updatedAccount, simulateHlsStakingDeposit } =
const { addedDeposits, addedDebts, updatedAccount, addedTrades, simulateHlsStakingDeposit } =
useUpdatedAccount(props.account)
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
const { data: prices } = usePrices()
const [keepLeverage, toggleKeepLeverage] = useToggle(true)
const collateralAssetAmountInWallet = BN(
@ -55,6 +62,11 @@ export default function Deposit(props: Props) {
[addedDeposits, props.collateralAsset.denom],
)
const addedDepositFromSwap = useMemo(
() => addedTrades.find(byDenom(props.collateralAsset.denom))?.amount || BN_ZERO,
[addedTrades, props.collateralAsset.denom],
)
const borrowCoin = useMemo(
() =>
BNCoin.fromDenomAndBigNumber(
@ -64,25 +76,54 @@ export default function Deposit(props: Props) {
[addedDebts, props.borrowAsset.denom],
)
const warningMessages = useMemo(() => {
let messages: string[] = []
const capLeft = props.account.strategy.depositCap.max.minus(
props.account.strategy.depositCap.used,
)
if (capLeft.isLessThan(depositCoin.amount.plus(addedDepositFromSwap))) {
messages.push(getDepositCapMessage(props.collateralAsset.denom, capLeft, 'deposit'))
}
if (collateralAssetAmountInWallet.isZero()) {
messages.push(getNoBalanceMessage(props.collateralAsset.symbol))
}
return messages
}, [
addedDepositFromSwap,
collateralAssetAmountInWallet,
depositCoin.amount,
props.account.strategy.depositCap.max,
props.account.strategy.depositCap.used,
props.collateralAsset.denom,
props.collateralAsset.symbol,
])
const maxBorrowAmount = useMemo(
() => computeMaxBorrowAmount(props.collateralAsset.denom, 'deposit'),
[computeMaxBorrowAmount, props.collateralAsset.denom],
() => computeMaxBorrowAmount(props.borrowAsset.denom, 'deposit'),
[computeMaxBorrowAmount, props.borrowAsset.denom],
)
useEffect(() => {
const borrowWarningMessages = useMemo(() => {
let messages: string[] = []
if (borrowCoin.amount.isGreaterThan(maxBorrowAmount)) {
simulateHlsStakingDeposit(
BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, depositCoin.amount),
BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, maxBorrowAmount),
)
messages.push(getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow'))
}
const borrowLiquidity = props.borrowAsset.liquidity?.amount || BN_ZERO
if (borrowCoin.amount.isGreaterThan(borrowLiquidity)) {
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
}
return messages
}, [
borrowCoin.amount,
depositCoin.amount,
maxBorrowAmount,
props.borrowAsset.denom,
props.collateralAsset.denom,
simulateHlsStakingDeposit,
props.borrowAsset.liquidity?.amount,
])
const actions = useDepositActions({ depositCoin, borrowCoin })
@ -146,6 +187,7 @@ export default function Deposit(props: Props) {
{
title: 'Additional Borrow Amount',
amount: borrowCoin.amount.toNumber(),
warningMessages: borrowWarningMessages,
options: {
suffix: ` ${props.borrowAsset.symbol}`,
abbreviated: true,
@ -167,6 +209,7 @@ export default function Deposit(props: Props) {
[
borrowCoin.amount,
borrowRate,
borrowWarningMessages,
currentDebt,
keepLeverage,
props.borrowAsset.decimals,
@ -183,6 +226,7 @@ export default function Deposit(props: Props) {
max={collateralAssetAmountInWallet}
onChange={handleOnChange}
maxText='In Wallet'
warningMessages={warningMessages}
/>
<Divider className='my-6' />
<div className='flex flex-wrap items-center justify-between flex-1'>
@ -197,7 +241,15 @@ export default function Deposit(props: Props) {
</div>
<div className='flex flex-col gap-4'>
<SummaryItems items={items} />
<Button onClick={handleDeposit} text='Deposit' disabled={depositCoin.amount.isZero()} />
<Button
onClick={handleDeposit}
text='Deposit'
disabled={
depositCoin.amount.isZero() ||
!!warningMessages.length ||
!!borrowWarningMessages.length
}
/>
</div>
</>
)

View File

@ -11,6 +11,7 @@ import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { BN } from 'utils/helpers'
import { getNoBalanceMessage } from 'utils/messages'
interface Props {
account: Account
@ -80,18 +81,30 @@ export default function Repay(props: Props) {
[props.borrowAsset.denom, removeDebts],
)
const warningMessages = useMemo(() => {
if (borrowAssetAmountInWallet.isZero()) {
return [getNoBalanceMessage(props.borrowAsset.symbol)]
}
return []
}, [borrowAssetAmountInWallet, props.borrowAsset.symbol])
return (
<>
<TokenInputWithSlider
amount={removedDebts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO}
amount={repayAmount}
asset={props.borrowAsset}
max={maxRepayAmount}
onChange={handleOnChange}
maxText='In Wallet'
warningMessages={warningMessages}
/>
<div className='flex flex-col gap-4'>
<SummaryItems items={items} />
<Button onClick={handleRepay} text='Repay' />
<Button
onClick={handleRepay}
text='Repay'
disabled={warningMessages.length !== 0 || repayAmount.isZero()}
/>
</div>
</>
)

View File

@ -8,6 +8,7 @@ import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { byDenom } from 'utils/array'
import { getHealthFactorMessage } from 'utils/messages'
interface Props {
account: Account
@ -47,16 +48,30 @@ export default function Withdraw(props: Props) {
})
}, [props.account.id, removedDeposit, withdraw])
const withdrawAmount = useMemo(() => removedDeposit?.amount || BN_ZERO, [removedDeposit?.amount])
const warningMessages = useMemo(() => {
if (maxWithdrawAmount.isLessThan(withdrawAmount) || maxWithdrawAmount.isZero()) {
return [getHealthFactorMessage(props.collateralAsset.denom, maxWithdrawAmount, 'withdraw')]
}
return []
}, [maxWithdrawAmount, props.collateralAsset.denom, withdrawAmount])
return (
<>
<TokenInputWithSlider
amount={removedDeposit?.amount || BN_ZERO}
amount={withdrawAmount}
asset={props.collateralAsset}
max={maxWithdrawAmount}
onChange={handleChange}
maxText='Available'
warningMessages={warningMessages}
/>
<Button
onClick={onClick}
text='Withdraw'
disabled={withdrawAmount.isZero() || warningMessages.length !== 0}
/>
<Button onClick={onClick} text='Withdraw' disabled={removedDeposit?.amount?.isZero()} />
</>
)
}

View File

@ -7,6 +7,7 @@ import Repay from 'components/Modals/HLS/Manage/Repay'
import Withdraw from 'components/Modals/HLS/Manage/Withdraw'
import ModalContentWithSummary from 'components/Modals/ModalContentWithSummary'
import useAccount from 'hooks/useAccount'
import useBorrowAsset from 'hooks/useBorrowAsset'
import useStore from 'store'
import { getAssetByDenom } from 'utils/assets'
@ -14,7 +15,7 @@ export default function HlsManageModalController() {
const modal = useStore((s) => s.hlsManageModal)
const { data: account } = useAccount(modal?.accountId)
const collateralAsset = getAssetByDenom(modal?.staking.strategy.denoms.deposit || '')
const borrowAsset = getAssetByDenom(modal?.staking.strategy.denoms.borrow || '')
const borrowAsset = useBorrowAsset(modal?.staking.strategy.denoms.borrow || '')
if (!modal || !collateralAsset || !borrowAsset || !account) return null
@ -31,7 +32,7 @@ export default function HlsManageModalController() {
interface Props {
account: HLSAccountWithStrategy
action: HlsStakingManageAction
borrowAsset: Asset
borrowAsset: BorrowAsset
collateralAsset: Asset
}
@ -59,6 +60,7 @@ function HlsModal(props: Props) {
return (
<ModalContentWithSummary
account={props.account}
isHls
header={
<Header
action={props.action}

View File

@ -3,6 +3,7 @@ import React from 'react'
import Modal from 'components/Modal'
import Content from 'components/Modals/HLS/Deposit'
import Header from 'components/Modals/HLS/Header'
import useBorrowAsset from 'hooks/useBorrowAsset'
import useStore from 'store'
import { getAssetByDenom } from 'utils/assets'
@ -12,9 +13,8 @@ export default function HlsModalController() {
const primaryAsset = getAssetByDenom(
modal?.vault?.denoms.primary || modal?.strategy?.denoms.deposit || '',
)
const secondaryAsset = getAssetByDenom(
modal?.vault?.denoms.secondary || modal?.strategy?.denoms.borrow || '',
)
const secondaryAsset = useBorrowAsset(modal?.strategy?.denoms.borrow || '')
if (!primaryAsset || !secondaryAsset) return null
@ -41,7 +41,7 @@ export default function HlsModalController() {
interface Props {
primaryAsset: Asset
secondaryAsset: Asset
secondaryAsset: BorrowAsset
strategy?: HLSStrategy
vaultAddress: string | null
}

View File

@ -8,6 +8,7 @@ import Modal, { ModalProps } from 'components/Modal'
import useStore from 'store'
interface Props extends ModalProps {
isHls?: boolean
account?: Account
isContentCard?: boolean
subHeader?: React.ReactNode
@ -47,7 +48,9 @@ export default function ModalContentWithSummary(props: Props) {
>
{props.subHeader && props.subHeader}
{modalContent(props.content, props.isContentCard, props.account)}
{props.account && <AccountSummary account={updatedAccount || props.account} />}
{props.account && (
<AccountSummary account={updatedAccount || props.account} isHls={props.isHls} />
)}
</Modal>
)
}

View File

@ -189,6 +189,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
maxText='Max Borrow'
onChange={(amount) => updateAssets(coin.denom, amount)}
onDelete={() => onDelete(coin.denom)}
warningMessages={[]}
/>
)
})}

View File

@ -212,7 +212,7 @@ export default function VaultDeposit(props: Props) {
max={availablePrimaryAmount}
maxText='Balance'
asset={primaryAsset}
warning={availablePrimaryAmount.isZero() ? getWarningText(primaryAsset) : undefined}
warningMessages={availablePrimaryAmount.isZero() ? [getWarningText(primaryAsset)] : []}
disabled={disableInput}
/>
{!props.isCustomRatio && (
@ -224,8 +224,10 @@ export default function VaultDeposit(props: Props) {
max={availableSecondaryAmount}
maxText='Balance'
asset={secondaryAsset}
warning={availableSecondaryAmount.isZero() ? getWarningText(secondaryAsset) : undefined}
disabled={disableInput}
warningMessages={
availableSecondaryAmount.isZero() ? [getWarningText(secondaryAsset)] : []
}
/>
</div>
</div>

View File

@ -7,6 +7,7 @@ import { MAX_AMOUNT_DECIMALS } from 'constants/math'
import useAccount from 'hooks/useAccount'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices'
import { getAccountSummaryStats } from 'utils/accounts'
@ -23,6 +24,7 @@ function Content(props: Props) {
const { data } = useBorrowMarketAssetsTableData(false)
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
const { data: hlsStrategies } = useHLSStakingAssets()
const stats = useMemo(() => {
if (!account || !borrowAssets.length || !lendingAssets.length) return DEFAULT_PORTFOLIO_STATS
@ -32,6 +34,8 @@ function Content(props: Props) {
prices,
borrowAssets,
lendingAssets,
hlsStrategies,
account.kind === 'high_levered_strategy',
)
return [
@ -72,7 +76,7 @@ function Content(props: Props) {
sub: DEFAULT_PORTFOLIO_STATS[4].sub,
},
]
}, [account, borrowAssets, lendingAssets, prices])
}, [account, borrowAssets, hlsStrategies, lendingAssets, prices])
return (
<Skeleton

View File

@ -12,6 +12,7 @@ import useAccount from 'hooks/useAccount'
import useAccountId from 'hooks/useAccountId'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import useLocalStorage from 'hooks/useLocalStorage'
import usePrices from 'hooks/usePrices'
@ -34,6 +35,8 @@ export default function PortfolioCard(props: Props) {
const currentAccountId = useAccountId()
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
const { data } = useBorrowMarketAssetsTableData(false)
const { data: hlsStrategies } = useHLSStakingAssets()
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
const [reduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
@ -52,8 +55,15 @@ export default function PortfolioCard(props: Props) {
const apr = useMemo(() => {
if (!lendingAssets.length || !borrowAssets.length || !prices.length || !account) return null
return calculateAccountApr(account, borrowAssets, lendingAssets, prices)
}, [lendingAssets, borrowAssets, prices, account])
return calculateAccountApr(
account,
borrowAssets,
lendingAssets,
prices,
hlsStrategies,
account.kind === 'high_levered_strategy',
)
}, [lendingAssets, borrowAssets, prices, account, hlsStrategies])
const stats: { title: ReactNode; sub: string }[] = useMemo(() => {
const isLoaded = account && prices.length && apr !== null

View File

@ -7,6 +7,7 @@ import SummarySkeleton from 'components/Portfolio/SummarySkeleton'
import { MAX_AMOUNT_DECIMALS } from 'constants/math'
import useAccounts from 'hooks/useAccounts'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
@ -21,6 +22,7 @@ export default function PortfolioSummary() {
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
const { data: accounts } = useAccounts('default', urlAddress || walletAddress)
const { data: hlsStrategies } = useHLSStakingAssets()
const stats = useMemo(() => {
if (!accounts?.length) return
@ -47,6 +49,7 @@ export default function PortfolioSummary() {
prices,
borrowAssets,
lendingAssets,
hlsStrategies,
)
return [
@ -87,7 +90,7 @@ export default function PortfolioSummary() {
sub: 'Combined leverage',
},
]
}, [accounts, borrowAssets, lendingAssets, prices])
}, [accounts, borrowAssets, hlsStrategies, lendingAssets, prices])
if (!walletAddress && !urlAddress) return null

View File

@ -3,12 +3,14 @@ import { Navigate, Outlet, Route, Routes as RoutesWrapper } from 'react-router-d
import Layout from 'pages/_layout'
import BorrowPage from 'pages/BorrowPage'
import FarmPage from 'pages/FarmPage'
import HLSFarmPage from 'pages/HLSFarmPage'
import HLSStakingPage from 'pages/HLSStakingPage'
import LendPage from 'pages/LendPage'
import MobilePage from 'pages/MobilePage'
import PortfolioAccountPage from 'pages/PortfolioAccountPage'
import PortfolioPage from 'pages/PortfolioPage'
import TradePage from 'pages/TradePage'
import { ENABLE_HLS } from 'utils/constants'
export default function Routes() {
return (
@ -26,8 +28,8 @@ export default function Routes() {
<Route path='/borrow' element={<BorrowPage />} />
<Route path='/portfolio' element={<PortfolioPage />} />
<Route path='/mobile' element={<MobilePage />} />
<Route path='/hls-staking' element={<HLSStakingPage />} />
{/*<Route path='/hls-farm' element={<HLSFarmPage />} />*/}
{ENABLE_HLS && <Route path='/hls-staking' element={<HLSStakingPage />} />}
{ENABLE_HLS && <Route path='/hls-farm' element={<HLSFarmPage />} />}
<Route path='/' element={<TradePage />} />
<Route path='/wallets/:address'>
<Route path='trade' element={<TradePage />} />
@ -35,11 +37,11 @@ export default function Routes() {
<Route path='lend' element={<LendPage />} />
<Route path='borrow' element={<BorrowPage />} />
<Route path='portfolio' element={<PortfolioPage />} />
<Route path='hls-staking' element={<HLSStakingPage />} />
{ENABLE_HLS && <Route path='hls-staking' element={<HLSStakingPage />} />}
{ENABLE_HLS && <Route path='hls-farm' element={<HLSFarmPage />} />}
<Route path='portfolio/:accountId'>
<Route path='' element={<PortfolioAccountPage />} />
</Route>
{/*<Route path='hls-farm' element={<HLSFarmPage />} />*/}
<Route path='' element={<TradePage />} />
</Route>
<Route path='*' element={<Navigate to='/' />} />

View File

@ -1,5 +1,5 @@
import classNames from 'classnames'
import { ChangeEvent, useEffect, useMemo, useRef, useState } from 'react'
import { ChangeEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import Draggable from 'react-draggable'
import { OverlayMark } from 'components/Icons'
@ -34,7 +34,7 @@ export default function Slider(props: Props) {
const nodeRef = useRef(null)
const [isDragging, setIsDragging] = useToggle()
function handleSliderRect() {
const handleSliderRect = useCallback(() => {
const leftCap = ref.current?.getBoundingClientRect().left ?? 0
const rightCap = ref.current?.getBoundingClientRect().right ?? 0
const newSliderWidth = ref.current?.getBoundingClientRect().width ?? 0
@ -50,7 +50,7 @@ export default function Slider(props: Props) {
width: newSliderWidth,
})
}
}
}, [sliderRect.left, sliderRect.right, sliderRect.width])
function handleDrag(e: any) {
if (!isDragging) {
@ -107,7 +107,7 @@ export default function Slider(props: Props) {
useEffect(() => {
handleSliderRect()
}, [])
}, [handleSliderRect])
return (
<div>

View File

@ -1,7 +1,9 @@
import classNames from 'classnames'
import React from 'react'
import { FormattedNumber } from 'components/FormattedNumber'
import Text from 'components/Text'
import WarningMessages from 'components/WarningMessages'
interface Props {
items: SummaryItem[]
@ -13,11 +15,17 @@ export default function SummaryItems(props: Props) {
{props.items.map((item) => (
<React.Fragment key={item.title}>
<Text className='text-white/60 text-sm'>{item.title}</Text>
<FormattedNumber
className='place-self-end text-sm'
amount={item.amount}
options={item.options}
/>
<span className='flex justify-end'>
<WarningMessages messages={item.warningMessages || []} />
<FormattedNumber
className={classNames(
'place-self-end text-sm',
item.warningMessages?.length && 'text-warning',
)}
amount={item.amount}
options={item.options}
/>
</span>
</React.Fragment>
))}
</div>

View File

@ -23,6 +23,7 @@ interface Props {
current: number
max: number
}
warningMessages: string[]
}
export default function TokenInputWithSlider(props: Props) {
@ -70,6 +71,7 @@ export default function TokenInputWithSlider(props: Props) {
hasSelect={props.hasSelect}
balances={props.balances}
accountId={props.accountId}
warningMessages={props.warningMessages}
/>
<Slider
value={percentage || 0}

View File

@ -5,11 +5,11 @@ import AssetImage from 'components/Asset/AssetImage'
import Button from 'components/Button'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { ExclamationMarkTriangle, TrashBin } from 'components/Icons'
import { TrashBin } from 'components/Icons'
import NumberInput from 'components/NumberInput'
import Select from 'components/Select'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import WarningMessages from 'components/WarningMessages'
import { ASSETS } from 'constants/assets'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
@ -26,9 +26,9 @@ interface Props {
disabled?: boolean
hasSelect?: boolean
maxText?: string
warning?: string
onChangeAsset?: (asset: Asset) => void
onDelete?: () => void
warningMessages: string[]
}
export default function TokenInput(props: Props) {
@ -55,7 +55,7 @@ export default function TokenInput(props: Props) {
data-testid='token-input-wrapper'
className={classNames(
'relative isolate z-20 box-content flex h-11 w-full rounded-sm border bg-white/5',
props.warning ? 'border-warning' : 'border-white/20',
props.warningMessages.length ? 'border-warning' : 'border-white/20',
)}
>
{props.hasSelect && props.balances ? (
@ -87,17 +87,7 @@ export default function TokenInput(props: Props) {
<TrashBin width={16} />
</div>
)}
{props.warning && (
<div className='grid items-center px-2'>
<Tooltip
content={`You don't have any ${props.asset.symbol}. Please first deposit ${props.asset.symbol} into your Credit Account before.`}
type='info'
interactive
>
<ExclamationMarkTriangle className='text-warning' />
</Tooltip>
</div>
)}
<WarningMessages messages={props.warningMessages} />
</div>
<div className='flex'>

View File

@ -130,8 +130,8 @@ export class DataFeed implements IDatafeedChartApi {
setTimeout(async () => {
await this.getPairsWithData()
callback(configurationData)
})
callback(configurationData)
}
resolveSymbol(pairName: string, onResolve: ResolveCallback, onError: ErrorCallback) {

View File

@ -0,0 +1,40 @@
import Divider from 'components/Divider'
import { ExclamationMarkTriangle, InfoCircle } from 'components/Icons'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
interface Props {
messages: string[]
}
export default function WarningMessages(props: Props) {
if (!props.messages.length) return null
return (
<div className='grid items-center pr-2 cursor-pointer'>
<Tooltip
content={
<div className='p-2'>
{props.messages.map((message, index) => (
<div key={message}>
<div className='grid grid-cols-[22px,auto] gap-2'>
<InfoCircle />
<div>
<Text size='sm'>{message}</Text>
{index !== props.messages.length - 1 && (
<Divider className='!bg-white/30 my-2' />
)}
</div>
</div>
</div>
))}
</div>
}
type='warning'
interactive
>
<ExclamationMarkTriangle width={24} className='text-warning' />
</Tooltip>
</div>
)
}

View File

@ -1,5 +1,5 @@
export const EMPTY_ACCOUNT: Account = {
id: '',
id: 'default',
kind: 'default',
debts: [],
deposits: [],

View File

@ -19,6 +19,7 @@ interface EnvironmentVariables {
PYTH_ENDPOINT: string
MAINNET_REST_API: string
WALLET_CONNECT_ID: string
STRIDE_APRS: string
}
export const ENV: EnvironmentVariables = {
@ -42,4 +43,5 @@ export const ENV: EnvironmentVariables = {
PYTH_ENDPOINT: process.env.NEXT_PUBLIC_PYTH_ENDPOINT || '',
MAINNET_REST_API: process.env.NEXT_PUBLIC_MAINNET_REST || '',
WALLET_CONNECT_ID: process.env.NEXT_PUBLIC_WALLET_CONNECT_ID || '',
STRIDE_APRS: process.env.NEXT_PUBLIC_STRIDE_APRS || '',
}

View File

@ -4,6 +4,6 @@ export const EARN_TABS: Tab[] = [
]
export const HLS_TABS: Tab[] = [
{ page: 'hls-farm', name: 'Farm' },
{ page: 'hls-staking', name: 'Staking' },
{ page: 'hls-farm', name: 'Farm' },
]

View File

@ -2,10 +2,18 @@ import useSWR from 'swr'
import getAccountIds from 'api/wallets/getAccountIds'
export default function useAccountIdsAndKinds(address?: string, suspense = true) {
export default function useAccountIdsAndKinds(address?: string, suspense = true, noHls = false) {
return useSWR(
`wallets/${address}/account-ids`,
() => getAccountIds(address).then((accountIdsAndKinds) => accountIdsAndKinds.map((a) => a.id)),
() =>
getAccountIds(address).then((accountIdsAndKinds) => {
if (noHls) {
return accountIdsAndKinds
.filter((accountIdAndKind) => accountIdAndKind.kind !== 'high_levered_strategy')
.map((a) => a.id)
}
return accountIdsAndKinds.map((a) => a.id)
}),
{
suspense: suspense,
fallback: [] as string[],

View File

@ -270,6 +270,7 @@ export function useUpdatedAccount(account?: Account) {
addedDeposits,
addedDebts,
addedLends,
addedTrades,
leverage,
removedDeposits,
removedDebts,

View File

@ -8,7 +8,7 @@ export default function HLSFarmPage() {
return (
<div className='flex flex-wrap w-full gap-6'>
<MigrationBanner />
<Tab tabs={HLS_TABS} activeTabIdx={0} />
<Tab tabs={HLS_TABS} activeTabIdx={1} />
<HlsFarmIntro />
<AvailableHLSVaults />
</div>

View File

@ -1,13 +1,15 @@
import Tab from 'components/Earn/Tab'
import ActiveStakingAccounts from 'components/HLS/Staking/ActiveStakingAccounts'
import AvailableHlsStakingAssets from 'components/HLS/Staking/AvailableHLSStakingAssets'
import HLSStakingIntro from 'components/HLS/Staking/HLSStakingIntro'
import MigrationBanner from 'components/MigrationBanner'
import { HLS_TABS } from 'constants/pages'
export default function HLSStakingPage() {
return (
<div className='flex flex-wrap w-full gap-6'>
<MigrationBanner />
{/*<Tab tabs={HLS_TABS} activeTabIdx={1} />*/}
<Tab tabs={HLS_TABS} activeTabIdx={0} />
<HLSStakingIntro />
<AvailableHlsStakingAssets />
<ActiveStakingAccounts />

View File

@ -15,5 +15,6 @@ export default function createCommonSlice(set: SetState<CommonSlice>, get: GetSt
useMargin: true,
useAutoRepay: true,
isOracleStale: false,
isHLS: false,
}
}

View File

@ -125,3 +125,14 @@ interface HLSStrategyNoCap {
interface DepositedHLSStrategy extends HLSStrategy {
depositedAmount: BigNumber
}
interface StakingApr {
chainId: string
currentYield: number
denom: string
fees: number
name: string
strideYield: number
unbondingDelay: number
unbondingPeriod: number
}

View File

@ -2,4 +2,5 @@ interface SummaryItem {
amount: number
options: FormatOptions
title: string
warningMessages?: string[]
}

View File

@ -14,6 +14,7 @@ interface CommonSlice {
useMargin: boolean
useAutoRepay: boolean
isOracleStale: boolean
isHLS: boolean
}
interface FocusComponent {

View File

@ -66,6 +66,8 @@ export const calculateAccountApr = (
borrowAssetsData: BorrowMarketTableData[],
lendingAssetsData: LendingMarketTableData[],
prices: BNCoin[],
hlsStrategies: HLSStrategy[],
isHls?: boolean,
): BigNumber => {
const depositValue = calculateAccountValue('deposits', account, prices)
const lendsValue = calculateAccountValue('lends', account, prices)
@ -75,12 +77,31 @@ export const calculateAccountApr = (
const totalNetValue = totalValue.minus(debtsValue)
if (totalNetValue.isLessThanOrEqualTo(0)) return BN_ZERO
const { vaults, lends, debts } = account
const { vaults, lends, debts, deposits } = account
let totalDepositsInterestValue = BN_ZERO
let totalLendsInterestValue = BN_ZERO
let totalVaultsInterestValue = BN_ZERO
let totalDebtInterestValue = BN_ZERO
if (isHls) {
deposits?.forEach((deposit) => {
const asset = getAssetByDenom(deposit.denom)
if (!asset) return BN_ZERO
const price = prices.find(byDenom(deposit.denom))?.amount ?? 0
const amount = BN(deposit.amount).shiftedBy(-asset.decimals)
const apy =
hlsStrategies.find((strategy) => strategy.denoms.deposit === deposit.denom)?.apy || 0
const positionInterest = amount
.multipliedBy(price)
.multipliedBy(convertApyToApr(apy, 365))
.dividedBy(100)
totalDepositsInterestValue = totalDepositsInterestValue.plus(positionInterest)
})
}
lends?.forEach((lend) => {
const asset = getAssetByDenom(lend.denom)
if (!asset) return BN_ZERO
@ -118,6 +139,7 @@ export const calculateAccountApr = (
const totalInterestValue = totalLendsInterestValue
.plus(totalVaultsInterestValue)
.minus(totalDebtInterestValue)
.plus(totalDepositsInterestValue)
return totalInterestValue.dividedBy(totalNetValue).times(100)
}
@ -262,10 +284,19 @@ export function getAccountSummaryStats(
prices: BNCoin[],
borrowAssets: BorrowMarketTableData[],
lendingAssets: LendingMarketTableData[],
hlsStrategies: HLSStrategy[],
isHls?: boolean,
) {
const [deposits, lends, debts, vaults] = getAccountPositionValues(account, prices)
const positionValue = deposits.plus(lends).plus(vaults)
const apr = calculateAccountApr(account, borrowAssets, lendingAssets, prices)
const apr = calculateAccountApr(
account,
borrowAssets,
lendingAssets,
prices,
hlsStrategies,
isHls,
)
const leverage = calculateAccountLeverage(account, prices)
return {

View File

@ -1,4 +1,10 @@
import { BN } from './helpers'
export const devideByPotentiallyZero = (numerator: number, denominator: number): number => {
if (denominator === 0) return 0
return numerator / denominator
}
export function getLeveragedApy(baseApy: number, borrowRate: number, leverage: number): number {
return BN(leverage).times(baseApy).minus(BN(leverage).minus(1).times(borrowRate)).toNumber()
}

34
src/utils/messages.ts Normal file
View File

@ -0,0 +1,34 @@
import { formatAmountWithSymbol } from './formatters'
export function getNoBalanceMessage(symbol: string) {
return `You don't have an ${symbol} balance in your account.`
}
export function getDepositCapMessage(
denom: string,
amount: BigNumber,
action: 'deposit' | 'borrow',
) {
return `You cannot ${action} more than ${formatAmountWithSymbol({
denom,
amount: amount.toString(),
})} due to the deposit cap on this asset being reached in the protocol.`
}
export function getLiquidityMessage(denom: string, amount: BigNumber) {
return `You cannot borrow more than ${formatAmountWithSymbol({
denom,
amount: amount.toString(),
})} due to the available market liquidity.`
}
export function getHealthFactorMessage(
denom: string,
amount: BigNumber,
action: 'borrow' | 'withdraw',
) {
return `You cannot ${action} more than ${formatAmountWithSymbol({
denom,
amount: amount.toString(),
})}, as it will result in a health factor lower than 1.`
}

View File

@ -19,7 +19,7 @@ export function getRoute(page: Page, address?: string, accountId?: string | null
}
export function getPage(pathname: string): Page {
const pages: Page[] = ['trade', 'borrow', 'farm', 'lend', 'portfolio']
const pages: Page[] = ['trade', 'borrow', 'farm', 'lend', 'portfolio', 'hls-farm', 'hls-staking']
const segments = pathname.split('/')
const page = segments.find((segment) => pages.includes(segment as Page))

View File

@ -157,6 +157,9 @@ export function getEnterVaultActions(
secondaryCoin: BNCoin,
slippage: number,
): Action[] {
primaryCoin.amount = primaryCoin.amount.times(0.8).integerValue()
secondaryCoin.amount = secondaryCoin.amount.times(0.8).integerValue()
return [
{
provide_liquidity: {

View File

@ -52,6 +52,7 @@ module.exports = {
},
backgroundImage: {
intro: 'url(/images/bg-intro.webp), url(/images/bg-intro.png)',
'intro-hls': 'url(/images/bg-intro-hls.webp), url(/images/bg-intro-hls.png)',
},
backgroundSize: {
desktop: '100% auto',
@ -84,6 +85,7 @@ module.exports = {
atom: '#6f7390',
axlusdc: '#478edc',
body: '#0D0012',
'body-hls': '#090000',
'body-dark': '#141621',
chart: '#220e1d',
error: '#F04438',
@ -104,8 +106,11 @@ module.exports = {
'martian-red': '#FF645F',
osmo: '#9f1ab9',
'orb-primary': '#b12f25',
'orb-primary-hls': '#FF645F',
'orb-secondary': '#530781',
'orb-secondary-hls': '#a03b45',
'orb-tertiary': '#ff00c7',
'orb-tertiary-hls': '#FB9562',
profit: '#41a4a9',
primary: '#FF625E',
secondary: '#FB9562',
@ -142,6 +147,7 @@ module.exports = {
height: {
4.5: '18px',
15: '60px',
45: '180px',
50: '200px',
55: '220px',
},
@ -282,13 +288,13 @@ module.exports = {
addUtilities({
'.blur-orb-primary': {
filter: 'blur(clamp(50px, 8vw, 100px))',
filter: 'blur(clamp(50px, 8vw, 30px))',
},
'.blur-orb-secondary': {
filter: 'blur(clamp(60px, 20vw, 140px))',
filter: 'blur(clamp(60px, 20vw, 160px))',
},
'.blur-orb-tertiary': {
filter: 'blur(clamp(60px, 10vw, 110px))',
filter: 'blur(clamp(60px, 10vw, 150px))',
},
'.border-glas': {
background:

View File

@ -60,5 +60,8 @@ if (!process.env.NEXT_PUBLIC_MAINNET_REST) {
if (!process.env.NEXT_PUBLIC_RPC) {
throw 'NEXT_PUBLIC_RPC is not defined'
}
if (!process.env.NEXT_PUBLIC_STRIDE_APRS) {
throw 'NEXT_PUBLIC_STRIDE_APRS is not defined'
}
console.log('✅ Required env variables set')