diff --git a/apps/marginfi-v2-xnft/App.tsx b/apps/marginfi-v2-xnft/App.tsx index 8b3921580b..d0f0da9d87 100644 --- a/apps/marginfi-v2-xnft/App.tsx +++ b/apps/marginfi-v2-xnft/App.tsx @@ -16,6 +16,7 @@ import { SwapContextProvider } from "~/context"; import { useConnection } from "~/hooks/useConnection"; import { useWallet } from "~/hooks/useWallet"; import { ROUTE_CACHE_DURATION } from "~/consts"; +import { PieChartIcon, ReceiveMoneyIcon, TokenSwapIcon } from "~/assets/icons"; require("~/styles/globals.css"); require("~/styles/fonts.css"); @@ -82,7 +83,7 @@ function TabNavigator() { options={{ header: (props: BottomTabHeaderProps) => , tabBarLabel: "Lend", - tabBarIcon: ({ color, size }) => , + tabBarIcon: ({ color, size }) => , }} /> , tabBarLabel: "Swap", - tabBarIcon: ({ color, size }) => , + tabBarIcon: ({ color, size }) => , }} /> , tabBarLabel: "Portfolio", - tabBarIcon: ({ color, size }) => , + tabBarIcon: ({ color, size }) => , }} /> diff --git a/apps/marginfi-v2-xnft/src/assets/icons/PieChartIcon.tsx b/apps/marginfi-v2-xnft/src/assets/icons/PieChartIcon.tsx new file mode 100644 index 0000000000..7d3c16a0e3 --- /dev/null +++ b/apps/marginfi-v2-xnft/src/assets/icons/PieChartIcon.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +interface PieChartIconProps extends React.SVGAttributes { + color?: string; +} + +export const PieChartIcon: React.FC = ({ width = "24", height = "24", color = "white" }) => { + return ( + + + + ); +}; diff --git a/apps/marginfi-v2-xnft/src/assets/icons/ReceiveMoneyIcon.tsx b/apps/marginfi-v2-xnft/src/assets/icons/ReceiveMoneyIcon.tsx new file mode 100644 index 0000000000..763a18e82a --- /dev/null +++ b/apps/marginfi-v2-xnft/src/assets/icons/ReceiveMoneyIcon.tsx @@ -0,0 +1,19 @@ +import React from "react"; + +interface ReceiveMoneyIconProps extends React.SVGAttributes { + color?: string; +} + +export const ReceiveMoneyIcon: React.FC = ({ width = "24", height = "24", color = "black" }) => { + return ( + + + + + + ); +}; diff --git a/apps/marginfi-v2-xnft/src/assets/icons/TokenSwapIcon.tsx b/apps/marginfi-v2-xnft/src/assets/icons/TokenSwapIcon.tsx new file mode 100644 index 0000000000..c83464147a --- /dev/null +++ b/apps/marginfi-v2-xnft/src/assets/icons/TokenSwapIcon.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +interface TokenSwapIconProps extends React.SVGAttributes { + color?: string; +} + +export const TokenSwapIcon: React.FC = ({ width = "24", height = "24", color = "black" }) => { + return ( + + + + ); +}; diff --git a/apps/marginfi-v2-xnft/src/assets/icons/chevron-down.svg b/apps/marginfi-v2-xnft/src/assets/icons/chevron-down.svg deleted file mode 100644 index 1b95cdaf65..0000000000 --- a/apps/marginfi-v2-xnft/src/assets/icons/chevron-down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/apps/marginfi-v2-xnft/src/assets/icons/index.ts b/apps/marginfi-v2-xnft/src/assets/icons/index.ts index 36e9e1618e..abd3687121 100644 --- a/apps/marginfi-v2-xnft/src/assets/icons/index.ts +++ b/apps/marginfi-v2-xnft/src/assets/icons/index.ts @@ -11,3 +11,6 @@ export * from "./RefreshIcon"; export * from "./SettingsIcon"; export * from "./CloseIcon"; export * from "./ErrorIcon"; +export * from "./PieChartIcon"; +export * from "./ReceiveMoneyIcon"; +export * from "./TokenSwapIcon"; diff --git a/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCard.tsx b/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCard.tsx index 69b6e95bfc..88a72cff52 100644 --- a/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCard.tsx +++ b/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCard.tsx @@ -7,7 +7,7 @@ import { MarginfiAccountWrapper, MarginfiClient } from "@mrgnlabs/marginfi-clien import { PoolCardPosition } from "./PoolCardPosition"; import { useConnection } from "~/hooks/useConnection"; import { ActionType, Emissions, ExtendedBankInfo, FEE_MARGIN, getCurrentAction } from "@mrgnlabs/marginfi-v2-ui-state"; -import { showErrorToast } from "~/utils"; +import { showErrorToast, showSuccessToast } from "~/utils"; import { percentFormatter, usdFormatter } from "@mrgnlabs/mrgn-common"; type Props = { @@ -120,6 +120,8 @@ export function PoolCard({ const withdrawAll = bankInfo.isActive ? borrowOrLendAmount === bankInfo.position.amount : false; await _marginfiAccount.withdraw(borrowOrLendAmount, bankInfo.address, withdrawAll); } + + showSuccessToast(`${currentAction + "ing"} ${borrowOrLendAmount} ${bankInfo.meta.tokenSymbol} 👍`); } catch (error: any) { console.log(`Error while ${currentAction + "ing"}`); console.log(error); @@ -136,8 +138,46 @@ export function PoolCard({ [bankInfo, connection, currentAction, marginfiAccount, marginfiClient, nativeSolBalance, reloadBanks] ); + const closeBalance = useCallback(async () => { + if (!marginfiAccount) { + showErrorToast("marginfi account not ready."); + return; + } + + if (!bankInfo.isActive) { + showErrorToast("no position to close."); + return; + } + + try { + if (bankInfo.position.isLending) { + await marginfiAccount.withdraw(0, bankInfo.address, true); + } else { + await marginfiAccount.repay(0, bankInfo.address, true); + } + showSuccessToast("Closing 👍"); + } catch (error: any) { + showSuccessToast(`Error while closing balance: ${error.message}`); + console.log(`Error while closing balance`); + console.log(error); + } + + // TODO: set values back to 0 + try { + await reloadBanks(); + } catch (error: any) { + console.log("Error while reloading state"); + console.log(error); + } + }, [bankInfo, marginfiAccount, reloadBanks]); + return ( - + {bankInfo.meta.tokenSymbol} @@ -163,7 +203,7 @@ export function PoolCard({ currentAction={currentAction} isBankFilled={isInLendingMode ? depositFilled >= 0.9999 : borrowFilled >= 0.9999} bank={bankInfo} - onAction={(amount) => borrowOrLend(amount)} + onAction={(amount) => (amount ? borrowOrLend(amount) : closeBalance())} /> ); diff --git a/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCardActions.tsx b/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCardActions.tsx index 4546412ead..d2671d033f 100644 --- a/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCardActions.tsx +++ b/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCardActions.tsx @@ -3,12 +3,13 @@ import { View, Text, Pressable } from "react-native"; import tw from "~/styles/tailwind"; import { NumberInput, PrimaryButton, SecondaryButton } from "~/components/Common"; import { ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; +import { uiToNative } from "@mrgnlabs/mrgn-common"; type Props = { currentAction: ActionType; bank: ExtendedBankInfo; isBankFilled: boolean; - onAction: (amount: string) => void; + onAction: (amount?: string) => void; }; export function PoolCardActions({ currentAction, bank, isBankFilled, onAction }: Props) { @@ -27,20 +28,20 @@ export function PoolCardActions({ currentAction, bank, isBankFilled, onAction }: } }, [bank.userInfo, currentAction]); - const isDisabled = useMemo(() => { - switch (currentAction) { - case ActionType.Deposit: - return isBankFilled; - case ActionType.Withdraw: - return false; - case ActionType.Borrow: - return isBankFilled; - case ActionType.Repay: - return false; - } - }, [currentAction, isBankFilled]); + const isDust = useMemo( + () => bank.isActive && uiToNative(bank.position.amount, bank.info.state.mintDecimals).isZero(), + [bank] + ); + + const isDisabled = useMemo( + () => + (isDust && uiToNative(bank.userInfo.tokenAccount.balance, bank.info.state.mintDecimals).isZero()) || + maxAmount === 0, + [currentAction, bank, isDust, maxAmount] + ); const buttonText = useMemo(() => { + if (isDust) return "Close"; switch (currentAction) { case ActionType.Deposit: return isDisabled ? "Deposits reached the limit" : "Supply"; @@ -51,7 +52,7 @@ export function PoolCardActions({ currentAction, bank, isBankFilled, onAction }: case ActionType.Repay: return "Repay"; } - }, [currentAction, isDisabled]); + }, [currentAction, isDisabled, isDust]); return ( <> @@ -72,7 +73,7 @@ export function PoolCardActions({ currentAction, bank, isBankFilled, onAction }: {currentAction == ActionType.Withdraw || currentAction == ActionType.Repay ? ( - onAction(amount)} /> + (isDust ? onAction() : onAction(amount))} /> ) : ( onAction(amount)} /> )} diff --git a/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCardSkeleton.tsx b/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCardSkeleton.tsx index ca9d8e9a53..cae35f07d7 100644 --- a/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCardSkeleton.tsx +++ b/apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCardSkeleton.tsx @@ -4,7 +4,7 @@ import { View } from "react-native"; import tw from "~/styles/tailwind"; export const PoolCardSkeleton = (props?: JSX.IntrinsicAttributes & IContentLoaderProps) => ( - + bankFilled >= 0.9, [bankFilled]); return ( - + Weight {assetWeight} diff --git a/apps/marginfi-v2-xnft/src/screens/LendScreen.tsx b/apps/marginfi-v2-xnft/src/screens/LendScreen.tsx index 7b95940b21..f8c4b18714 100644 --- a/apps/marginfi-v2-xnft/src/screens/LendScreen.tsx +++ b/apps/marginfi-v2-xnft/src/screens/LendScreen.tsx @@ -68,52 +68,56 @@ export function LendScreen() { Filter my positions Global pools - {extendedBankInfos.length > 0 ? ( - globalPools.length > 0 ? ( - globalPools.map((extendedBankInfo, idx) => ( - { - if (!connection) return; - fetchMrgnlendState({ marginfiConfig: config.mfiConfig, connection, wallet }); - }} - marginfiClient={marginfiClient} - > - )) + + {extendedBankInfos.length > 0 ? ( + globalPools.length > 0 ? ( + globalPools.map((extendedBankInfo, idx) => ( + { + if (!connection) return; + fetchMrgnlendState({ marginfiConfig: config.mfiConfig, connection, wallet }); + }} + marginfiClient={marginfiClient} + > + )) + ) : ( + No Global Pools Found + ) ) : ( - No Global Pools Found - ) - ) : ( - - )} + + )} + Isolated pools - {extendedBankInfos.length > 0 ? ( - isolatedPools.length > 0 ? ( - isolatedPools.map((extendedBankInfo, idx) => ( - { - if (!connection) return; - fetchMrgnlendState({ marginfiConfig: config.mfiConfig, connection, wallet }); - }} - marginfiClient={marginfiClient} - > - )) + + {extendedBankInfos.length > 0 ? ( + isolatedPools.length > 0 ? ( + isolatedPools.map((extendedBankInfo, idx) => ( + { + if (!connection) return; + fetchMrgnlendState({ marginfiConfig: config.mfiConfig, connection, wallet }); + }} + marginfiClient={marginfiClient} + > + )) + ) : ( + No Isolated Pools Found + ) ) : ( - No Isolated Pools Found - ) - ) : ( - - )} + + )} +