From 3c3e722c491114823ca7310b7606ec794902a5d0 Mon Sep 17 00:00:00 2001 From: Linkie Link Date: Tue, 4 Jul 2023 16:15:36 +0200 Subject: [PATCH] Mp 2821 unlocked positions (#276) --- src/api/vaults/getDepositedVaults.ts | 28 +- src/components/Button/index.tsx | 4 +- .../Earn/Lend/LendingMarketsTable.tsx | 18 +- src/components/Earn/Tab.tsx | 2 +- src/components/Earn/vault/VaultExpanded.tsx | 118 +++-- src/components/Earn/vault/VaultTable.tsx | 50 +- .../Earn/vault/VaultUnlockBanner.tsx | 60 +++ src/components/Earn/vault/Vaults.tsx | 56 ++- src/components/Icons/Account.svg | 7 +- src/components/Icons/AccountArrowDown.svg | 8 + src/components/Icons/ArrowChartLineUp.svg | 7 +- src/components/Icons/ArrowCircledTopRight.svg | 7 +- src/components/Icons/ArrowDownLine.svg | 7 +- src/components/Icons/ArrowRight.svg | 8 +- src/components/Icons/ArrowUpLine.svg | 7 +- src/components/Icons/Check.svg | 6 +- src/components/Icons/CheckCircled.svg | 7 +- src/components/Icons/ChevronDown.svg | 9 +- src/components/Icons/ChevronLeft.svg | 7 +- src/components/Icons/ChevronRight.svg | 9 +- src/components/Icons/ChevronUp.svg | 7 +- src/components/Icons/Copy.svg | 6 +- src/components/Icons/Cross.svg | 2 +- src/components/Icons/CrossCircled.svg | 7 +- src/components/Icons/Enter.svg | 7 +- .../Icons/ExclamationMarkCircled.svg | 8 +- .../Icons/ExclamationMarkTriangle.svg | 9 +- src/components/Icons/ExternalLink.svg | 6 +- src/components/Icons/Gear.svg | 12 +- src/components/Icons/Heart.svg | 9 +- src/components/Icons/LockLocked.svg | 8 + src/components/Icons/LockUnlocked.svg | 7 +- src/components/Icons/Logo.svg | 173 ++++++- src/components/Icons/MarsProtocol.svg | 80 +++- src/components/Icons/Osmo.svg | 450 +++++++++--------- src/components/Icons/OverlayMark.svg | 5 +- src/components/Icons/Plus.svg | 10 +- src/components/Icons/PlusCircled.svg | 7 +- src/components/Icons/Questionmark.svg | 8 +- src/components/Icons/Search.svg | 7 +- src/components/Icons/Shield.svg | 7 +- src/components/Icons/SortAsc.svg | 14 +- src/components/Icons/SortDesc.svg | 14 +- src/components/Icons/SortNone.svg | 13 +- src/components/Icons/Subtract.svg | 6 +- src/components/Icons/TrashBin.svg | 7 +- src/components/Icons/Wallet.svg | 4 +- src/components/Icons/index.ts | 2 + src/components/MarketAssetTable/index.tsx | 5 +- src/components/Modal.tsx | 5 +- .../AddVaultBorrowAssetsModal.tsx | 2 +- .../AssetAmountSelectActionModal/index.tsx | 6 +- src/components/Modals/Borrow/BorrowModal.tsx | 4 +- src/components/Modals/ModalsContainer.tsx | 16 +- src/components/Modals/Unlock/UnlockModal.tsx | 6 +- .../Modals/Unlock/UnlockModalContent.tsx | 12 +- src/components/Modals/Vault/index.tsx | 4 +- .../WithdrawFromVaults/WithdrawFromVaults.tsx | 100 ++++ src/components/Modals/index.tsx | 7 + src/components/NotificationBanner.tsx | 58 +++ src/components/Overlay.tsx | 2 +- src/components/SearchBar.tsx | 4 +- .../TradeModule/AssetSelector/AssetList.tsx | 2 +- .../AssetSelector/AssetOverlay.tsx | 4 +- src/constants/vaults.ts | 6 +- src/pages/FarmPage.tsx | 4 +- src/pages/LendPage.tsx | 6 +- src/pages/_layout.tsx | 2 +- src/store/slices/broadcast.ts | 61 ++- src/store/slices/modal.ts | 1 + src/types/enums/vault.ts | 5 + src/types/interfaces/store/broadcast.d.ts | 12 +- src/types/interfaces/store/modals.d.ts | 1 + src/types/interfaces/vaults.d.ts | 1 + src/utils/formatters.ts | 14 + src/utils/vaults.ts | 7 +- tailwind.config.js | 39 +- 77 files changed, 1251 insertions(+), 465 deletions(-) create mode 100644 src/components/Earn/vault/VaultUnlockBanner.tsx create mode 100644 src/components/Icons/AccountArrowDown.svg create mode 100644 src/components/Icons/LockLocked.svg create mode 100644 src/components/Modals/WithdrawFromVaults/WithdrawFromVaults.tsx create mode 100644 src/components/Modals/index.tsx create mode 100644 src/components/NotificationBanner.tsx create mode 100644 src/types/enums/vault.ts diff --git a/src/api/vaults/getDepositedVaults.ts b/src/api/vaults/getDepositedVaults.ts index 0cb22276..9845f272 100644 --- a/src/api/vaults/getDepositedVaults.ts +++ b/src/api/vaults/getDepositedVaults.ts @@ -1,13 +1,14 @@ import moment from 'moment' import { getClient, getCreditManagerQueryClient, getVaultQueryClient } from 'api/cosmwasm-client' -import getVaults from 'api/vaults/getVaults' -import { BN } from 'utils/helpers' import getPrice from 'api/prices/getPrice' +import getVaults from 'api/vaults/getVaults' +import { VaultStatus } from 'types/enums/vault' import { VaultPosition, VaultPositionAmount, } from 'types/generated/mars-credit-manager/MarsCreditManager.types' +import { BN } from 'utils/helpers' async function getUnlocksAtTimestamp(unlockingId: number, vaultAddress: string) { try { @@ -23,26 +24,24 @@ async function getUnlocksAtTimestamp(unlockingId: number, vaultAddress: string) } } -async function getVaultPositionStatusAndUnlockTime( +async function getVaultPositionStatusAndUnlockIdAndUnlockTime( vaultPosition: VaultPosition, -): Promise<[VaultStatus, number | undefined]> { +): Promise<[VaultStatus, number | undefined, number | undefined]> { const amount = vaultPosition.amount - if ('unlocked' in amount) return ['unlocked', undefined] + if (VaultStatus.UNLOCKED in amount) return [VaultStatus.UNLOCKED, undefined, undefined] if (amount.locking.unlocking.length) { - const unlocksAtTimestamp = await getUnlocksAtTimestamp( - amount.locking.unlocking[0].id, - vaultPosition.vault.address, - ) + const unlockId = amount.locking.unlocking[0].id + const unlocksAtTimestamp = await getUnlocksAtTimestamp(unlockId, vaultPosition.vault.address) if (moment(unlocksAtTimestamp).isBefore(new Date())) { - return ['unlocked', unlocksAtTimestamp] + return [VaultStatus.UNLOCKED, unlockId, unlocksAtTimestamp] } - return ['unlocking', unlocksAtTimestamp] + return [VaultStatus.UNLOCKING, unlockId, unlocksAtTimestamp] } else { - return ['active', undefined] + return [VaultStatus.ACTIVE, undefined, undefined] } } @@ -153,14 +152,15 @@ async function getDepositedVaults(accountId: string): Promise throw 'Could not find the deposited vault among all vaults' } - const [[status, unlocksAt], valuesAndAmounts] = await Promise.all([ - getVaultPositionStatusAndUnlockTime(vaultPosition), + const [[status, unlockId, unlocksAt], valuesAndAmounts] = await Promise.all([ + getVaultPositionStatusAndUnlockIdAndUnlockTime(vaultPosition), getVaultValuesAndAmounts(vault, vaultPosition), ]) return { ...vault, status, + unlockId, unlocksAt, ...valuesAndAmounts, } diff --git a/src/components/Button/index.tsx b/src/components/Button/index.tsx index ed4affcd..9905a8ed 100644 --- a/src/components/Button/index.tsx +++ b/src/components/Button/index.tsx @@ -1,7 +1,6 @@ import classNames from 'classnames' import React, { LegacyRef, ReactElement, ReactNode, useMemo } from 'react' -import { CircularProgress } from 'components/CircularProgress' import { buttonBorderClasses, buttonColorClasses, @@ -15,6 +14,7 @@ import { focusClasses, } from 'components/Button/constants' import { glowElement } from 'components/Button/utils' +import { CircularProgress } from 'components/CircularProgress' import { ChevronDown } from 'components/Icons' import useStore from 'store' @@ -120,7 +120,7 @@ const Button = React.forwardRef(function Button( {children && children} {rightIcon && {rightIcon}} {hasSubmenu && ( - + )} diff --git a/src/components/Earn/Lend/LendingMarketsTable.tsx b/src/components/Earn/Lend/LendingMarketsTable.tsx index 75767954..f6dbfe92 100644 --- a/src/components/Earn/Lend/LendingMarketsTable.tsx +++ b/src/components/Earn/Lend/LendingMarketsTable.tsx @@ -1,18 +1,18 @@ import { ColumnDef, Row, Table } from '@tanstack/react-table' -import { useMemo } from 'react' -import Image from 'next/image' import classNames from 'classnames' +import Image from 'next/image' +import { useMemo } from 'react' -import Text from 'components/Text' -import AssetListTable from 'components/MarketAssetTable' -import TitleAndSubCell from 'components/TitleAndSubCell' -import { ChevronDown, ChevronRight } from 'components/Icons' -import { convertLiquidityRateToAPR, demagnify, formatValue } from 'utils/formatters' -import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow' import LendingActionButtons from 'components/Earn/Lend/LendingActionButtons' import LendingDetails from 'components/Earn/Lend/LendingDetails' -import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice' import { FormattedNumber } from 'components/FormattedNumber' +import { ChevronDown, ChevronRight } from 'components/Icons' +import AssetListTable from 'components/MarketAssetTable' +import MarketAssetTableRow from 'components/MarketAssetTable/MarketAssetTableRow' +import Text from 'components/Text' +import TitleAndSubCell from 'components/TitleAndSubCell' +import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice' +import { convertLiquidityRateToAPR, demagnify, formatValue } from 'utils/formatters' interface Props { title: string diff --git a/src/components/Earn/Tab.tsx b/src/components/Earn/Tab.tsx index 338353a2..77f539db 100644 --- a/src/components/Earn/Tab.tsx +++ b/src/components/Earn/Tab.tsx @@ -14,7 +14,7 @@ export default function Tab(props: Props) { const { address, accountId } = useParams() return ( -
+
@@ -10,6 +16,11 @@ interface Props { } export default function VaultExpanded(props: Props) { + const vault = props.row.original as DepositedVault + const { accountId } = useParams() + const [isConfirming, setIsConfirming] = useState(false) + const withdrawFromVaults = useStore((s) => s.withdrawFromVaults) + function enterVaultHandler() { useStore.setState({ vaultModal: { @@ -29,9 +40,79 @@ export default function VaultExpanded(props: Props) { }) } - let isDeposited: boolean = false - if ((props.row.original as DepositedVault)?.amounts) { - isDeposited = true + function unlockHandler() { + useStore.setState({ unlockModal: { vault } }) + } + + async function withdrawHandler() { + if (!accountId) return + const vaults = [props.row.original as DepositedVault] + setIsConfirming(true) + await withdrawFromVaults({ + fee: hardcodedFee, + accountId: accountId, + vaults, + }) + } + + const status = vault.status + + /* BUTTONS */ + + function DepositButton() { + return ( + + ) + } + + function DepositMoreButton() { + return ( + + ) + } + + function UnlockButton() { + return ( + + + + ) + } + + function UnlockingButton() { + return ( + + ) + } + + function UnlockedButton() { + return ( + + ) } return ( @@ -45,30 +126,13 @@ export default function VaultExpanded(props: Props) { !isExpanded && props.row.toggleExpanded() }} > - +
- {isDeposited ? ( - <> - - - - ) : ( - - )} + {!status && } + + {status === VaultStatus.ACTIVE && } + {status === VaultStatus.UNLOCKING && } + {status === VaultStatus.UNLOCKED && }
diff --git a/src/components/Earn/vault/VaultTable.tsx b/src/components/Earn/vault/VaultTable.tsx index 8a25cdc7..a3c191ec 100644 --- a/src/components/Earn/vault/VaultTable.tsx +++ b/src/components/Earn/vault/VaultTable.tsx @@ -10,19 +10,20 @@ import { import classNames from 'classnames' import React from 'react' +import DisplayCurrency from 'components/DisplayCurrency' import VaultExpanded from 'components/Earn/vault/VaultExpanded' import VaultLogo from 'components/Earn/vault/VaultLogo' import { VaultRow } from 'components/Earn/vault/VaultRow' import { ChevronDown, SortAsc, SortDesc, SortNone } from 'components/Icons' +import Loading from 'components/Loading' import Text from 'components/Text' import TitleAndSubCell from 'components/TitleAndSubCell' import { VAULT_DEPOSIT_BUFFER } from 'constants/vaults' -import { getAssetByDenom } from 'utils/assets' -import { convertPercentage, formatPercent, formatValue } from 'utils/formatters' -import DisplayCurrency from 'components/DisplayCurrency' import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' -import Loading from 'components/Loading' +import { VaultStatus } from 'types/enums/vault' +import { getAssetByDenom } from 'utils/assets' +import { convertPercentage, formatPercent, formatValue, produceCountdown } from 'utils/formatters' type Props = { data: Vault[] | DepositedVault[] @@ -40,14 +41,49 @@ export const VaultTable = (props: Props) => { header: 'Vault', accessorKey: 'name', cell: ({ row }) => { + const vault = row.original as DepositedVault + const timeframe = vault.lockup.timeframe[0] + const unlockDuration = !!timeframe ? ` - (${vault.lockup.duration}${timeframe})` : '' + + const status = vault.status + let remainingTime = 0 + + if (status === VaultStatus.UNLOCKING && vault.unlocksAt) { + remainingTime = vault.unlocksAt - Date.now() + } + return (
- + + {status === VaultStatus.UNLOCKING && ( +
+
+
+ + {produceCountdown(remainingTime)} + +
+
+ + Unlocking + +
+
+
+ )} + {status === VaultStatus.UNLOCKED && ( + + Unlocked + + )}
) }, diff --git a/src/components/Earn/vault/VaultUnlockBanner.tsx b/src/components/Earn/vault/VaultUnlockBanner.tsx new file mode 100644 index 00000000..037863ea --- /dev/null +++ b/src/components/Earn/vault/VaultUnlockBanner.tsx @@ -0,0 +1,60 @@ +import { useState } from 'react' +import { useParams } from 'react-router-dom' + +import Button from 'components/Button' +import { ChevronRight } from 'components/Icons' +import NotificationBanner from 'components/NotificationBanner' +import useStore from 'store' +import { hardcodedFee } from 'utils/constants' + +interface Props { + vaults: DepositedVault[] +} + +export default function VaultUnlockBanner(props: Props) { + const { accountId } = useParams() + const [isConfirming, setIsConfirming] = useState(false) + const withdrawFromVaults = useStore((s) => s.withdrawFromVaults) + + async function handleWithdraw() { + if (!accountId) return + if (props.vaults.length > 1) { + useStore.setState({ + withdrawFromVaultsModal: props.vaults, + }) + } else { + setIsConfirming(true) + await withdrawFromVaults({ + fee: hardcodedFee, + accountId: accountId, + vaults: props.vaults, + }) + setIsConfirming(false) + } + } + + if (props.vaults.length === 0) return null + + return ( + } + iconClassName='text-success w-2 h-4' + showProgressIndicator={isConfirming} + /> + } + /> + ) +} diff --git a/src/components/Earn/vault/Vaults.tsx b/src/components/Earn/vault/Vaults.tsx index 5a476a32..66d6d93a 100644 --- a/src/components/Earn/vault/Vaults.tsx +++ b/src/components/Earn/vault/Vaults.tsx @@ -3,10 +3,12 @@ import { useParams } from 'react-router-dom' import Card from 'components/Card' import { VaultTable } from 'components/Earn/vault/VaultTable' +import VaultUnlockBanner from 'components/Earn/vault/VaultUnlockBanner' import { IS_TESTNET } from 'constants/env' import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults' -import useVaults from 'hooks/useVaults' import useDepositedVaults from 'hooks/useDepositedVaults' +import useVaults from 'hooks/useVaults' +import { VaultStatus } from 'types/enums/vault' import { BN } from 'utils/helpers' interface Props { @@ -17,6 +19,7 @@ function Content(props: Props) { const { accountId } = useParams() const { data: vaults } = useVaults() const { data: depositedVaults } = useDepositedVaults(accountId || '') + const isAvailable = props.type === 'available' const vaultsMetaData = IS_TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA @@ -38,31 +41,30 @@ function Content(props: Props) { ) }, [vaults, depositedVaults, vaultsMetaData]) - const vaultsToDisplay = props.type === 'available' ? available : deposited + const vaultsToDisplay = isAvailable ? available : deposited if (!vaultsToDisplay.length) return null - if (props.type === 'deposited') { - return ( - - - - ) + const unlockedVaults: DepositedVault[] = [] + + if (!isAvailable && depositedVaults?.length > 0) { + depositedVaults.forEach((vault) => { + if (vault.status === VaultStatus.UNLOCKED) { + unlockedVaults.push(vault) + } + }) } - return -} - -export default function Vaults(props: Props) { return ( - - : null}> - - - + <> + {!isAvailable && } + + + + ) } @@ -82,16 +84,18 @@ function Fallback() { }, })) - return + return ( + + + + ) } export function AvailableVaults() { return ( - - }> - - - + }> + + ) } diff --git a/src/components/Icons/Account.svg b/src/components/Icons/Account.svg index 7614f453..2a81af69 100644 --- a/src/components/Icons/Account.svg +++ b/src/components/Icons/Account.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/AccountArrowDown.svg b/src/components/Icons/AccountArrowDown.svg new file mode 100644 index 00000000..9a79fc8a --- /dev/null +++ b/src/components/Icons/AccountArrowDown.svg @@ -0,0 +1,8 @@ + + + diff --git a/src/components/Icons/ArrowChartLineUp.svg b/src/components/Icons/ArrowChartLineUp.svg index c1531d66..6255ff09 100644 --- a/src/components/Icons/ArrowChartLineUp.svg +++ b/src/components/Icons/ArrowChartLineUp.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/ArrowCircledTopRight.svg b/src/components/Icons/ArrowCircledTopRight.svg index 37785bc7..321bc736 100644 --- a/src/components/Icons/ArrowCircledTopRight.svg +++ b/src/components/Icons/ArrowCircledTopRight.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/ArrowDownLine.svg b/src/components/Icons/ArrowDownLine.svg index b98d6b5d..a12041cd 100644 --- a/src/components/Icons/ArrowDownLine.svg +++ b/src/components/Icons/ArrowDownLine.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/ArrowRight.svg b/src/components/Icons/ArrowRight.svg index f640ee81..b3432e1c 100644 --- a/src/components/Icons/ArrowRight.svg +++ b/src/components/Icons/ArrowRight.svg @@ -1,3 +1,9 @@ - + diff --git a/src/components/Icons/ArrowUpLine.svg b/src/components/Icons/ArrowUpLine.svg index fdb7fa07..47eca24b 100644 --- a/src/components/Icons/ArrowUpLine.svg +++ b/src/components/Icons/ArrowUpLine.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/Check.svg b/src/components/Icons/Check.svg index 3bb8519d..7978e86e 100644 --- a/src/components/Icons/Check.svg +++ b/src/components/Icons/Check.svg @@ -1,3 +1,3 @@ - - - \ No newline at end of file + + + diff --git a/src/components/Icons/CheckCircled.svg b/src/components/Icons/CheckCircled.svg index 3c315baf..cfc34daf 100644 --- a/src/components/Icons/CheckCircled.svg +++ b/src/components/Icons/CheckCircled.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/ChevronDown.svg b/src/components/Icons/ChevronDown.svg index 963bd902..3e381360 100644 --- a/src/components/Icons/ChevronDown.svg +++ b/src/components/Icons/ChevronDown.svg @@ -1,3 +1,8 @@ - - + + diff --git a/src/components/Icons/ChevronLeft.svg b/src/components/Icons/ChevronLeft.svg index a03ac332..67b85fea 100644 --- a/src/components/Icons/ChevronLeft.svg +++ b/src/components/Icons/ChevronLeft.svg @@ -1,4 +1,7 @@ - + diff --git a/src/components/Icons/ChevronRight.svg b/src/components/Icons/ChevronRight.svg index ecd97cb6..a6b3dbea 100644 --- a/src/components/Icons/ChevronRight.svg +++ b/src/components/Icons/ChevronRight.svg @@ -1,3 +1,8 @@ - - + + diff --git a/src/components/Icons/ChevronUp.svg b/src/components/Icons/ChevronUp.svg index dcba5d72..d83c313b 100644 --- a/src/components/Icons/ChevronUp.svg +++ b/src/components/Icons/ChevronUp.svg @@ -1,4 +1,7 @@ - + diff --git a/src/components/Icons/Copy.svg b/src/components/Icons/Copy.svg index c50e4843..3d8d16e5 100644 --- a/src/components/Icons/Copy.svg +++ b/src/components/Icons/Copy.svg @@ -1,3 +1,5 @@ - - + + diff --git a/src/components/Icons/Cross.svg b/src/components/Icons/Cross.svg index 90ef1bc5..b86749f9 100644 --- a/src/components/Icons/Cross.svg +++ b/src/components/Icons/Cross.svg @@ -1,3 +1,3 @@ - + diff --git a/src/components/Icons/CrossCircled.svg b/src/components/Icons/CrossCircled.svg index f61f68c5..48dbf898 100644 --- a/src/components/Icons/CrossCircled.svg +++ b/src/components/Icons/CrossCircled.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/Enter.svg b/src/components/Icons/Enter.svg index 383ae5cd..0d0715cc 100644 --- a/src/components/Icons/Enter.svg +++ b/src/components/Icons/Enter.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/ExclamationMarkCircled.svg b/src/components/Icons/ExclamationMarkCircled.svg index 76a5d2a1..4c2f43d5 100644 --- a/src/components/Icons/ExclamationMarkCircled.svg +++ b/src/components/Icons/ExclamationMarkCircled.svg @@ -1,3 +1,9 @@ - + diff --git a/src/components/Icons/ExclamationMarkTriangle.svg b/src/components/Icons/ExclamationMarkTriangle.svg index 86cc778f..2dc63872 100644 --- a/src/components/Icons/ExclamationMarkTriangle.svg +++ b/src/components/Icons/ExclamationMarkTriangle.svg @@ -1,3 +1,8 @@ - - + + diff --git a/src/components/Icons/ExternalLink.svg b/src/components/Icons/ExternalLink.svg index 176aacab..c373779b 100644 --- a/src/components/Icons/ExternalLink.svg +++ b/src/components/Icons/ExternalLink.svg @@ -1,5 +1,5 @@ - + - \ No newline at end of file + diff --git a/src/components/Icons/Gear.svg b/src/components/Icons/Gear.svg index 9feb7652..09185026 100644 --- a/src/components/Icons/Gear.svg +++ b/src/components/Icons/Gear.svg @@ -1,6 +1,6 @@ - - - \ No newline at end of file + + + diff --git a/src/components/Icons/Heart.svg b/src/components/Icons/Heart.svg index 841bb31b..2a9484c8 100644 --- a/src/components/Icons/Heart.svg +++ b/src/components/Icons/Heart.svg @@ -1,3 +1,10 @@ - + diff --git a/src/components/Icons/LockLocked.svg b/src/components/Icons/LockLocked.svg new file mode 100644 index 00000000..6d8e3a16 --- /dev/null +++ b/src/components/Icons/LockLocked.svg @@ -0,0 +1,8 @@ + + + diff --git a/src/components/Icons/LockUnlocked.svg b/src/components/Icons/LockUnlocked.svg index 8973c211..73ac2c3a 100644 --- a/src/components/Icons/LockUnlocked.svg +++ b/src/components/Icons/LockUnlocked.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/Logo.svg b/src/components/Icons/Logo.svg index b3fd914f..4d7b53ba 100644 --- a/src/components/Icons/Logo.svg +++ b/src/components/Icons/Logo.svg @@ -1,24 +1,151 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Icons/MarsProtocol.svg b/src/components/Icons/MarsProtocol.svg index 0a99600b..1cb0e438 100644 --- a/src/components/Icons/MarsProtocol.svg +++ b/src/components/Icons/MarsProtocol.svg @@ -1,5 +1,9 @@ - - - - + + + - + - + - + - + - - + - + - + - + C213.8,48,213.7,48.6,213.7,49.3z" + /> + diff --git a/src/components/Icons/Osmo.svg b/src/components/Icons/Osmo.svg index a1935c96..b13415c9 100644 --- a/src/components/Icons/Osmo.svg +++ b/src/components/Icons/Osmo.svg @@ -1,340 +1,340 @@ - + - - - + + + - - - + + + - - - + + + - - - - + + + + - - + + - - - - + + + + - - + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - \ No newline at end of file + diff --git a/src/components/Icons/OverlayMark.svg b/src/components/Icons/OverlayMark.svg index e0718860..1ac464b1 100644 --- a/src/components/Icons/OverlayMark.svg +++ b/src/components/Icons/OverlayMark.svg @@ -1,3 +1,6 @@ - + diff --git a/src/components/Icons/Plus.svg b/src/components/Icons/Plus.svg index a55dc3dd..03503d9a 100644 --- a/src/components/Icons/Plus.svg +++ b/src/components/Icons/Plus.svg @@ -1,3 +1,9 @@ - - + + diff --git a/src/components/Icons/PlusCircled.svg b/src/components/Icons/PlusCircled.svg index 5b646292..72a20fb2 100644 --- a/src/components/Icons/PlusCircled.svg +++ b/src/components/Icons/PlusCircled.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/Questionmark.svg b/src/components/Icons/Questionmark.svg index 17518cb0..de4c88ec 100644 --- a/src/components/Icons/Questionmark.svg +++ b/src/components/Icons/Questionmark.svg @@ -1,4 +1,6 @@ - - - + + + diff --git a/src/components/Icons/Search.svg b/src/components/Icons/Search.svg index 619e5b0f..cba4326d 100644 --- a/src/components/Icons/Search.svg +++ b/src/components/Icons/Search.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/Shield.svg b/src/components/Icons/Shield.svg index f707ab51..56e256ca 100644 --- a/src/components/Icons/Shield.svg +++ b/src/components/Icons/Shield.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/SortAsc.svg b/src/components/Icons/SortAsc.svg index 88925f95..635acb55 100644 --- a/src/components/Icons/SortAsc.svg +++ b/src/components/Icons/SortAsc.svg @@ -1 +1,13 @@ - \ No newline at end of file + + + + diff --git a/src/components/Icons/SortDesc.svg b/src/components/Icons/SortDesc.svg index 8811bbad..26dd3f31 100644 --- a/src/components/Icons/SortDesc.svg +++ b/src/components/Icons/SortDesc.svg @@ -1 +1,13 @@ - \ No newline at end of file + + + + diff --git a/src/components/Icons/SortNone.svg b/src/components/Icons/SortNone.svg index d79c0da7..145492d0 100644 --- a/src/components/Icons/SortNone.svg +++ b/src/components/Icons/SortNone.svg @@ -1 +1,12 @@ - \ No newline at end of file + + + + diff --git a/src/components/Icons/Subtract.svg b/src/components/Icons/Subtract.svg index fa628894..217b83eb 100644 --- a/src/components/Icons/Subtract.svg +++ b/src/components/Icons/Subtract.svg @@ -1,7 +1,7 @@ - - + + diff --git a/src/components/Icons/TrashBin.svg b/src/components/Icons/TrashBin.svg index 0e1fbc31..9b8b3ea5 100644 --- a/src/components/Icons/TrashBin.svg +++ b/src/components/Icons/TrashBin.svg @@ -1,3 +1,8 @@ - + diff --git a/src/components/Icons/Wallet.svg b/src/components/Icons/Wallet.svg index 03993a52..1aa1671e 100644 --- a/src/components/Icons/Wallet.svg +++ b/src/components/Icons/Wallet.svg @@ -1,5 +1,5 @@ - + diff --git a/src/components/Icons/index.ts b/src/components/Icons/index.ts index dae48f31..7c3880f9 100644 --- a/src/components/Icons/index.ts +++ b/src/components/Icons/index.ts @@ -1,5 +1,6 @@ // @index(['./*.svg'], f => `export { default as ${f.name} } from 'components/Icons/${f.name}.svg'`) export { default as Account } from 'components/Icons/Account.svg' +export { default as AccountArrowDown } from 'components/Icons/AccountArrowDown.svg' export { default as ArrowChartLineUp } from 'components/Icons/ArrowChartLineUp.svg' export { default as ArrowCircledTopRight } from 'components/Icons/ArrowCircledTopRight.svg' export { default as ArrowDownLine } from 'components/Icons/ArrowDownLine.svg' @@ -20,6 +21,7 @@ export { default as ExclamationMarkTriangle } from 'components/Icons/Exclamation export { default as ExternalLink } from 'components/Icons/ExternalLink.svg' export { default as Gear } from 'components/Icons/Gear.svg' export { default as Heart } from 'components/Icons/Heart.svg' +export { default as LockLocked } from 'components/Icons/LockLocked.svg' export { default as LockUnlocked } from 'components/Icons/LockUnlocked.svg' export { default as Logo } from 'components/Icons/Logo.svg' export { default as MarsProtocol } from 'components/Icons/MarsProtocol.svg' diff --git a/src/components/MarketAssetTable/index.tsx b/src/components/MarketAssetTable/index.tsx index 9549d0a8..2d795ff1 100644 --- a/src/components/MarketAssetTable/index.tsx +++ b/src/components/MarketAssetTable/index.tsx @@ -11,20 +11,21 @@ import { import classNames from 'classnames' import React from 'react' +import Card from 'components/Card' import { SortAsc, SortDesc, SortNone } from 'components/Icons' import Text from 'components/Text' -import Card from 'components/Card' interface Props { title: string data: TData[] columns: ColumnDef[] + sorting?: SortingState rowRenderer: (row: Row, table: Table) => JSX.Element } function AssetListTable(props: Props) { const { title, data, columns } = props - const [sorting, setSorting] = React.useState([]) + const [sorting, setSorting] = React.useState(props.sorting ?? []) const table = useReactTable({ data, diff --git a/src/components/Modal.tsx b/src/components/Modal.tsx index 46742eb6..d19c35f4 100644 --- a/src/components/Modal.tsx +++ b/src/components/Modal.tsx @@ -20,6 +20,7 @@ interface Props { export default function Modal(props: Props) { const ref: any = useRef(null) + const modalClassName = props.modalClassName ?? 'max-w-modal' function onClose() { ref.current?.close() @@ -48,10 +49,10 @@ export default function Modal(props: Props) { ref={ref} onCancel={onClose} className={classNames( - 'w-[895px] border-none bg-transparent text-white', + `w-screen border-none bg-transparent text-white`, 'focus-visible:outline-none', 'backdrop:bg-black/50 backdrop:backdrop-blur-sm', - props.modalClassName, + modalClassName, )} > Add Assets} onClose={onClose} - modalClassName='max-w-[478px]' + modalClassName='max-w-modal-xs' headerClassName='bg-white/10 border-b-white/5 border-b items-center p-4' > {showContent ? ( diff --git a/src/components/Modals/AssetAmountSelectActionModal/index.tsx b/src/components/Modals/AssetAmountSelectActionModal/index.tsx index 3434fa80..2ffa69d1 100644 --- a/src/components/Modals/AssetAmountSelectActionModal/index.tsx +++ b/src/components/Modals/AssetAmountSelectActionModal/index.tsx @@ -1,5 +1,7 @@ import { useCallback, useState } from 'react' +import CurrentAccountSummary from 'components/Account/CurrentAccountSummary' +import AssetImage from 'components/AssetImage' import Button from 'components/Button' import Card from 'components/Card' import Divider from 'components/Divider' @@ -7,10 +9,8 @@ import { ArrowRight } from 'components/Icons' import Modal from 'components/Modal' import Text from 'components/Text' import TokenInputWithSlider from 'components/TokenInputWithSlider' -import { BN } from 'utils/helpers' import { byDenom } from 'utils/array' -import CurrentAccountSummary from 'components/Account/CurrentAccountSummary' -import AssetImage from 'components/AssetImage' +import { BN } from 'utils/helpers' interface Props { asset: Asset diff --git a/src/components/Modals/Borrow/BorrowModal.tsx b/src/components/Modals/Borrow/BorrowModal.tsx index 55f22cbc..7ebdb53a 100644 --- a/src/components/Modals/Borrow/BorrowModal.tsx +++ b/src/components/Modals/Borrow/BorrowModal.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react' import AccountSummary from 'components/Account/AccountSummary' +import AssetImage from 'components/AssetImage' import Button from 'components/Button' import Card from 'components/Card' import Divider from 'components/Divider' @@ -14,11 +15,10 @@ import { ASSETS } from 'constants/assets' import useCurrentAccount from 'hooks/useCurrentAccount' import useToggle from 'hooks/useToggle' import useStore from 'store' +import { BNCoin } from 'types/classes/BNCoin' import { hardcodedFee } from 'utils/constants' import { formatPercent, formatValue } from 'utils/formatters' import { BN } from 'utils/helpers' -import AssetImage from 'components/AssetImage' -import { BNCoin } from 'types/classes/BNCoin' function getDebtAmount(modal: BorrowModal | null) { if (!(modal?.marketData as BorrowAssetActive)?.debt) return '0' diff --git a/src/components/Modals/ModalsContainer.tsx b/src/components/Modals/ModalsContainer.tsx index 5e4f4c63..7e983d77 100644 --- a/src/components/Modals/ModalsContainer.tsx +++ b/src/components/Modals/ModalsContainer.tsx @@ -1,9 +1,12 @@ -import VaultModal from 'components/Modals/Vault' -import BorrowModal from 'components/Modals/Borrow/BorrowModal' -import FundAndWithdrawModal from 'components/Modals/FundWithdraw/FundAndWithdrawModal' -import AddVaultBorrowAssetsModal from 'components/Modals/AddVaultAssets/AddVaultBorrowAssetsModal' -import UnlockModal from 'components/Modals/Unlock/UnlockModal' -import LendAndReclaimModalController from 'components/Modals/LendAndReclaim' +import { + AddVaultBorrowAssetsModal, + BorrowModal, + FundAndWithdrawModal, + LendAndReclaimModalController, + UnlockModal, + VaultModal, + WithdrawFromVaults, +} from 'components/Modals' export default function ModalsContainer() { return ( @@ -14,6 +17,7 @@ export default function ModalsContainer() { + ) } diff --git a/src/components/Modals/Unlock/UnlockModal.tsx b/src/components/Modals/Unlock/UnlockModal.tsx index fe8333a6..92452b8f 100644 --- a/src/components/Modals/Unlock/UnlockModal.tsx +++ b/src/components/Modals/Unlock/UnlockModal.tsx @@ -1,8 +1,8 @@ +import { CircularProgress } from 'components/CircularProgress' import { LockUnlocked } from 'components/Icons' import Modal from 'components/Modal' -import useStore from 'store' import UnlockModalContent from 'components/Modals/Unlock/UnlockModalContent' -import { CircularProgress } from 'components/CircularProgress' +import useStore from 'store' export default function UnlockModal() { const modal = useStore((s) => s.unlockModal) @@ -20,7 +20,7 @@ export default function UnlockModal() {
} - modalClassName='w-[577px]' + modalClassName='max-w-modal-sm' headerClassName='p-8' contentClassName='px-8 pb-8' hideCloseBtn diff --git a/src/components/Modals/Unlock/UnlockModalContent.tsx b/src/components/Modals/Unlock/UnlockModalContent.tsx index e0afde09..b552afc5 100644 --- a/src/components/Modals/Unlock/UnlockModalContent.tsx +++ b/src/components/Modals/Unlock/UnlockModalContent.tsx @@ -1,8 +1,11 @@ -import { hardcodedFee } from 'utils/constants' +import { useState } from 'react' +import { useParams } from 'react-router-dom' + import Button from 'components/Button' import { Enter } from 'components/Icons' import Text from 'components/Text' import useStore from 'store' +import { hardcodedFee } from 'utils/constants' interface Props { depositedVault: DepositedVault @@ -27,10 +30,15 @@ function YesIcon() { export default function UnlockModalContent(props: Props) { const unlock = useStore((s) => s.unlock) + const [isWating, setIsConfirming] = useState(false) + const { accountId } = useParams() async function onConfirm() { + if (!accountId) return + setIsConfirming(true) await unlock({ fee: hardcodedFee, + accountId: accountId, vault: props.depositedVault, amount: props.depositedVault.amounts.locked.toString(), }) @@ -50,6 +58,7 @@ export default function UnlockModalContent(props: Props) { className='px-6' rightIcon={} onClick={onConfirm} + showProgressIndicator={isWating} />
diff --git a/src/components/Modals/Vault/index.tsx b/src/components/Modals/Vault/index.tsx index dbe26df6..15151494 100644 --- a/src/components/Modals/Vault/index.tsx +++ b/src/components/Modals/Vault/index.tsx @@ -1,10 +1,10 @@ import VaultLogo from 'components/Earn/vault/VaultLogo' import Modal from 'components/Modal' +import VaultModalContent from 'components/Modals/Vault/VaultModalContent' import Text from 'components/Text' import { ASSETS } from 'constants/assets' import useCurrentAccount from 'hooks/useCurrentAccount' import useStore from 'store' -import VaultModalContent from 'components/Modals/Vault/VaultModalContent' export default function VaultModalController() { const currentAccount = useCurrentAccount() @@ -43,7 +43,7 @@ function VaultModal(props: Props) { header={ - {`${props.modal.vault.symbols.primary} - ${props.modal.vault.symbols.secondary}`} + {props.modal.vault.name} } headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b' diff --git a/src/components/Modals/WithdrawFromVaults/WithdrawFromVaults.tsx b/src/components/Modals/WithdrawFromVaults/WithdrawFromVaults.tsx new file mode 100644 index 00000000..17dbfdb0 --- /dev/null +++ b/src/components/Modals/WithdrawFromVaults/WithdrawFromVaults.tsx @@ -0,0 +1,100 @@ +import { useState } from 'react' +import { useParams } from 'react-router-dom' + +import Button from 'components/Button' +import { CircularProgress } from 'components/CircularProgress' +import DisplayCurrency from 'components/DisplayCurrency' +import VaultLogo from 'components/Earn/vault/VaultLogo' +import { FormattedNumber } from 'components/FormattedNumber' +import Modal from 'components/Modal' +import Text from 'components/Text' +import useStore from 'store' +import { BNCoin } from 'types/classes/BNCoin' +import { getAssetByDenom } from 'utils/assets' +import { hardcodedFee } from 'utils/constants' +import { demagnify } from 'utils/formatters' +import { BN } from 'utils/helpers' + +export default function WithdrawFromVaults() { + const modal = useStore((s) => s.withdrawFromVaultsModal) + const { accountId } = useParams() + const [isConfirming, setIsConfirming] = useState(false) + const withdrawFromVaults = useStore((s) => s.withdrawFromVaults) + const baseCurrency = useStore((s) => s.baseCurrency) + + function onClose() { + useStore.setState({ withdrawFromVaultsModal: null }) + } + + async function withdrawHandler() { + if (!accountId || !modal) return + setIsConfirming(true) + await withdrawFromVaults({ + fee: hardcodedFee, + accountId: accountId, + vaults: modal, + }) + setIsConfirming(false) + onClose() + } + + return ( + + Unlocked Vaults + + } + modalClassName='max-w-modal-xs' + headerClassName='px-4 py-5.5 border-b-white/5 border-b' + contentClassName='p-4' + > + {modal ? ( +
+ {modal.map((vault) => { + const positionValue = vault.values.primary.plus(vault.values.secondary) + const coin = BNCoin.fromDenomAndBigNumber(baseCurrency.denom, positionValue) + const primaryAsset = getAssetByDenom(vault.denoms.primary) + const secondaryAsset = getAssetByDenom(vault.denoms.secondary) + + if (!primaryAsset || !secondaryAsset) return null + return ( +
+ +
+ {vault.name} + + Unlocked + +
+
+ + + +
+
+ ) + })} +
+ ) : ( + + )} +
+ ) +} diff --git a/src/components/Modals/index.tsx b/src/components/Modals/index.tsx new file mode 100644 index 00000000..cb57c53e --- /dev/null +++ b/src/components/Modals/index.tsx @@ -0,0 +1,7 @@ +export { default as AddVaultBorrowAssetsModal } from 'components/Modals/AddVaultAssets/AddVaultBorrowAssetsModal' +export { default as BorrowModal } from 'components/Modals/Borrow/BorrowModal' +export { default as FundAndWithdrawModal } from 'components/Modals/FundWithdraw/FundAndWithdrawModal' +export { default as LendAndReclaimModalController } from 'components/Modals/LendAndReclaim' +export { default as UnlockModal } from 'components/Modals/Unlock/UnlockModal' +export { default as VaultModal } from 'components/Modals/Vault' +export { default as WithdrawFromVaults } from 'components/Modals/WithdrawFromVaults/WithdrawFromVaults' diff --git a/src/components/NotificationBanner.tsx b/src/components/NotificationBanner.tsx new file mode 100644 index 00000000..62873eda --- /dev/null +++ b/src/components/NotificationBanner.tsx @@ -0,0 +1,58 @@ +import classNames from 'classnames' +import { useMemo } from 'react' + +import { CheckCircled, CrossCircled, ExclamationMarkTriangle } from 'components/Icons' +import Text from 'components/Text' + +interface Props { + type: 'success' | 'error' | 'warning' | 'info' + text: string + button: React.ReactNode +} + +export default function NotificationBanner(props: Props) { + const [glasColor, bgColor, icon] = useMemo(() => { + if (props.type === 'success') + return [ + 'bg-success-bg/20', + 'bg-success', + , + ] + if (props.type === 'error') + return [ + 'bg-error-bg/20', + 'bg-error', + , + ] + if (props.type === 'warning') + return [ + 'bg-warning-bg/20', + 'bg-warning', + , + ] + + return [ + 'bg-info-bg/20', + 'bg-info', + , + ] + }, [props.type]) + + return ( +
+
+
{icon}
+ + {props.text} + + {props.button} +
+
+ ) +} diff --git a/src/components/Overlay.tsx b/src/components/Overlay.tsx index c295f346..d7e1b8a5 100644 --- a/src/components/Overlay.tsx +++ b/src/components/Overlay.tsx @@ -21,7 +21,7 @@ export default function Overlay(props: Props) { className={classNames( 'max-w-screen absolute isolate z-50 rounded-sm shadow-overlay backdrop-blur-lg', props.hasBackdropIsolation ? 'bg-body' : 'gradient-popover', - 'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-sm before:p-[1px] before:border-glas', + 'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas', props.className, )} > diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index afff4d54..6afd05f3 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -19,8 +19,8 @@ const SearchBar = (props: Props) => {
diff --git a/src/components/Trade/TradeModule/AssetSelector/AssetList.tsx b/src/components/Trade/TradeModule/AssetSelector/AssetList.tsx index e03c6f03..d8d414ce 100644 --- a/src/components/Trade/TradeModule/AssetSelector/AssetList.tsx +++ b/src/components/Trade/TradeModule/AssetSelector/AssetList.tsx @@ -20,7 +20,7 @@ export default function AssetList(props: Props) { onClick={props.toggleOpen} > {props.type === 'buy' ? 'Buy asset' : 'Sell asset'} - + {props.isOpen && (props.assets.length === 0 ? ( diff --git a/src/components/Trade/TradeModule/AssetSelector/AssetOverlay.tsx b/src/components/Trade/TradeModule/AssetSelector/AssetOverlay.tsx index 3fef650d..db2b4517 100644 --- a/src/components/Trade/TradeModule/AssetSelector/AssetOverlay.tsx +++ b/src/components/Trade/TradeModule/AssetSelector/AssetOverlay.tsx @@ -1,4 +1,4 @@ -import { useCallback, useMemo, useRef } from 'react' +import { useCallback, useMemo } from 'react' import EscButton from 'components/Button/EscButton' import Divider from 'components/Divider' @@ -52,7 +52,7 @@ export default function AssetOverlay(props: Props) { return ( -
+
Select asset
diff --git a/src/constants/vaults.ts b/src/constants/vaults.ts index fbb667b0..edf5263f 100644 --- a/src/constants/vaults.ts +++ b/src/constants/vaults.ts @@ -16,7 +16,7 @@ export const TESTNET_VAULTS_META_DATA: VaultMetaData[] = [ }, symbols: { primary: 'OSMO', - secondary: 'OSMO-USDC.n', + secondary: 'USDC.n', }, isFeatured: true, }, @@ -35,7 +35,7 @@ export const TESTNET_VAULTS_META_DATA: VaultMetaData[] = [ }, symbols: { primary: 'OSMO', - secondary: 'OSMO-USDC.n', + secondary: 'USDC.n', }, isFeatured: true, }, @@ -54,7 +54,7 @@ export const TESTNET_VAULTS_META_DATA: VaultMetaData[] = [ }, symbols: { primary: 'OSMO', - secondary: 'OSMO-USDC.n', + secondary: 'USDC.n', }, isFeatured: true, }, diff --git a/src/pages/FarmPage.tsx b/src/pages/FarmPage.tsx index e01abaa4..e0ba5cf3 100644 --- a/src/pages/FarmPage.tsx +++ b/src/pages/FarmPage.tsx @@ -3,11 +3,11 @@ import { AvailableVaults, DepositedVaults } from 'components/Earn/vault/Vaults' export default function FarmPage() { return ( - <> +
{/* */} - +
) } diff --git a/src/pages/LendPage.tsx b/src/pages/LendPage.tsx index 97fc4df1..556bdb89 100644 --- a/src/pages/LendPage.tsx +++ b/src/pages/LendPage.tsx @@ -1,15 +1,15 @@ -import Tab from 'components/Earn/Tab' import LendingMarketsTable from 'components/Earn/Lend/LendingMarketsTable' +import Tab from 'components/Earn/Tab' import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData' export default function LendPage() { const { accountLentAssets, availableAssets } = useLendingMarketAssetsTableData() return ( - <> +
- +
) } diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx index 9c993c10..a621104b 100644 --- a/src/pages/_layout.tsx +++ b/src/pages/_layout.tsx @@ -22,7 +22,7 @@ export default function Layout({ children }: { children: React.ReactNode }) { className={classNames( 'lg:h-[calc(100vh-89px)]', 'p-6 lg:mt-[65px]', - 'align-items-center grid h-full grid-cols-[auto_min-content] place-items-start gap-6', + 'align-items-center grid h-full min-h-[900px] grid-cols-[auto_min-content] place-items-start gap-6', )} > {isFullWidth ? children :
{children}
} diff --git a/src/store/slices/broadcast.ts b/src/store/slices/broadcast.ts index 194a3977..77e418e5 100644 --- a/src/store/slices/broadcast.ts +++ b/src/store/slices/broadcast.ts @@ -4,15 +4,15 @@ import { GetState, SetState } from 'zustand' import { ENV } from 'constants/env' import { Store } from 'store' -import { getSingleValueFromBroadcastResult } from 'utils/broadcast' -import { formatAmountWithSymbol } from 'utils/formatters' -import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import { BNCoin } from 'types/classes/BNCoin' +import { ExecuteMsg as AccountNftExecuteMsg } from 'types/generated/mars-account-nft/MarsAccountNft.types' import { + Action, Action as CreditManagerAction, ExecuteMsg as CreditManagerExecuteMsg, } from 'types/generated/mars-credit-manager/MarsCreditManager.types' -import { ExecuteMsg as AccountNftExecuteMsg } from 'types/generated/mars-account-nft/MarsAccountNft.types' +import { getSingleValueFromBroadcastResult } from 'utils/broadcast' +import { formatAmountWithSymbol } from 'utils/formatters' export default function createBroadcastSlice( set: SetState, @@ -116,11 +116,23 @@ export default function createBroadcastSlice( ) return !!response.result }, - unlock: async (options: { fee: StdFee; vault: Vault; amount: string }) => { - const msg: CreditManagerAction = { - request_vault_unlock: { - vault: { address: options.vault.address }, - amount: options.amount, + unlock: async (options: { + fee: StdFee + accountId: string + vault: DepositedVault + amount: string + }) => { + const msg: CreditManagerExecuteMsg = { + update_credit_account: { + account_id: options.accountId, + actions: [ + { + request_vault_unlock: { + vault: { address: options.vault.address }, + amount: options.amount, + }, + }, + ], }, } @@ -133,6 +145,37 @@ export default function createBroadcastSlice( handleResponseMessages(response, `Requested unlock for ${options.vault.name}`) return !!response.result }, + + withdrawFromVaults: async (options: { + fee: StdFee + accountId: string + vaults: DepositedVault[] + }) => { + const actions: CreditManagerAction[] = [] + options.vaults.forEach((vault) => { + if (vault.unlockId) + actions.push({ + exit_vault_unlocked: { + id: vault.unlockId, + vault: { address: vault.address }, + }, + }) + }) + const msg: CreditManagerExecuteMsg = { + update_credit_account: { + account_id: options.accountId, + actions, + }, + } + + const response = await get().executeMsg({ msg, fee: options.fee }) + const vaultsString = options.vaults.length === 1 ? 'vault' : 'vaults' + handleResponseMessages( + response, + `You successfully withdrew ${options.vaults.length} unlocked ${vaultsString} to your account`, + ) + return !!response.result + }, depositIntoVault: async (options: { fee: StdFee; accountId: string; actions: Action[] }) => { const msg: CreditManagerExecuteMsg = { update_credit_account: { diff --git a/src/store/slices/modal.ts b/src/store/slices/modal.ts index c2015eb9..d242d077 100644 --- a/src/store/slices/modal.ts +++ b/src/store/slices/modal.ts @@ -11,5 +11,6 @@ export default function createModalSlice(set: SetState, get: GetStat unlockModal: null, lendAndReclaimModal: null, vaultModal: null, + withdrawFromVaultsModal: null, } } diff --git a/src/types/enums/vault.ts b/src/types/enums/vault.ts new file mode 100644 index 00000000..ff85581f --- /dev/null +++ b/src/types/enums/vault.ts @@ -0,0 +1,5 @@ +export enum VaultStatus { + ACTIVE = 'active', + UNLOCKING = 'unlocking', + UNLOCKED = 'unlocked', +} diff --git a/src/types/interfaces/store/broadcast.d.ts b/src/types/interfaces/store/broadcast.d.ts index 54221a6b..1be32a63 100644 --- a/src/types/interfaces/store/broadcast.d.ts +++ b/src/types/interfaces/store/broadcast.d.ts @@ -16,7 +16,17 @@ interface BroadcastSlice { createAccount: (options: { fee: StdFee }) => Promise deleteAccount: (options: { fee: StdFee; accountId: string }) => Promise deposit: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise - unlock: (options: { fee: StdFee; vault: Vault; amount: string }) => Promise + unlock: (options: { + fee: StdFee + accountId: string + vault: DepositedVault + amount: string + }) => Promise + withdrawFromVaults: (options: { + fee: StdFee + accountId: string + vaults: DepositedVault[] + }) => Promise depositIntoVault: (options: { fee: StdFee accountId: string diff --git a/src/types/interfaces/store/modals.d.ts b/src/types/interfaces/store/modals.d.ts index 1caa6528..5d36fb86 100644 --- a/src/types/interfaces/store/modals.d.ts +++ b/src/types/interfaces/store/modals.d.ts @@ -6,6 +6,7 @@ interface ModalSlice { fundAccountModal: boolean fundAndWithdrawModal: 'fund' | 'withdraw' | null vaultModal: VaultModal | null + withdrawFromVaultsModal: DepositedVault[] | null unlockModal: UnlockModal | null lendAndReclaimModal: LendAndReclaimModalConfig | null } diff --git a/src/types/interfaces/vaults.d.ts b/src/types/interfaces/vaults.d.ts index 6dc4e884..5f01ad81 100644 --- a/src/types/interfaces/vaults.d.ts +++ b/src/types/interfaces/vaults.d.ts @@ -55,6 +55,7 @@ interface VaultValuesAndAmounts { type VaultStatus = 'active' | 'unlocking' | 'unlocked' interface DepositedVault extends Vault, VaultValuesAndAmounts { status: VaultStatus + unlockId?: number unlocksAt?: number } diff --git a/src/utils/formatters.ts b/src/utils/formatters.ts index 1bf7df5e..c8d92e7d 100644 --- a/src/utils/formatters.ts +++ b/src/utils/formatters.ts @@ -1,4 +1,5 @@ import BigNumber from 'bignumber.js' +import moment from 'moment' import { BNCoin } from 'types/classes/BNCoin' import { getEnabledMarketAssets } from 'utils/assets' @@ -23,6 +24,19 @@ export interface FormatOptions { abbreviated?: boolean } +export const produceCountdown = (remainingTime: number) => { + const duration = moment.duration(remainingTime, 'milliseconds') + const days = formatValue(duration.asDays(), { minDecimals: 0, maxDecimals: 0 }) + + duration.subtract(days, 'days') + const hours = formatValue(duration.asHours(), { minDecimals: 0, maxDecimals: 0 }) + + duration.subtract(hours, 'hours') + const minutes = formatValue(duration.asMinutes(), { minDecimals: 0, maxDecimals: 0 }) + + return `${days}d ${hours}h ${minutes}m` +} + export const formatValue = (amount: number | string, options?: FormatOptions): string => { let numberOfZeroDecimals: number | null = null const minDecimals = options?.minDecimals ?? 2 diff --git a/src/utils/vaults.ts b/src/utils/vaults.ts index b958fe35..8548e55c 100644 --- a/src/utils/vaults.ts +++ b/src/utils/vaults.ts @@ -1,9 +1,12 @@ +import moment from 'moment' + import { IS_TESTNET } from 'constants/env' import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults' -import { BN } from 'utils/helpers' -import { getNetCollateralValue } from 'utils/accounts' import { BNCoin } from 'types/classes/BNCoin' +import { VaultStatus } from 'types/enums/vault' import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types' +import { getNetCollateralValue } from 'utils/accounts' +import { BN } from 'utils/helpers' import { getTokenPrice, getTokenValue } from 'utils/tokens' export function getVaultMetaData(address: string) { diff --git a/tailwind.config.js b/tailwind.config.js index 005ad064..f8caea9b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,12 +1,27 @@ /** @type {import('tailwindcss').Config} */ const plugin = require('tailwindcss/plugin') +const flipClass = plugin(function ({ addUtilities }) { + addUtilities({ + '.flip-x-180': { + transform: 'rotateX(180deg)', + }, + '.preserve-3d': { + transformStyle: 'preserve-3d', + }, + '.perspective': { + perspective: '1000px', + }, + '.backface-hidden': { + backfaceVisibility: 'hidden', + }, + }) +}) + module.exports = { content: ['./src/pages/**/*.{js,ts,jsx,tsx}', './src/components/**/*.{js,ts,jsx,tsx}'], safelist: [ 'h-2', - 'h-4.5', - 'h-15', 'text-3xs', 'text-3xs-caps', 'text-2xs', @@ -30,8 +45,6 @@ module.exports = { 'text-5xl-caps', 'text-5xl', 'w-2', - 'w-4.5', - 'w-15', ], theme: { extend: { @@ -39,6 +52,7 @@ module.exports = { fadein: 'fadein 1s ease-in-out forwards', glow: 'glow 1000ms ease-in-out forwards', progress: 'spin 1200ms cubic-bezier(0.5, 0, 0.5, 1) infinite', + flip: 'flip 10s linear alternate-reverse infinite', }, backdropBlur: { sticky: '50px', @@ -80,12 +94,15 @@ module.exports = { 'body-dark': '#141621', error: '#F04438', 'error-bg': '#FDA29B', + green: '#039855', grey: '#3a3c49', 'grey-dark': '#1a1c25', 'grey-highlight': '#4c4c4c', 'grey-light': '#bfbfbf', 'grey-medium': '#5f697a', header: 'rgba(59, 25, 40, 0.4);', + info: '#FDB022', + 'info-bg': '#FEDB7C', input: '#282a33', loss: '#f96363', mars: '#a03b45', @@ -101,6 +118,7 @@ module.exports = { 'success-bg': '#6CE9A6', 'vote-against': '#eb9e49', warning: '#F79009', + 'warning-bg': '#FEC84B', white: '#FFF', }, fontFamily: { @@ -148,6 +166,12 @@ module.exports = { '66%': { opacity: 1 }, '100%': { opacity: 0 }, }, + flip: { + '0%': { transform: 'rotateX(0deg)' }, + '45%': { transform: 'rotateX(0deg)' }, + '55%': { transform: 'rotateX(180deg)' }, + '100%': { transform: 'rotateX(180deg)' }, + }, }, letterSpacing: { normal: 0, @@ -164,10 +188,16 @@ module.exports = { }, maxWidth: { content: '1024px', + modal: '895px', + 'modal-sm': '517px', + 'modal-xs': '442px' }, minWidth: { 15: '60px', }, + padding: { + 5.5: '22px', + }, screens: { sm: '480px', md: '720px', @@ -202,6 +232,7 @@ module.exports = { }, }, plugins: [ + flipClass, require('tailwind-scrollbar-hide'), plugin(function ({ addBase, addUtilities, theme }) { addBase({