{showStaleOracle &&
}
{accountId &&
}
- {address && !isHLS &&
}
+ {showAccountMenu &&
}
diff --git a/src/components/header/V1DesktopHeader.tsx b/src/components/header/V1DesktopHeader.tsx
new file mode 100644
index 00000000..4635ddd2
--- /dev/null
+++ b/src/components/header/V1DesktopHeader.tsx
@@ -0,0 +1,81 @@
+import classNames from 'classnames'
+import { useMemo } from 'react'
+import { isDesktop } from 'react-device-detect'
+
+import Wallet from 'components/Wallet'
+import EscButton from 'components/common/Button/EscButton'
+import Settings from 'components/common/Settings'
+import ChainSelect from 'components/header/ChainSelect'
+import OracleResyncButton from 'components/header/OracleResyncButton'
+import RewardsCenter from 'components/header/RewardsCenter'
+import DesktopNavigation from 'components/header/navigation/DesktopNavigation'
+import useAccountId from 'hooks/useAccountId'
+import useStore from 'store'
+import { WalletID } from 'types/enums/wallet'
+import { getGovernanceUrl } from 'utils/helpers'
+
+const menuTree = (walletId: WalletID, chainConfig: ChainConfig): MenuTreeEntry[] => [
+ {
+ pages: ['v1'],
+ label: 'Red Bank',
+ },
+ { pages: ['governance'], label: 'Governance', externalUrl: getGovernanceUrl(walletId) },
+]
+
+export default function DesktopHeader() {
+ const address = useStore((s) => s.address)
+ const focusComponent = useStore((s) => s.focusComponent)
+ const isOracleStale = useStore((s) => s.isOracleStale)
+ const accountId = useAccountId()
+
+ function handleCloseFocusMode() {
+ if (focusComponent && focusComponent.onClose) focusComponent.onClose()
+ useStore.setState({ focusComponent: null })
+ }
+
+ const showStaleOracle = useMemo(() => isOracleStale && address, [isOracleStale, address])
+
+ if (!isDesktop) return null
+
+ return (
+
+ )
+}
diff --git a/src/components/header/navigation/DesktopNavigation.tsx b/src/components/header/navigation/DesktopNavigation.tsx
index 92fc1a47..a8c0c888 100644
--- a/src/components/header/navigation/DesktopNavigation.tsx
+++ b/src/components/header/navigation/DesktopNavigation.tsx
@@ -4,7 +4,6 @@ import { useMemo } from 'react'
import Button from 'components/common/Button'
import { ChevronDown, Logo } from 'components/common/Icons'
-import { menuTree } from 'components/header/DesktopHeader'
import { NavLink } from 'components/header/navigation/NavLink'
import { NavMenu } from 'components/header/navigation/NavMenu'
import useChainConfig from 'hooks/useChainConfig'
@@ -12,19 +11,24 @@ import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { WalletID } from 'types/enums/wallet'
+interface Props {
+ menuTree: (walletId: WalletID, chainConfig: ChainConfig) => MenuTreeEntry[]
+}
+
export function getIsActive(pages: string[]) {
const segments = location.pathname.split('/')
return pages.some((page) => segments.includes(page))
}
-export default function DesktopNavigation() {
+export default function DesktopNavigation(props: Props) {
+ const { menuTree } = props
const [showMenu, setShowMenu] = useToggle()
const { recentWallet } = useShuttle()
const chainConfig = useChainConfig()
const walletId = (recentWallet?.providerId as WalletID) ?? WalletID.Keplr
const focusComponent = useStore((s) => s.focusComponent)
- const menu = useMemo(() => menuTree(walletId, chainConfig), [walletId, chainConfig])
+ const menu = useMemo(() => menuTree(walletId, chainConfig), [walletId, chainConfig, menuTree])
return (
} />
} />
} />
+
} />
} />
{chainConfig.hls &&
} />}
{chainConfig.hls &&
} />}
@@ -47,6 +49,7 @@ export default function Routes() {
} />
{chainConfig.hls &&
} />}
{chainConfig.hls &&
} />}
+
} />
} />
diff --git a/src/components/portfolio/Account/Summary.tsx b/src/components/portfolio/Account/Summary.tsx
index 3818afa6..00ed0247 100644
--- a/src/components/portfolio/Account/Summary.tsx
+++ b/src/components/portfolio/Account/Summary.tsx
@@ -17,6 +17,7 @@ import { DEFAULT_PORTFOLIO_STATS } from 'utils/constants'
interface Props {
accountId: string
+ v1?: boolean
}
function Content(props: Props) {
@@ -74,21 +75,21 @@ function Content(props: Props) {
title: (
),
- sub: DEFAULT_PORTFOLIO_STATS[4].sub,
+ sub: props.v1 ? 'Total Leverage' : DEFAULT_PORTFOLIO_STATS[4].sub,
},
]
- }, [account, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs])
+ }, [account, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs, props.v1])
return (
)
diff --git a/src/components/portfolio/Overview/Summary.tsx b/src/components/portfolio/Overview/Summary.tsx
index dc121c9d..4b4afd88 100644
--- a/src/components/portfolio/Overview/Summary.tsx
+++ b/src/components/portfolio/Overview/Summary.tsx
@@ -89,7 +89,7 @@ export default function PortfolioSummary() {
title: (
),
diff --git a/src/components/v1/Borrowings.tsx b/src/components/v1/Borrowings.tsx
new file mode 100644
index 00000000..b480f899
--- /dev/null
+++ b/src/components/v1/Borrowings.tsx
@@ -0,0 +1,47 @@
+import BorrowingsTable from 'components/borrow/Table/ActiveBorrowingsTable'
+import useV1BorrowingsTableData from 'components/v1/Table/useV1BorrowingsTableData'
+import { BN_ZERO } from 'constants/math'
+import useBorrowEnabledAssets from 'hooks/assets/useBorrowEnabledAssets'
+
+export default function Borrowings() {
+ const { debtAssets } = useV1BorrowingsTableData()
+
+ if (!debtAssets?.length) {
+ return
+ }
+
+ return (
+ <>
+
+ >
+ )
+}
+
+function Fallback() {
+ const assets = useBorrowEnabledAssets()
+ const data: BorrowMarketTableData[] = assets.map((asset) => ({
+ asset,
+ apy: {
+ borrow: 0,
+ deposit: 0,
+ },
+ ltv: {
+ max: 0,
+ liq: 0,
+ },
+ liquidity: BN_ZERO,
+ marketLiquidityRate: 0,
+ cap: {
+ denom: asset.denom,
+ max: BN_ZERO,
+ used: BN_ZERO,
+ },
+ debt: BN_ZERO,
+ borrowEnabled: true,
+ depositEnabled: true,
+ deposits: BN_ZERO,
+ accountDebt: BN_ZERO,
+ }))
+
+ return
+}
diff --git a/src/components/v1/Deposits.tsx b/src/components/v1/Deposits.tsx
new file mode 100644
index 00000000..4fc025af
--- /dev/null
+++ b/src/components/v1/Deposits.tsx
@@ -0,0 +1,46 @@
+import DepositsTable from 'components/earn/lend/Table/DepositedLendsTable'
+import useV1DepositsTableData from 'components/v1/Table/useV1DepositsTableData'
+import { BN_ZERO } from 'constants/math'
+import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
+
+export default function Deposits() {
+ const { depositAssets } = useV1DepositsTableData()
+
+ if (!depositAssets?.length) {
+ return
+ }
+
+ return (
+ <>
+
+ >
+ )
+}
+
+function Fallback() {
+ const assets = useMarketEnabledAssets()
+
+ const data: LendingMarketTableData[] = assets.map((asset) => ({
+ asset,
+ borrowEnabled: true,
+ depositEnabled: true,
+ debt: BN_ZERO,
+ deposits: BN_ZERO,
+ liquidity: BN_ZERO,
+ cap: {
+ max: BN_ZERO,
+ used: BN_ZERO,
+ denom: asset.denom,
+ },
+ apy: {
+ borrow: 0,
+ deposit: 0,
+ },
+ ltv: {
+ max: 0,
+ liq: 0,
+ },
+ }))
+
+ return
+}
diff --git a/src/components/v1/Table/borrowings/Columns/Action.tsx b/src/components/v1/Table/borrowings/Columns/Action.tsx
new file mode 100644
index 00000000..a8238c63
--- /dev/null
+++ b/src/components/v1/Table/borrowings/Columns/Action.tsx
@@ -0,0 +1,20 @@
+import BorrowButton from 'components/v1/Table/borrowings/Columns/BorrowButton'
+import Manage from 'components/v1/Table/borrowings/Columns/Manage'
+
+export const MANAGE_META = {
+ accessorKey: 'manage',
+ enableSorting: false,
+ header: '',
+}
+
+interface Props {
+ data: BorrowMarketTableData
+}
+
+export default function Action(props: Props) {
+ const hasDebt = !props.data.accountDebtAmount?.isZero() ?? false
+
+ if (hasDebt) return
+
+ return
+}
diff --git a/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx b/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx
new file mode 100644
index 00000000..4232532a
--- /dev/null
+++ b/src/components/v1/Table/borrowings/Columns/BorrowButton.tsx
@@ -0,0 +1,51 @@
+import ActionButton from 'components/common/Button/ActionButton'
+import { Plus } from 'components/common/Icons'
+import Text from 'components/common/Text'
+import { Tooltip } from 'components/common/Tooltip'
+import ConditionalWrapper from 'hocs/ConditionalWrapper'
+import useAccount from 'hooks/accounts/useAccount'
+import useStore from 'store'
+
+interface Props {
+ data: BorrowMarketTableData
+}
+export default function BorrowButton(props: Props) {
+ const address = useStore((s) => s.address)
+ const { data: account } = useAccount(address)
+
+ const hasCollateral = account?.lends?.length ?? 0 > 0
+
+ return (
+
+
(
+ {`You don’t have assets deposited in the Red Bank. Please deposit assets before you borrow.`}
+ }
+ contentClassName='max-w-[200px]'
+ className='ml-auto'
+ >
+ {children}
+
+ )}
+ >
+ }
+ disabled={!hasCollateral}
+ color='tertiary'
+ onClick={(e) => {
+ useStore.setState({
+ v1BorrowAndRepayModal: { type: 'borrow', data: props.data },
+ })
+ e.stopPropagation()
+ }}
+ text='Borrow'
+ short
+ />
+
+
+ )
+}
diff --git a/src/components/v1/Table/borrowings/Columns/Manage.tsx b/src/components/v1/Table/borrowings/Columns/Manage.tsx
new file mode 100644
index 00000000..b05b896b
--- /dev/null
+++ b/src/components/v1/Table/borrowings/Columns/Manage.tsx
@@ -0,0 +1,47 @@
+import { useMemo } from 'react'
+
+import DropDownButton from 'components/common/Button/DropDownButton'
+import { HandCoins, Plus } from 'components/common/Icons'
+import useWalletBalances from 'hooks/useWalletBalances'
+import useStore from 'store'
+import { byDenom } from 'utils/array'
+
+interface Props {
+ data: BorrowMarketTableData
+}
+
+export default function Manage(props: Props) {
+ const address = useStore((s) => s.address)
+ const { data: balances } = useWalletBalances(address)
+ const hasBalance = !!balances.find(byDenom(props.data.asset.denom))
+
+ const ITEMS: DropDownItem[] = useMemo(
+ () => [
+ {
+ icon:
,
+ text: 'Borrow more',
+ onClick: () =>
+ useStore.setState({
+ v1BorrowAndRepayModal: { type: 'borrow', data: props.data },
+ }),
+ },
+ {
+ icon:
,
+ text: 'Repay',
+ onClick: () =>
+ useStore.setState({
+ v1BorrowAndRepayModal: { type: 'repay', data: props.data },
+ }),
+ disabled: !hasBalance,
+ disabledTooltip: `You don’t have any ${props.data.asset.symbol} in your Wallet.`,
+ },
+ ],
+ [hasBalance, props.data],
+ )
+
+ return (
+
+
+
+ )
+}
diff --git a/src/components/v1/Table/deposits/Columns/Action.tsx b/src/components/v1/Table/deposits/Columns/Action.tsx
new file mode 100644
index 00000000..b08f2c52
--- /dev/null
+++ b/src/components/v1/Table/deposits/Columns/Action.tsx
@@ -0,0 +1,19 @@
+import DepositButton from 'components/v1/Table/deposits/Columns/DepositButton'
+import Manage from 'components/v1/Table/deposits/Columns/Manage'
+
+export const MANAGE_META = {
+ accessorKey: 'manage',
+ enableSorting: false,
+ header: '',
+}
+
+interface Props {
+ data: LendingMarketTableData
+}
+export default function Action(props: Props) {
+ const hasDeposits = !props.data.accountLentAmount?.isZero() ?? false
+
+ if (hasDeposits) return
+
+ return
+}
diff --git a/src/components/v1/Table/deposits/Columns/DepositButton.tsx b/src/components/v1/Table/deposits/Columns/DepositButton.tsx
new file mode 100644
index 00000000..60c876cf
--- /dev/null
+++ b/src/components/v1/Table/deposits/Columns/DepositButton.tsx
@@ -0,0 +1,51 @@
+import ActionButton from 'components/common/Button/ActionButton'
+import { ArrowUpLine } from 'components/common/Icons'
+import Text from 'components/common/Text'
+import { Tooltip } from 'components/common/Tooltip'
+import ConditionalWrapper from 'hocs/ConditionalWrapper'
+import useWalletBalances from 'hooks/useWalletBalances'
+import useStore from 'store'
+import { byDenom } from 'utils/array'
+
+interface Props {
+ data: LendingMarketTableData
+}
+export default function DepositButton(props: Props) {
+ const address = useStore((s) => s.address)
+ const { data: balances } = useWalletBalances(address)
+ const hasBalance = !!balances.find(byDenom(props.data.asset.denom))
+
+ return (
+
+
(
+ {`You don’t have any ${props.data.asset.symbol} in your Wallet.`}
+ }
+ contentClassName='max-w-[200px]'
+ className='ml-auto'
+ >
+ {children}
+
+ )}
+ >
+ }
+ disabled={!hasBalance}
+ color='tertiary'
+ onClick={(e) => {
+ useStore.setState({
+ v1DepositAndWithdrawModal: { type: 'deposit', data: props.data },
+ })
+ e.stopPropagation()
+ }}
+ text='Deposit'
+ short
+ />
+
+
+ )
+}
diff --git a/src/components/v1/Table/deposits/Columns/Manage.tsx b/src/components/v1/Table/deposits/Columns/Manage.tsx
new file mode 100644
index 00000000..728afbf1
--- /dev/null
+++ b/src/components/v1/Table/deposits/Columns/Manage.tsx
@@ -0,0 +1,48 @@
+import { useMemo } from 'react'
+
+import DropDownButton from 'components/common/Button/DropDownButton'
+import { ArrowDownLine, ArrowUpLine } from 'components/common/Icons'
+import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
+import useWalletBalances from 'hooks/useWalletBalances'
+import useStore from 'store'
+import { byDenom } from 'utils/array'
+
+interface Props {
+ data: LendingMarketTableData
+}
+
+export default function Manage(props: Props) {
+ const address = useStore((s) => s.address)
+ const { data: balances } = useWalletBalances(address)
+ const hasBalance = !!balances.find(byDenom(props.data.asset.denom))
+
+ const ITEMS: DropDownItem[] = useMemo(
+ () => [
+ {
+ icon:
,
+ text: 'Deposit more',
+ onClick: () =>
+ useStore.setState({
+ v1DepositAndWithdrawModal: { type: 'deposit', data: props.data },
+ }),
+ disabled: !hasBalance,
+ disabledTooltip: `You don’t have any ${props.data.asset.symbol} in your Wallet.`,
+ },
+ {
+ icon:
,
+ text: 'Withdraw',
+ onClick: () =>
+ useStore.setState({
+ v1DepositAndWithdrawModal: { type: 'withdraw', data: props.data },
+ }),
+ },
+ ],
+ [hasBalance, props.data],
+ )
+
+ return (
+
+
+
+ )
+}
diff --git a/src/components/v1/Table/useV1BorrowingsTableData.ts b/src/components/v1/Table/useV1BorrowingsTableData.ts
new file mode 100644
index 00000000..62b544d3
--- /dev/null
+++ b/src/components/v1/Table/useV1BorrowingsTableData.ts
@@ -0,0 +1,38 @@
+import { useMemo } from 'react'
+
+import { BN_ZERO } from 'constants/math'
+import useAccount from 'hooks/accounts/useAccount'
+import useMarkets from 'hooks/markets/useMarkets'
+import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
+import useStore from 'store'
+
+export default function useV1BorrowingsTableData() {
+ const address = useStore((s) => s.address)
+ const markets = useMarkets()
+ const { data: v1Positions } = useAccount(address)
+ const { convertAmount } = useDisplayCurrencyPrice()
+
+ return useMemo((): {
+ debtAssets: BorrowMarketTableData[]
+ } => {
+ const userDebts = v1Positions?.debts ?? []
+ const debtAssets: BorrowMarketTableData[] = []
+
+ markets
+ .filter((market) => market.borrowEnabled)
+ .forEach((market) => {
+ const amount =
+ userDebts.find((debt) => debt.denom === market.asset.denom)?.amount ?? BN_ZERO
+ const value = amount ? convertAmount(market.asset, amount) : undefined
+
+ const borrowMarketAsset: BorrowMarketTableData = {
+ ...market,
+ accountDebtAmount: amount,
+ accountDebtValue: value,
+ }
+ debtAssets.push(borrowMarketAsset)
+ })
+
+ return { debtAssets }
+ }, [v1Positions, markets, convertAmount])
+}
diff --git a/src/components/v1/Table/useV1DepositsTableData.ts b/src/components/v1/Table/useV1DepositsTableData.ts
new file mode 100644
index 00000000..3224a224
--- /dev/null
+++ b/src/components/v1/Table/useV1DepositsTableData.ts
@@ -0,0 +1,39 @@
+import { useMemo } from 'react'
+
+import { BN_ZERO } from 'constants/math'
+import useAccount from 'hooks/accounts/useAccount'
+import useMarkets from 'hooks/markets/useMarkets'
+import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
+import useStore from 'store'
+import { byDenom } from 'utils/array'
+
+export default function useV1DepositsTableData(): {
+ depositAssets: LendingMarketTableData[]
+} {
+ const address = useStore((s) => s.address)
+ const markets = useMarkets()
+ const { data: v1Positions } = useAccount(address)
+ const { convertAmount } = useDisplayCurrencyPrice()
+
+ return useMemo(() => {
+ const depositAssets: LendingMarketTableData[] = []
+ const userCollateral = v1Positions?.lends ?? []
+
+ markets.forEach((market) => {
+ const amount = userCollateral.find(byDenom(market.asset.denom))?.amount ?? BN_ZERO
+ const value = amount ? convertAmount(market.asset, amount) : undefined
+
+ const lendingMarketAsset: LendingMarketTableData = {
+ ...market,
+ accountLentValue: value,
+ accountLentAmount: amount,
+ }
+
+ depositAssets.push(lendingMarketAsset)
+ })
+
+ return {
+ depositAssets,
+ }
+ }, [markets, v1Positions, convertAmount])
+}
diff --git a/src/components/v1/V1Intro.tsx b/src/components/v1/V1Intro.tsx
new file mode 100644
index 00000000..a5a67838
--- /dev/null
+++ b/src/components/v1/V1Intro.tsx
@@ -0,0 +1,24 @@
+import WalletConnectButton from 'components/Wallet/WalletConnectButton'
+import Intro from 'components/common/Intro'
+import useStore from 'store'
+
+export default function V1Intro() {
+ const address = useStore((state) => state.address)
+ return (
+
+ Welcome to the Red Bank!
+
+ This is the first version (v1) of the Red Bank. It provides simple lending and borrowing,
+ without the use of Credit Accounts.
+
+ Deposited funds can‘t be used on v2 as collateral.
+ >
+ }
+ bg='v1'
+ >
+ {!address && }
+
+ )
+}
diff --git a/src/configs/assets/WETH.xal.ts b/src/configs/assets/WETH.axl.ts
similarity index 100%
rename from src/configs/assets/WETH.xal.ts
rename to src/configs/assets/WETH.axl.ts
diff --git a/src/configs/assets/stkATOM.ts b/src/configs/assets/stkATOM.ts
new file mode 100644
index 00000000..4116de1b
--- /dev/null
+++ b/src/configs/assets/stkATOM.ts
@@ -0,0 +1,16 @@
+const stkATOM: AssetMetaData = {
+ symbol: 'stkATOM',
+ name: 'Persistence Staked Atom',
+ id: 'stkATOM',
+ color: '#c73238',
+ logo: '/images/tokens/stkatom.svg',
+ decimals: 6,
+ hasOraclePrice: true,
+ isEnabled: true,
+ isMarket: true,
+ isDisplayCurrency: true,
+ isAutoLendEnabled: false,
+ isStaking: true,
+}
+
+export default stkATOM
diff --git a/src/configs/assets/wstETH.ts b/src/configs/assets/wstETH.ts
new file mode 100644
index 00000000..d2d24e63
--- /dev/null
+++ b/src/configs/assets/wstETH.ts
@@ -0,0 +1,18 @@
+const wstETH: AssetMetaData = {
+ symbol: 'wstETH',
+ id: 'wstETH',
+ name: 'Lido Wrapped Staked Ethereum',
+ color: '#00a3ff',
+ logo: '/images/tokens/wsteth.svg',
+ decimals: 18,
+ hasOraclePrice: true,
+ isEnabled: true,
+ isMarket: true,
+ isDisplayCurrency: true,
+ isAutoLendEnabled: true,
+ isBorrowEnabled: true,
+ pythPriceFeedId: '0x6df640f3b8963d8f8358f791f352b8364513f6ab1cca5ed3f1f7b5448980e784',
+ pythFeedName: 'WSTETHUSD',
+}
+
+export default wstETH
diff --git a/src/configs/chains/neutron/neutron-1.ts b/src/configs/chains/neutron/neutron-1.ts
new file mode 100644
index 00000000..00f0b9bb
--- /dev/null
+++ b/src/configs/chains/neutron/neutron-1.ts
@@ -0,0 +1,86 @@
+import { Bech32Address } from '@keplr-wallet/cosmos'
+
+import ATOM from 'configs/assets/ATOM'
+import DYDX from 'configs/assets/DYDX'
+import NTRN from 'configs/assets/NTRN'
+import USDCaxl from 'configs/assets/USDC.axl'
+import USDollar from 'configs/assets/USDollar'
+import WETHaxl from 'configs/assets/WETH.axl'
+import stATOM from 'configs/assets/stATOM'
+import stkATOM from 'configs/assets/stkATOM'
+import wstETH from 'configs/assets/wstETH'
+import { NETWORK } from 'types/enums/network'
+import { ChainInfoID } from 'types/enums/wallet'
+
+const Neutron1: ChainConfig = {
+ assets: [
+ { ...NTRN, denom: 'untrn' },
+ { ...USDCaxl, denom: 'ibc/F082B65C88E4B6D5EF1DB243CDA1D331D002759E938A0F5CD3FFDC5D53B3E349' },
+ {
+ ...ATOM,
+ denom: 'ibc/C4CFF46FD6DE35CA4CF4CE031E643C8FDC9BA4B99AE598E9B0ED98FE3A2319F9',
+ },
+ {
+ ...stATOM,
+ denom: 'ibc/B7864B03E1B9FD4F049243E92ABD691586F682137037A9F3FCA5222815620B3C',
+ },
+ {
+ ...stkATOM,
+ denom: 'ibc/3649CE0C8A2C79048D8C6F31FF18FA69C9BC7EB193512E0BD03B733011290445',
+ },
+ { ...WETHaxl, denom: 'ibc/A585C2D15DCD3B010849B453A2CFCB5E213208A5AB665691792684C26274304D' },
+ {
+ ...wstETH,
+ denom: 'factory/neutron1ug740qrkquxzrk2hh29qrlx3sktkfml3je7juusc2te7xmvsscns0n2wry/wstETH',
+ },
+ { ...DYDX, denom: 'ibc/2CB87BCE0937B1D1DFCEE79BE4501AAF3C265E923509AEAC410AD85D27F35130' },
+ USDollar,
+ ],
+ id: ChainInfoID.Neutron1,
+ name: 'Neutron',
+ contracts: {
+ redBank: 'neutron1n97wnm7q6d2hrcna3rqlnyqw2we6k0l8uqvmyqq6gsml92epdu7quugyph',
+ incentives: 'neutron1aszpdh35zsaz0yj80mz7f5dtl9zq5jfl8hgm094y0j0vsychfekqxhzd39',
+ oracle: 'neutron1dwp6m7pdrz6rnhdyrx5ha0acsduydqcpzkylvfgspsz60pj2agxqaqrr7g',
+ swapper: 'neutron1udr9fc3kd743dezrj38v2ac74pxxr6qsx4xt4nfpcfczgw52rvyqyjp5au',
+ params: 'neutron16kqg3hr2qc36gz2wqvdzsctatkmzd3ss5gc07tnj6u3n5ajw89asrx8hfp',
+ creditManager: 'neutron1kj50g96c86nu7jmy5y7uy5cyjanntgru0eekmwz2qcmyyvx6383s8dgvm6',
+ accountNft: 'neutron17wvpxdc3k37054ume0ga4r0r6ra2rpfe622m0ecgd9s7xd5s0qusspc4ct',
+ perps: 'neutron14v9g7regs90qvful7djcajsvrfep5pg9qau7qm6wya6c2lzcpnms692dlt',
+ pyth: 'neutron1m2emc93m9gpwgsrsf2vylv9xvgqh654630v7dfrhrkmr5slly53spg85wv',
+ },
+ endpoints: {
+ routes: 'https://app.astroport.fi/api/routes',
+ rpc: process.env.NEXT_PUBLIC_NEUTRON_RPC ?? 'https://rpc-kralum.neutron-1.neutron.org',
+ rest: process.env.NEXT_PUBLIC_NEUTRON_REST ?? 'https://rest-kralum.neutron-1.neutron.org',
+ swap: 'https://neutron.astroport.fi/swap',
+ pools: '', //TODO: ⛓️ Implement this
+ explorer: 'https://mintscan.io/neutron',
+ aprs: {
+ vaults: 'https://api.marsprotocol.io/v1/vaults/neutron',
+ stride: 'https://edge.stride.zone/api/stake-stats',
+ },
+ },
+ network: NETWORK.MAINNET,
+ vaults: [],
+ explorerName: 'Mintscan',
+ bech32Config: Bech32Address.defaultBech32Config('neutron'),
+ defaultCurrency: {
+ coinDenom: 'NTRN',
+ coinMinimalDenom: 'untrn',
+ coinDecimals: 6,
+ coinGeckoId: 'neutron',
+ gasPriceStep: {
+ low: 0,
+ average: 0.025,
+ high: 0.04,
+ },
+ },
+ features: ['ibc-transfer', 'ibc-go'],
+ gasPrice: '0.025untrn',
+ hls: false,
+ perps: false,
+ farm: false,
+}
+
+export default Neutron1
diff --git a/src/configs/chains/osmosis/osmosis-1.ts b/src/configs/chains/osmosis/osmosis-1.ts
index b25594e8..227c022b 100644
--- a/src/configs/chains/osmosis/osmosis-1.ts
+++ b/src/configs/chains/osmosis/osmosis-1.ts
@@ -13,7 +13,7 @@ import USDCaxl from 'configs/assets/USDC.axl'
import USDT from 'configs/assets/USDT'
import USDollar from 'configs/assets/USDollar'
import WBTCaxl from 'configs/assets/WBTC.axl'
-import WETHaxl from 'configs/assets/WETH.xal'
+import WETHaxl from 'configs/assets/WETH.axl'
import OSMO_ATOM from 'configs/assets/lp/OSMO-ATOM'
import OSMO_USDC from 'configs/assets/lp/OSMO_USDC'
import OSMO_WBTC from 'configs/assets/lp/OSMO_WBTC'
diff --git a/src/constants/pageMetadata.ts b/src/constants/pageMetadata.ts
index a212f879..86344373 100644
--- a/src/constants/pageMetadata.ts
+++ b/src/constants/pageMetadata.ts
@@ -47,6 +47,11 @@ const PAGE_METADATA = {
'Stake MARS token to ascend to the Martian Council and help govern key changes to the protocol.',
keywords: 'martian council, mars governance, cosmos governance, mars voting, mars staking',
},
+ v1: {
+ title: 'Mars Protocol V1',
+ description: "Lend, borrow and earn on the galaxy's most powerful credit protocol.",
+ keywords: 'martian council, mars governance, cosmos governance, mars voting, mars staking',
+ },
}
export default PAGE_METADATA
diff --git a/src/constants/wallets.ts b/src/constants/wallets.ts
index c641fbf9..062d7948 100644
--- a/src/constants/wallets.ts
+++ b/src/constants/wallets.ts
@@ -15,7 +15,7 @@ export const WALLETS: WalletInfos = {
walletConnect: 'Cosmostation WalletConnect',
imageURL: '/images/wallets/cosmostation.png',
mobileImageURL: '/images/wallets/cosmostation-wc.png',
- supportedChains: [ChainInfoID.Osmosis1, ChainInfoID.OsmosisDevnet],
+ supportedChains: [ChainInfoID.Osmosis1, ChainInfoID.OsmosisDevnet, ChainInfoID.Pion1],
},
[WalletID.Keplr]: {
name: 'Keplr Wallet',
diff --git a/src/hooks/accounts/useAccount.tsx b/src/hooks/accounts/useAccount.tsx
index 980cc590..2a5f6a06 100644
--- a/src/hooks/accounts/useAccount.tsx
+++ b/src/hooks/accounts/useAccount.tsx
@@ -1,14 +1,22 @@
import useSWR from 'swr'
import getAccount from 'api/accounts/getAccount'
+import getV1Positions from 'api/v1/getV1Positions'
import useChainConfig from 'hooks/useChainConfig'
+import useStore from 'store'
export default function useAccount(accountId?: string, suspense?: boolean) {
const chainConfig = useChainConfig()
+ const address = useStore((s) => s.address)
+ const isV1 = accountId === address
+
+ const cacheKey = isV1
+ ? `chains/${chainConfig.id}/v1/user/${accountId}`
+ : `chains/${chainConfig.id}/accounts/${accountId}`
return useSWR(
- accountId && `chains/${chainConfig.id}/accounts/${accountId}`,
- () => getAccount(chainConfig, accountId),
+ accountId && cacheKey,
+ () => (isV1 ? getV1Positions(chainConfig, accountId) : getAccount(chainConfig, accountId)),
{
suspense: suspense,
revalidateOnFocus: false,
diff --git a/src/hooks/useClients.ts b/src/hooks/useClients.ts
index 24b03dbb..4cd3d471 100644
--- a/src/hooks/useClients.ts
+++ b/src/hooks/useClients.ts
@@ -11,6 +11,7 @@ import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.cl
import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.client'
import { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client'
import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client'
+import { getUrl } from 'utils/url'
export default function useClients() {
const chainConfig = useChainConfig()
@@ -18,8 +19,7 @@ export default function useClients() {
const swr = useSWR(
`chains/${chainConfig.id}/clients`,
async () => {
- const client = await CosmWasmClient.connect(chainConfig.endpoints.rpc)
-
+ const client = await CosmWasmClient.connect(getUrl(chainConfig.endpoints.rpc))
return {
creditManager: new MarsCreditManagerQueryClient(
client,
diff --git a/src/pages/BorrowPage.tsx b/src/pages/BorrowPage.tsx
index 8eefc851..eec33e71 100644
--- a/src/pages/BorrowPage.tsx
+++ b/src/pages/BorrowPage.tsx
@@ -1,11 +1,9 @@
import Borrowings from 'components/borrow/Borrowings'
import BorrowIntro from 'components/borrow/BorrowIntro'
-import MigrationBanner from 'components/common/MigrationBanner'
export default function BorrowPage() {
return (
-
diff --git a/src/pages/FarmPage.tsx b/src/pages/FarmPage.tsx
index 4c6bcccd..07938c9c 100644
--- a/src/pages/FarmPage.tsx
+++ b/src/pages/FarmPage.tsx
@@ -1,13 +1,11 @@
+import Tab from 'components/earn/Tab'
import FarmIntro from 'components/earn/farm/FarmIntro'
import Vaults from 'components/earn/farm/Vaults'
-import Tab from 'components/earn/Tab'
-import MigrationBanner from 'components/common/MigrationBanner'
import { EARN_TABS } from 'constants/pages'
export default function FarmPage() {
return (
-
diff --git a/src/pages/HLSFarmPage.tsx b/src/pages/HLSFarmPage.tsx
index 40e75b38..290ceb4a 100644
--- a/src/pages/HLSFarmPage.tsx
+++ b/src/pages/HLSFarmPage.tsx
@@ -1,13 +1,11 @@
import Tab from 'components/earn/Tab'
import AvailableHLSVaults from 'components/hls/Farm/AvailableHLSVaults'
import HlsFarmIntro from 'components/hls/Farm/HLSFarmIntro'
-import MigrationBanner from 'components/common/MigrationBanner'
import { HLS_TABS } from 'constants/pages'
export default function HLSFarmPage() {
return (
-
diff --git a/src/pages/HLSStakingPage.tsx b/src/pages/HLSStakingPage.tsx
index 3aeac7c8..d8f57501 100644
--- a/src/pages/HLSStakingPage.tsx
+++ b/src/pages/HLSStakingPage.tsx
@@ -2,13 +2,11 @@ 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/common/MigrationBanner'
import { HLS_TABS } from 'constants/pages'
export default function HLSStakingPage() {
return (
-
diff --git a/src/pages/LendPage.tsx b/src/pages/LendPage.tsx
index 81a15210..450ede17 100644
--- a/src/pages/LendPage.tsx
+++ b/src/pages/LendPage.tsx
@@ -1,4 +1,3 @@
-import MigrationBanner from 'components/common/MigrationBanner'
import Tab from 'components/earn/Tab'
import LendIntro from 'components/earn/lend/LendIntro'
import Lends from 'components/earn/lend/Lends'
@@ -10,7 +9,6 @@ export default function LendPage() {
return (
-
{chainConfig.farm &&
}
diff --git a/src/pages/PortfolioAccountPage.tsx b/src/pages/PortfolioAccountPage.tsx
index b0308320..ab0464b5 100644
--- a/src/pages/PortfolioAccountPage.tsx
+++ b/src/pages/PortfolioAccountPage.tsx
@@ -1,6 +1,5 @@
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
-import MigrationBanner from 'components/common/MigrationBanner'
import ShareBar from 'components/common/ShareBar'
import Balances from 'components/portfolio/Account/Balances'
import BreadCrumbs from 'components/portfolio/Account/BreadCrumbs'
@@ -25,7 +24,6 @@ export default function PortfolioAccountPage() {
return (
-
diff --git a/src/pages/PortfolioPage.tsx b/src/pages/PortfolioPage.tsx
index 6e7269c2..0d05c6ae 100644
--- a/src/pages/PortfolioPage.tsx
+++ b/src/pages/PortfolioPage.tsx
@@ -1,13 +1,11 @@
-import MigrationBanner from 'components/common/MigrationBanner'
+import ShareBar from 'components/common/ShareBar'
import AccountOverview from 'components/portfolio/Overview'
import PortfolioSummary from 'components/portfolio/Overview/Summary'
import PortfolioIntro from 'components/portfolio/PortfolioIntro'
-import ShareBar from 'components/common/ShareBar'
export default function PortfolioPage() {
return (
-
diff --git a/src/pages/TradePage.tsx b/src/pages/TradePage.tsx
index 0a27a128..34eef272 100644
--- a/src/pages/TradePage.tsx
+++ b/src/pages/TradePage.tsx
@@ -1,7 +1,6 @@
import { useMemo } from 'react'
import { useLocation } from 'react-router-dom'
-import MigrationBanner from 'components/common/MigrationBanner'
import AccountDetailsCard from 'components/trade/AccountDetailsCard'
import TradeChart from 'components/trade/TradeChart'
import TradeModule from 'components/trade/TradeModule'
@@ -47,7 +46,6 @@ export default function TradePage() {
)
return (
-
diff --git a/src/pages/V1Page.tsx b/src/pages/V1Page.tsx
new file mode 100644
index 00000000..0236c50a
--- /dev/null
+++ b/src/pages/V1Page.tsx
@@ -0,0 +1,19 @@
+import Summary from 'components/portfolio/Account/Summary'
+import Borrowings from 'components/v1/Borrowings'
+import Deposits from 'components/v1/Deposits'
+import V1Intro from 'components/v1/V1Intro'
+import useStore from 'store'
+
+export default function V1Page() {
+ const address = useStore((s) => s.address)
+ return (
+
+
+ {address &&
}
+
+
+
+
+
+ )
+}
diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx
index dba5d0a4..72cb6af9 100644
--- a/src/pages/_layout.tsx
+++ b/src/pages/_layout.tsx
@@ -4,13 +4,14 @@ import { isMobile } from 'react-device-detect'
import { useLocation } from 'react-router-dom'
import { SWRConfig } from 'swr'
+import ModalsContainer from 'components/Modals/ModalsContainer'
import AccountDetails from 'components/account/AccountDetails'
import Background from 'components/common/Background'
import Footer from 'components/common/Footer'
import PageMetadata from 'components/common/PageMetadata'
import Toaster from 'components/common/Toaster'
import DesktopHeader from 'components/header/DesktopHeader'
-import ModalsContainer from 'components/Modals/ModalsContainer'
+import V1DesktopHeader from 'components/header/V1DesktopHeader'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { LocalStorageKeys } from 'constants/localStorageKeys'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
@@ -25,6 +26,8 @@ interface Props {
}
function PageContainer(props: Props) {
+ const isV1 = useStore((s) => s.isV1)
+
if (isMobile) return props.children
if (!props.focusComponent)
@@ -32,7 +35,8 @@ function PageContainer(props: Props) {
{props.children}
@@ -50,6 +54,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
const location = useLocation()
const focusComponent = useStore((s) => s.focusComponent)
const address = useStore((s) => s.address)
+ const isV1 = useStore((s) => s.isV1)
const [reduceMotion] = useLocalStorage
(
LocalStorageKeys.REDUCE_MOTION,
@@ -67,7 +72,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
-
+ {isV1 ? : }
{
+ let msg: RedBankExecuteMsg
+ let toastOptions: ToastObjectOptions = {
+ action: type,
+ accountId: get().address,
+ changes: {},
+ }
+ let funds: Coin[] = []
+
+ switch (type) {
+ case 'withdraw':
+ msg = {
+ withdraw: {
+ amount: coin.amount.toString(),
+ denom: coin.denom,
+ },
+ }
+ toastOptions = {
+ ...toastOptions,
+ changes: { deposits: [coin] },
+ target: 'wallet',
+ }
+ break
+ case 'repay':
+ msg = {
+ repay: {},
+ }
+ toastOptions.changes = { deposits: [coin] }
+ funds = [coin.toCoin()]
+ break
+ case 'borrow':
+ msg = {
+ borrow: {
+ amount: coin.amount.toString(),
+ denom: coin.denom,
+ },
+ }
+ toastOptions = {
+ ...toastOptions,
+ changes: { debts: [coin] },
+ target: 'wallet',
+ }
+ break
+ default:
+ msg = {
+ deposit: {},
+ }
+ toastOptions.changes = { deposits: [coin] }
+ funds = [coin.toCoin()]
+ }
+
+ const redBankContract = get().chainConfig.contracts.redBank
+
+ const response = get().executeMsg({
+ messages: [generateExecutionMessage(get().address, redBankContract, msg, funds)],
+ })
+
+ get().setToast({
+ response,
+ options: toastOptions,
+ })
+
+ return response.then((response) => !!response.result)
+ },
}
}
diff --git a/src/store/slices/common.ts b/src/store/slices/common.ts
index eee116cc..f8ffb448 100644
--- a/src/store/slices/common.ts
+++ b/src/store/slices/common.ts
@@ -19,5 +19,6 @@ export default function createCommonSlice(set: SetState, get: GetSt
useAutoRepay: true,
isOracleStale: false,
isHLS: false,
+ isV1: false,
}
}
diff --git a/src/store/slices/modal.ts b/src/store/slices/modal.ts
index fe373b2a..76277afe 100644
--- a/src/store/slices/modal.ts
+++ b/src/store/slices/modal.ts
@@ -19,5 +19,7 @@ export default function createModalSlice(set: SetState, get: GetStat
vaultModal: null,
walletAssetsModal: null,
withdrawFromVaultsModal: null,
+ v1DepositAndWithdrawModal: null,
+ v1BorrowAndRepayModal: null,
}
}
diff --git a/src/types/interfaces/market.d.ts b/src/types/interfaces/market.d.ts
index f96d2b47..9858f4b0 100644
--- a/src/types/interfaces/market.d.ts
+++ b/src/types/interfaces/market.d.ts
@@ -17,7 +17,8 @@ interface Market {
}
interface BorrowMarketTableData extends Market {
- accountDebt?: BigNumber
+ accountDebtAmount?: BigNumber
+ accountDebtValue?: BigNumber
}
interface LendingMarketTableData extends Market {
diff --git a/src/types/interfaces/route.d.ts b/src/types/interfaces/route.d.ts
index 59fff097..cd4f5fca 100644
--- a/src/types/interfaces/route.d.ts
+++ b/src/types/interfaces/route.d.ts
@@ -11,6 +11,7 @@ type Page =
| 'hls-staking'
| 'governance'
| 'execute'
+ | 'v1'
type OsmosisRouteResponse = {
amount_in: {
diff --git a/src/types/interfaces/store/broadcast.d.ts b/src/types/interfaces/store/broadcast.d.ts
index 719c1c69..1ec3edd1 100644
--- a/src/types/interfaces/store/broadcast.d.ts
+++ b/src/types/interfaces/store/broadcast.d.ts
@@ -167,4 +167,7 @@ interface BroadcastSlice {
borrow: BNCoin[]
reclaims: ActionCoin[]
}) => Promise
+ v1Action: (type: V1ActionType, funds: BNCoin) => Promise
}
+
+type V1ActionType = 'withdraw' | 'deposit' | 'borrow' | 'repay'
diff --git a/src/types/interfaces/store/common.d.ts b/src/types/interfaces/store/common.d.ts
index 93d61291..9c9122cd 100644
--- a/src/types/interfaces/store/common.d.ts
+++ b/src/types/interfaces/store/common.d.ts
@@ -18,6 +18,7 @@ interface CommonSlice {
useAutoRepay: boolean
isOracleStale: boolean
isHLS: boolean
+ isV1: boolean
}
interface FocusComponent {
diff --git a/src/types/interfaces/store/modals.d.ts b/src/types/interfaces/store/modals.d.ts
index ceef9fa6..e0fc378f 100644
--- a/src/types/interfaces/store/modals.d.ts
+++ b/src/types/interfaces/store/modals.d.ts
@@ -15,6 +15,8 @@ interface ModalSlice {
vaultModal: VaultModal | null
walletAssetsModal: WalletAssetModal | null
withdrawFromVaultsModal: DepositedVault[] | null
+ v1DepositAndWithdrawModal: V1DepositAndWithdrawModal | null
+ v1BorrowAndRepayModal: V1BorrowAndRepayModal | null
}
interface AlertDialogButton {
@@ -84,3 +86,13 @@ interface HlsManageModal {
}
type HlsStakingManageAction = 'deposit' | 'withdraw' | 'repay' | 'leverage'
+
+interface V1DepositAndWithdrawModal {
+ type: 'deposit' | 'withdraw'
+ data: LendingMarketTableData
+}
+
+interface V1BorrowAndRepayModal {
+ type: 'borrow' | 'repay'
+ data: BorrowMarketTableData
+}
diff --git a/src/types/interfaces/v1.d.ts b/src/types/interfaces/v1.d.ts
new file mode 100644
index 00000000..e40c3902
--- /dev/null
+++ b/src/types/interfaces/v1.d.ts
@@ -0,0 +1,4 @@
+interface V1Positions {
+ deposits: BNCoin[]
+ debts: BNCoin[]
+}
diff --git a/src/utils/getCurrentChainId.ts b/src/utils/getCurrentChainId.ts
index 2350c12a..701e5400 100644
--- a/src/utils/getCurrentChainId.ts
+++ b/src/utils/getCurrentChainId.ts
@@ -23,6 +23,10 @@ export const getCurrentChainId = () => {
if (currentNetwork === NETWORK.TESTNET) chainId = ChainInfoID.OsmosisDevnet
break
+ case 'neutron':
+ if (currentNetwork === NETWORK.MAINNET) chainId = ChainInfoID.Neutron1
+ break
+
case 'testnet-neutron':
if (currentNetwork === NETWORK.TESTNET) chainId = ChainInfoID.Pion1
break
@@ -42,6 +46,10 @@ export const getCurrentChainId = () => {
if (currentNetwork === NETWORK.TESTNET) chainId = ChainInfoID.OsmosisDevnet
break
+ case ChainInfoID.Neutron1:
+ if (currentNetwork === NETWORK.MAINNET) chainId = ChainInfoID.Neutron1
+ break
+
case ChainInfoID.Pion1:
if (currentNetwork === NETWORK.TESTNET) chainId = ChainInfoID.Pion1
break
diff --git a/src/utils/route.ts b/src/utils/route.ts
index aae1b19e..723cc922 100644
--- a/src/utils/route.ts
+++ b/src/utils/route.ts
@@ -40,6 +40,7 @@ export function getPage(pathname: string): Page {
'portfolio',
'hls-farm',
'hls-staking',
+ 'v1',
]
const segments = pathname.split('/')
diff --git a/src/utils/url.ts b/src/utils/url.ts
index a3a331c9..d411c924 100644
--- a/src/utils/url.ts
+++ b/src/utils/url.ts
@@ -1,9 +1,10 @@
-export const getUrl = (baseUrl: string, path: string): string => {
+export const getUrl = (baseUrl: string, path?: string): string => {
+ if (!path) path = ''
const isPlaceholder = baseUrl.split('APP_').length > 1
if (isPlaceholder) return baseUrl + '/' + path
- const url = new URL(baseUrl)
+ const url = new URL(baseUrl.split('?')[0])
if (process.env.NEXT_PUBLIC_API_KEY)
return `${url.href}${path}?x-apikey=${process.env.NEXT_PUBLIC_API_KEY}`
diff --git a/tailwind.config.js b/tailwind.config.js
index 321717d1..6a9d67ff 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -240,6 +240,7 @@ module.exports = {
},
maxWidth: {
content: '1024px',
+ v1: '1248px',
modal: '895px',
'modal-md': '556px',
'modal-sm': '526px',
@@ -259,7 +260,7 @@ module.exports = {
screens: {
sm: '480px',
md: '720px',
- lg: '1024px',
+ lg: '1280px',
xl: '1280px',
'2xl': '1920px',
},
@@ -504,6 +505,12 @@ module.exports = {
textTransform: 'uppercase',
letterSpacing: '9px',
},
+ '.bg-v1': {
+ backgroundImage: 'url(/images/bg-v1.svg)',
+ backgroundRepeat: 'no-repeat',
+ backgroundSize: '100% auto',
+ backgroundPosition: 'top',
+ },
})
}),
plugin(({ matchUtilities, theme }) => {