diff --git a/public/images/bg.svg b/public/images/bg.svg deleted file mode 100644 index eb576ac3..00000000 --- a/public/images/bg.svg +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/sort-asc.svg b/public/images/sort-asc.svg new file mode 100644 index 00000000..14ac909a --- /dev/null +++ b/public/images/sort-asc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/sort-desc.svg b/public/images/sort-desc.svg new file mode 100644 index 00000000..965f6643 --- /dev/null +++ b/public/images/sort-desc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/images/sort-none.svg b/public/images/sort-none.svg new file mode 100644 index 00000000..c8825c6b --- /dev/null +++ b/public/images/sort-none.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/wallets/[wallet]/accounts/[account]/borrow/loading.tsx b/src/app/wallets/[wallet]/accounts/[account]/borrow/loading.tsx index 8c8208af..955eca17 100644 --- a/src/app/wallets/[wallet]/accounts/[account]/borrow/loading.tsx +++ b/src/app/wallets/[wallet]/accounts/[account]/borrow/loading.tsx @@ -1,3 +1,5 @@ -export default function Loading() { - return '...isLoading' +import Loading from 'components/Loading' + +export default function page() { + return } diff --git a/src/app/wallets/[wallet]/accounts/[account]/borrow/page.tsx b/src/app/wallets/[wallet]/accounts/[account]/borrow/page.tsx index 1a636668..56715d0d 100644 --- a/src/app/wallets/[wallet]/accounts/[account]/borrow/page.tsx +++ b/src/app/wallets/[wallet]/accounts/[account]/borrow/page.tsx @@ -1,31 +1,51 @@ -import { BorrowTable } from 'components/BorrowTable' -import { AccountDebtTable } from 'components/AccountDebtTable' +import { BorrowTable } from 'components/Borrow/BorrowTable' import { Card } from 'components/Card' import Loading from 'components/Loading' import { Text } from 'components/Text' import { Suspense } from 'react' +import { getAccountDebts, getBorrowData } from 'utils/api' +import { getMarketAssets } from 'utils/assets' + +export default async function page({ params }: { params: PageParams }) { + const debtData = await getAccountDebts(params.account) + const borrowData = await getBorrowData() + + const marketAssets = getMarketAssets() + + const { available, active } = marketAssets.reduce( + (prev: { available: BorrowAsset[]; active: BorrowAssetActive[] }, curr) => { + const borrow = borrowData.find((borrow) => borrow.denom === curr.denom) + if (borrow) { + const debt = debtData.find((debt) => debt.denom === curr.denom) + if (debt) { + prev.active.push({ + ...borrow, + debt: { + amount: '100000', + value: '12389478321', + }, + }) + } else { + prev.available.push(borrow) + } + } + return prev + }, + { available: [], active: [] }, + ) -export default function page({ params }: { params: PageParams }) { return (
- - - Debt data - - }> - {/* @ts-expect-error Server Component */} - - - - - - Borrow data - - }> - {/* @ts-expect-error Server Component */} - - - + {active.length > 0 && ( + + + + )} + {available.length > 0 && ( + + + + )}
) } diff --git a/src/app/wallets/[wallet]/accounts/[account]/council/page.tsx b/src/app/wallets/[wallet]/accounts/[account]/council/page.tsx index eed5dfc7..6cb165eb 100644 --- a/src/app/wallets/[wallet]/accounts/[account]/council/page.tsx +++ b/src/app/wallets/[wallet]/accounts/[account]/council/page.tsx @@ -1,13 +1,10 @@ import { Card } from 'components/Card' -import { Text } from 'components/Text' export default function page() { return (
- - - Council Placeholder - + + <>
) diff --git a/src/app/wallets/[wallet]/accounts/[account]/earn/page.tsx b/src/app/wallets/[wallet]/accounts/[account]/earn/page.tsx index 0f22c202..cfe7607a 100644 --- a/src/app/wallets/[wallet]/accounts/[account]/earn/page.tsx +++ b/src/app/wallets/[wallet]/accounts/[account]/earn/page.tsx @@ -4,18 +4,9 @@ import { Text } from 'components/Text' export default function page() { return (
- - - Yield Module - + + <> -
- - - Placeholder - - -
) } diff --git a/src/app/wallets/[wallet]/accounts/[account]/trade/page.tsx b/src/app/wallets/[wallet]/accounts/[account]/trade/page.tsx index b073d940..e841dbef 100644 --- a/src/app/wallets/[wallet]/accounts/[account]/trade/page.tsx +++ b/src/app/wallets/[wallet]/accounts/[account]/trade/page.tsx @@ -1,28 +1,20 @@ import { Card } from 'components/Card' -import { Text } from 'components/Text' export default function page() { return (
- - - Tradingview Graph - + + <>
- {/* */} - - - Orderbook module (optional) - + + <>
- - - Order history - + + <>
) diff --git a/src/app/wallets/[wallet]/layout.tsx b/src/app/wallets/[wallet]/layout.tsx index cb2bedf8..04bb3c60 100644 --- a/src/app/wallets/[wallet]/layout.tsx +++ b/src/app/wallets/[wallet]/layout.tsx @@ -15,7 +15,7 @@ export default async function RootLayout({
- {children} +
{children}
) } diff --git a/src/components/Background.tsx b/src/components/Background.tsx index 18094a06..1042c2ce 100644 --- a/src/components/Background.tsx +++ b/src/components/Background.tsx @@ -1,20 +1,10 @@ 'use client' -import { useWalletManager, WalletConnectionStatus } from '@marsprotocol/wallet-connector' import classNames from 'classnames' -const filter = { - day: 'brightness-100 hue-rotate-0', - night: '-hue-rotate-82 brightness-30', -} - export default function Background() { - const { status } = useWalletManager() - const backgroundClasses = classNames( - status === WalletConnectionStatus.Connected ? filter.day : filter.night, - 'top-0 left-0 absolute block h-full w-full flex-col bg-body bg-mars bg-desktop bg-top bg-no-repeat filter', - true && 'transition-background duration-3000 ease-linear', + 'top-0 left-0 absolute block h-full w-full flex-col bg-body bg-desktop bg-top bg-no-repeat filter bg-[#06040C]', ) return
diff --git a/src/components/Borrow/AssetExpanded.tsx b/src/components/Borrow/AssetExpanded.tsx new file mode 100644 index 00000000..a7725030 --- /dev/null +++ b/src/components/Borrow/AssetExpanded.tsx @@ -0,0 +1,41 @@ +import React from 'react' + +import { getMarketAssets } from 'utils/assets' +import { Row } from '@tanstack/react-table' +import { Button } from 'components/Button' +import { useRouter } from 'next/navigation' + +type AssetRowProps = { + row: Row + onBorrowClick: () => void + onRepayClick: () => void + resetExpanded: (defaultState?: boolean | undefined) => void +} + +export default function AssetExpanded(props: AssetRowProps) { + const router = useRouter() + const marketAssets = getMarketAssets() + const asset = marketAssets.find((asset) => asset.denom === props.row.original.denom) + + if (!asset) return null + + return ( + { + e.preventDefault() + const isExpanded = props.row.getIsExpanded() + props.resetExpanded() + !isExpanded && props.row.toggleExpanded() + }} + > + +
+
+ + + ) +} diff --git a/src/components/Borrow/AssetRow.tsx b/src/components/Borrow/AssetRow.tsx index b6b4a847..44ddce8a 100644 --- a/src/components/Borrow/AssetRow.tsx +++ b/src/components/Borrow/AssetRow.tsx @@ -1,85 +1,37 @@ -import Image from 'next/image' -import React, { useState } from 'react' +import React from 'react' -import { ChevronDown, ChevronUp } from 'components/Icons' -import { formatCurrency } from 'utils/formatters' -import { Button } from 'components/Button' +import { getMarketAssets } from 'utils/assets' +import { flexRender, Row } from '@tanstack/react-table' type AssetRowProps = { - data: { - denom: string - symbol: string - logo: string - name: string - borrowed: { - amount: number - value: number - } | null - borrowRate: number - marketLiquidity: number - } - onBorrowClick: () => void - onRepayClick: () => void + row: Row + resetExpanded: (defaultState?: boolean | undefined) => void } -export const AssetRow = ({ data, onBorrowClick, onRepayClick }: AssetRowProps) => { - const [isExpanded, setIsExpanded] = useState(false) +export const AssetRow = (props: AssetRowProps) => { + const marketAssets = getMarketAssets() + const asset = marketAssets.find((asset) => asset.denom === props.row.original.denom) + + if (!asset) return null return ( -
setIsExpanded((current) => !current)} + { + e.preventDefault() + const isExpanded = props.row.getIsExpanded() + props.resetExpanded() + !isExpanded && props.row.toggleExpanded() + }} > -
-
- token -
-
{data.symbol}
-
{data.name}
-
-
-
- {data.borrowRate ? `${(data.borrowRate * 100).toFixed(2)}%` : '-'} -
-
- {data.borrowed ? ( -
-
{data.borrowed.amount}
-
{formatCurrency(data.borrowed.value)}
-
- ) : ( - '-' - )} -
-
{data.marketLiquidity}
-
-
{isExpanded ? : }
-
-
- {isExpanded && ( -
-
Additional Stuff Placeholder
-
- - -
-
- )} -
+ {props.row.getVisibleCells().map((cell, index) => { + return ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ) + })} + ) } diff --git a/src/components/Borrow/BorrowTable.tsx b/src/components/Borrow/BorrowTable.tsx index 8280502a..7f98ded4 100644 --- a/src/components/Borrow/BorrowTable.tsx +++ b/src/components/Borrow/BorrowTable.tsx @@ -1,3 +1,5 @@ +'use client' + import { ColumnDef, flexRender, @@ -11,76 +13,53 @@ import React from 'react' import { AssetRow } from 'components/Borrow/AssetRow' import { ChevronDown, ChevronUp } from 'components/Icons' -import { formatCurrency } from 'utils/formatters' - -interface Market { - denom: string - symbol: string - logo: string - name: string - borrowed: { - amount: number - value: number - } | null - borrowRate: number - marketLiquidity: number -} +import { getMarketAssets } from 'utils/assets' +import classNames from 'classnames' +import AssetExpanded from './AssetExpanded' type Props = { - data: Market[] - onBorrowClick: (denom: string) => void - onRepayClick: (denom: string) => void + data: BorrowAsset[] | BorrowAssetActive[] } -export const BorrowTable = ({ data, onBorrowClick, onRepayClick }: Props) => { +export const BorrowTable = (props: Props) => { const [sorting, setSorting] = React.useState([]) + const marketAssets = getMarketAssets() - const columns = React.useMemo[]>( + const columns = React.useMemo[]>( () => [ { header: 'Asset', id: 'symbol', - accessorFn: (row) => ( -
- token -
-
{row.symbol}
-
{row.name}
+ cell: ({ row }) => { + const asset = marketAssets.find((asset) => asset.denom === row.original.denom) + + if (!asset) return null + + return ( +
+ token +
+
{asset.symbol}
+
{asset.name}
+
-
- ), - cell: (info) => info.getValue(), + ) + }, }, { accessorKey: 'borrowRate', header: 'Borrow Rate', - accessorFn: (row) => ( -
- {row.borrowRate ? `${(row.borrowRate * 100).toFixed(2)}%` : '-'} -
- ), - cell: (info) => info.getValue(), + cell: ({ row }) =>
{(Number(row.original.borrowRate) * 100).toFixed(2)}%
, }, { - accessorKey: 'age', - header: 'Borrowed', - accessorFn: (row) => ( -
- {row.borrowed ? ( -
-
{row.borrowed.amount}
-
{formatCurrency(row.borrowed.value)}
-
- ) : ( - '-' - )} -
- ), - cell: (info) => info.getValue(), - }, - { - accessorKey: 'marketLiquidity', + accessorKey: 'liquidity', header: 'Liquidity Available', + cell: ({ row }) => ( +
+
{row.original.liquidity.amount}
+
${row.original.liquidity.value}
+
+ ), }, { accessorKey: 'status', @@ -98,7 +77,7 @@ export const BorrowTable = ({ data, onBorrowClick, onRepayClick }: Props) => { ) const table = useReactTable({ - data, + data: props.data, columns, state: { sorting, @@ -110,47 +89,67 @@ export const BorrowTable = ({ data, onBorrowClick, onRepayClick }: Props) => { }) return ( -
- {table.getHeaderGroups().map((headerGroup) => ( -
- {headerGroup.headers.map((header) => { - return ( -
- {header.isPlaceholder ? null : ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header, index) => { + return ( + + ) + })} + + ))} + + + {table.getRowModel().rows.map((row) => { + if (row.getIsExpanded()) { return ( - onBorrowClick(row.original.denom)} - onRepayClick={() => onRepayClick(row.original.denom)} - /> + + + {}} + onRepayClick={() => {}} + resetExpanded={table.resetExpanded} + /> + ) - }) - )} - - + } + return + })} + +
- {flexRender(header.column.columnDef.header, header.getContext())} - {{ - asc: ' 🔼', - desc: ' 🔽', - }[header.column.getIsSorted() as string] ?? null} + {header.column.getCanSort() + ? { + asc: ( + mars + ), + desc: ( + mars + ), + false: ( + mars + ), + }[header.column.getIsSorted() as string] ?? null + : null} + {flexRender(header.column.columnDef.header, header.getContext())}
- )} - - ) - })} - - ))} -
- {table.getRowModel().rows.length === 0 ? ( -
No Data
- ) : ( - table.getRowModel().rows.map((row) => { +
) } diff --git a/src/components/BorrowTable.tsx b/src/components/BorrowTable.tsx deleted file mode 100644 index e15d5291..00000000 --- a/src/components/BorrowTable.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { getBorrowData } from 'utils/api' - -export async function BorrowTable() { - const borrowData = await getBorrowData() - - return borrowData.map((borrow) => { - return ( -

- {borrow.denom} {borrow.borrowRate} {borrow.marketLiquidity} -

- ) - }) -} diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 8e8be983..fa011111 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -2,19 +2,21 @@ import classNames from 'classnames' import { ReactNode } from 'react' interface Props { + title: string children: ReactNode className?: string } -export const Card = ({ children, className }: Props) => { +export const Card = (props: Props) => { return ( -
- {children} -
+
{props.title}
+
{props.children}
+ ) } diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx index 6322b116..04fd64dd 100644 --- a/src/components/Loading.tsx +++ b/src/components/Loading.tsx @@ -14,10 +14,10 @@ export default function Loading(props: Props) {
diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index a3f9660f..9855f2b0 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -20,7 +20,10 @@ export const Modal = ({ children, content, className, open, setOpen }: Props) => return open ? (
- + {setOpen && ( { - const marketAssets = getMarketAssets() - - const [modalState, setModalState] = useState({ - show: false, - data: { tokenDenom: '' }, - }) - - const selectedAccount = useStore((s) => s.selectedAccount) - - const { data: allowedCoinsData } = useAllowedCoins() - const { data: positionsData } = useCreditAccountPositions(selectedAccount ?? '') - const { data: marketsData } = useMarkets() - const { data: tokenPrices } = useTokenPrices() - const { data: redbankBalances } = useRedbankBalances() - - // recreate modals and reset state whenever ref changes - const modalId = useRef(0) - - const borrowedAssetsMap = useMemo(() => { - let borrowedAssetsMap: Map = new Map() - - positionsData?.debts.forEach((coin) => { - borrowedAssetsMap.set(coin.denom, coin.amount) - }) - - return borrowedAssetsMap - }, [positionsData]) - - const { borrowedAssets, notBorrowedAssets } = useMemo(() => { - return { - borrowedAssets: - allowedCoinsData - ?.filter((denom) => borrowedAssetsMap.has(denom)) - .map((denom) => { - const { symbol, name, logo } = getTokenInfo(denom, marketAssets) - const borrowRate = Number(marketsData?.[denom].borrow_rate) || 0 - const marketLiquidity = BigNumber( - redbankBalances?.find((asset) => asset.denom.toLowerCase() === denom.toLowerCase()) - ?.amount || 0, - ) - .div(10 ** getTokenDecimals(denom, marketAssets)) - .toNumber() - - const borrowAmount = BigNumber(borrowedAssetsMap.get(denom) as string) - .div(10 ** getTokenDecimals(denom, marketAssets)) - .toNumber() - const borrowValue = borrowAmount * (tokenPrices?.[denom] ?? 0) - - const rowData = { - denom, - symbol, - logo, - name, - borrowed: { - amount: borrowAmount, - value: borrowValue, - }, - borrowRate, - marketLiquidity, - } - - return rowData - }) ?? [], - notBorrowedAssets: - allowedCoinsData - ?.filter((denom) => !borrowedAssetsMap.has(denom)) - .map((denom) => { - const { symbol, name, logo } = getTokenInfo(denom, marketAssets) - const borrowRate = Number(marketsData?.[denom].borrow_rate) || 0 - const marketLiquidity = BigNumber( - redbankBalances?.find((asset) => asset.denom.toLowerCase() === denom.toLowerCase()) - ?.amount || 0, - ) - .div(10 ** getTokenDecimals(denom, marketAssets)) - .toNumber() - - const rowData = { - denom, - symbol, - logo, - name, - borrowed: null, - borrowRate, - marketLiquidity, - } - - return rowData - }) ?? [], - } - }, [allowedCoinsData, borrowedAssetsMap, marketsData, redbankBalances, tokenPrices, marketAssets]) - - const handleBorrowClick = (denom: string) => { - setModalState({ show: 'borrow', data: { tokenDenom: denom } }) - modalId.current += 1 - } - - const handleRepayClick = (denom: string) => { - setModalState({ show: 'repay', data: { tokenDenom: denom } }) - modalId.current += 1 - } - - return ( -
-
- -
- - Borrowings - - -
-
- -
- - Available to Borrow - - -
-
-
- setModalState({ ...modalState, show: false })} - /> - setModalState({ ...modalState, show: false })} - /> -
- ) -} - -export default Borrow diff --git a/src/pages/api/markets/borrow.ts b/src/pages/api/markets/borrow.ts index 1b358828..71ad8562 100644 --- a/src/pages/api/markets/borrow.ts +++ b/src/pages/api/markets/borrow.ts @@ -3,6 +3,7 @@ import { NextApiRequest, NextApiResponse } from 'next' import { ENV_MISSING_MESSAGE, URL_API } from 'constants/env' import { getMarketAssets } from 'utils/assets' import { Coin } from '@cosmjs/stargate' +import BigNumber from 'bignumber.js' export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (!URL_API) { @@ -12,18 +13,25 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const marketAssets = getMarketAssets() const $liquidity = fetch(`${URL_API}/markets/liquidity`) const $markets = fetch(`${URL_API}/markets`) + const $prices = fetch(`${URL_API}/prices`) - const borrow: BorrowData[] = await Promise.all([$liquidity, $markets]).then( - async ([$liquidity, $markets]) => { + const borrow: BorrowAsset[] = await Promise.all([$liquidity, $markets, $prices]).then( + async ([$liquidity, $markets, $prices]) => { const liquidity: Coin[] = await $liquidity.json() const markets: Market[] = await $markets.json() + const prices: Coin[] = await $prices.json() return marketAssets.map((asset) => { const currentMarket = markets.find((market) => market.denom === asset.denom) + const price = prices.find((coin) => coin.denom === asset.denom)?.amount ?? '1' + const amount = liquidity.find((coin) => coin.denom === asset.denom)?.amount ?? '0' return { denom: asset.denom, borrowRate: currentMarket?.borrow_rate ?? '0', - marketLiquidity: liquidity.find((coin) => coin.denom === asset.denom)?.amount ?? '0', + liquidity: { + amount: amount, + value: new BigNumber(amount).times(price).toString(), + }, } }) }, @@ -35,9 +43,3 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.status(404) } - -export interface BorrowData { - denom: string - borrowRate: string - marketLiquidity: string -} diff --git a/src/pages/api/prices/index.ts b/src/pages/api/prices/index.ts index ff4f20c5..1b1b710e 100644 --- a/src/pages/api/prices/index.ts +++ b/src/pages/api/prices/index.ts @@ -3,6 +3,7 @@ import { NextApiRequest, NextApiResponse } from 'next' import { ADDRESS_ORACLE, ENV_MISSING_MESSAGE, URL_GQL } from 'constants/env' import { getMarketAssets } from 'utils/assets' +import { Coin } from '@cosmjs/stargate' export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (!URL_GQL || !ADDRESS_ORACLE) { @@ -31,13 +32,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) `, ) - const data = Object.values(result?.prices).reduce( - (acc, entry) => ({ - ...acc, - [entry.denom]: Number(entry.price), - }), - {}, - ) as { [key in string]: number } + const data: Coin[] = Object.values(result?.prices).reduce((acc: Coin[], curr) => { + return [...acc, { denom: curr.denom, amount: curr.price }] as Coin[] + }, []) return res.status(200).json(data) } diff --git a/src/types/interfaces/asset.d.ts b/src/types/interfaces/asset.d.ts index ac44e635..a298af47 100644 --- a/src/types/interfaces/asset.d.ts +++ b/src/types/interfaces/asset.d.ts @@ -15,3 +15,19 @@ interface Asset { interface OtherAsset extends Omit { symbol: 'MARS' } + +interface BorrowAsset { + denom: string + borrowRate: string + liquidity: { + amount: string + value: string + } +} + +interface BorrowAssetActive extends BorrowAsset { + debt: { + amount: string + value: string + } +} diff --git a/src/utils/api.ts b/src/utils/api.ts index 232ee776..848bf845 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,6 +1,5 @@ import { Coin } from '@cosmjs/stargate' import { URL_API } from 'constants/env' -import { BorrowData } from 'pages/api/markets/borrow' export async function callAPI(endpoint: string): Promise { const response = await fetch(`${URL_API}${endpoint}`, { @@ -11,7 +10,7 @@ export async function callAPI(endpoint: string): Promise { } export async function getBorrowData() { - return callAPI('/markets/borrow') + return callAPI('/markets/borrow') } export async function getCreditAccounts(address: string) { diff --git a/tailwind.config.js b/tailwind.config.js index 791a9c4c..a0ee8f7c 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -36,7 +36,6 @@ module.exports = { fadein: 'fadein 1s ease-in-out forwards', }, backgroundImage: { - mars: 'url(/images/bg.svg)', 'fund-modal': 'url(/images/fund-bg.webp), url(/images/fund-bg.png)', 'delete-modal': 'url(/images/delete-account-bg.webp), url(/images/delete-account-bg.png)', 'create-modal': 'url(/images/create-account-bg.webp), url(/images/create-account-bg.png)',