Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mfi-v2-xnft): xnft refine & dust cleanup and layout fixes #228

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions apps/marginfi-v2-xnft/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -82,7 +83,7 @@ function TabNavigator() {
options={{
header: (props: BottomTabHeaderProps) => <LogoTitle />,
tabBarLabel: "Lend",
tabBarIcon: ({ color, size }) => <MaterialCommunityIcons name="account" color={color} size={size} />,
tabBarIcon: ({ color, size }) => <ReceiveMoneyIcon color={color} height={size} width={size} />,
}}
/>
<Tab.Screen
Expand All @@ -91,7 +92,7 @@ function TabNavigator() {
options={{
header: (props: BottomTabHeaderProps) => <LogoTitle />,
tabBarLabel: "Swap",
tabBarIcon: ({ color, size }) => <MaterialCommunityIcons name="bank" color={color} size={size} />,
tabBarIcon: ({ color, size }) => <TokenSwapIcon color={color} height={size} width={size} />,
}}
/>
<Tab.Screen
Expand All @@ -100,7 +101,7 @@ function TabNavigator() {
options={{
header: (props: BottomTabHeaderProps) => <LogoTitle />,
tabBarLabel: "Portfolio",
tabBarIcon: ({ color, size }) => <MaterialCommunityIcons name="home" color={color} size={size} />,
tabBarIcon: ({ color, size }) => <PieChartIcon color={color} height={size} width={size} />,
}}
/>
</Tab.Navigator>
Expand Down
16 changes: 16 additions & 0 deletions apps/marginfi-v2-xnft/src/assets/icons/PieChartIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";

interface PieChartIconProps extends React.SVGAttributes<SVGElement> {
color?: string;
}

export const PieChartIcon: React.FC<PieChartIconProps> = ({ width = "24", height = "24", color = "white" }) => {
return (
<svg width={width} height={height} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
d="M11 2.04932V12.9999H21.9506C21.4489 18.0533 17.1853 21.9999 12 21.9999C6.47715 21.9999 2 17.5228 2 11.9999C2 6.81459 5.94668 2.55104 11 2.04932ZM13 2.04932C17.7244 2.51839 21.4816 6.27552 21.9506 10.9999H13V2.04932Z"
fill={color}
/>
</svg>
);
};
19 changes: 19 additions & 0 deletions apps/marginfi-v2-xnft/src/assets/icons/ReceiveMoneyIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";

interface ReceiveMoneyIconProps extends React.SVGAttributes<SVGElement> {
color?: string;
}

export const ReceiveMoneyIcon: React.FC<ReceiveMoneyIconProps> = ({ width = "24", height = "24", color = "black" }) => {
return (
<svg width={width} height={height} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<g transform="translate(0,0)">
<path
d="M258 21.89c-.5 0-1.2 0-1.8.12-4.6.85-10.1 5.1-13.7 14.81-3.8 9.7-4.6 23.53-1.3 38.34 3.4 14.63 10.4 27.24 18.2 34.94 7.6 7.7 14.5 9.8 19.1 9 4.8-.7 10.1-5.1 13.7-14.7 3.8-9.64 4.8-23.66 1.4-38.35-3.5-14.8-10.4-27.29-18.2-34.94-6.6-6.8-12.7-9.22-17.4-9.22zM373.4 151.4c-11 .3-24.9 3.2-38.4 8.9-15.6 6.8-27.6 15.9-34.2 24.5-6.6 8.3-7.2 14.6-5.1 18.3 2.2 3.7 8.3 7.2 20 7.7 11.7.7 27.5-2.2 43-8.8 15.5-6.7 27.7-15.9 34.3-24.3 6.6-8.3 7.1-14.8 5-18.5-2.1-3.8-8.3-7.1-20-7.5-1.6-.3-3-.3-4.6-.3zm-136.3 92.9c-6.6.1-12.6.9-18 2.3-11.8 3-18.6 8.4-20.8 14.9-2.5 6.5 0 14.3 7.8 22.7 8.2 8.2 21.7 16.1 38.5 20.5 16.7 4.4 32.8 4.3 44.8 1.1 12.1-3.1 18.9-8.6 21.1-15 2.3-6.5 0-14.2-8.1-22.7-7.9-8.2-21.4-16.1-38.2-20.4-9.5-2.5-18.8-3.5-27.1-3.4zm160.7 58.1L336 331.7c4.2.2 14.7.5 14.7.5l6.6 8.7 54.7-28.5-14.2-10zm-54.5.1l-57.4 27.2c5.5.3 18.5.5 23.7.8l49.8-23.6-16.1-4.4zm92.6 10.8l-70.5 37.4 14.5 18.7 74.5-44.6-18.5-11.5zm-278.8 9.1a40.33 40.33 0 0 0-9 1c-71.5 16.5-113.7 17.9-126.2 17.9H18v107.5s11.6-1.7 30.9-1.8c37.3 0 103 6.4 167 43.8 3.4 2.1 10.7 2.9 19.8 2.9 24.3 0 61.2-5.8 69.7-9C391 452.6 494 364.5 494 364.5l-32.5-28.4s-79.8 50.9-89.9 55.8c-91.1 44.7-164.9 16.8-164.9 16.8s119.9 3 158.4-27.3l-22.6-34s-82.8-2.3-112.3-6.2c-15.4-2-48.7-18.8-73.1-18.8z"
fill={color}
fillOpacity="1"
/>
</g>
</svg>
);
};
16 changes: 16 additions & 0 deletions apps/marginfi-v2-xnft/src/assets/icons/TokenSwapIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";

interface TokenSwapIconProps extends React.SVGAttributes<SVGElement> {
color?: string;
}

export const TokenSwapIcon: React.FC<TokenSwapIconProps> = ({ width = "24", height = "24", color = "black" }) => {
return (
<svg width={width} height={height} viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
d="M21.5 9C21.5 11.7039 19.849 14.0223 17.5 15.0018L17.5 15C17.5 10.3056 13.6944 6.5 9.00001 6.5L8.99817 6.5C9.97773 4.15105 12.2961 2.5 15 2.5C18.5899 2.5 21.5 5.41015 21.5 9ZM7 3C4.79086 3 3 4.79086 3 7V8.5H5V7C5 5.89543 5.89543 5 7 5H8.5V3H7ZM19 15.5V17C19 18.1046 18.1046 19 17 19H15.5V21H17C19.2091 21 21 19.2091 21 17V15.5H19ZM9 21.5C12.5899 21.5 15.5 18.5899 15.5 15C15.5 11.4101 12.5899 8.5 9 8.5C5.41015 8.5 2.5 11.4101 2.5 15C2.5 18.5899 5.41015 21.5 9 21.5ZM9 12.5L11.5 15L9 17.5L6.5 15L9 12.5Z"
fill={color}
/>
</svg>
);
};
3 changes: 0 additions & 3 deletions apps/marginfi-v2-xnft/src/assets/icons/chevron-down.svg

This file was deleted.

3 changes: 3 additions & 0 deletions apps/marginfi-v2-xnft/src/assets/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
46 changes: 43 additions & 3 deletions apps/marginfi-v2-xnft/src/components/Lend/PoolCard/PoolCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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);
Expand All @@ -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 (
<View style={tw`bg-[#1C2125] rounded-xl px-12px py-16px flex flex-column gap-16px `}>
<View
style={[
tw`bg-[#1C2125] rounded-xl px-12px py-16px flex flex-column gap-16px w-[341px]`,
// { height: "fit-content" },
]}
>
<View style={tw`flex flex-row justify-between`}>
<View style={tw`flex flex-row gap-7px`}>
<Image style={styles.logo} source={{ uri: bankInfo.meta.tokenLogoUri }} alt={bankInfo.meta.tokenSymbol} />
Expand All @@ -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())}
/>
</View>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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";
Expand All @@ -51,7 +52,7 @@ export function PoolCardActions({ currentAction, bank, isBankFilled, onAction }:
case ActionType.Repay:
return "Repay";
}
}, [currentAction, isDisabled]);
}, [currentAction, isDisabled, isDust]);

return (
<>
Expand All @@ -72,7 +73,7 @@ export function PoolCardActions({ currentAction, bank, isBankFilled, onAction }:
</View>
</View>
{currentAction == ActionType.Withdraw || currentAction == ActionType.Repay ? (
<SecondaryButton title={buttonText ?? ""} onPress={() => onAction(amount)} />
<SecondaryButton title={buttonText ?? ""} onPress={() => (isDust ? onAction() : onAction(amount))} />
) : (
<PrimaryButton title={buttonText ?? ""} onPress={() => onAction(amount)} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { View } from "react-native";
import tw from "~/styles/tailwind";

export const PoolCardSkeleton = (props?: JSX.IntrinsicAttributes & IContentLoaderProps) => (
<View style={tw`bg-[#1C2125] rounded-xl px-12px py-16px gap-16px `}>
<View style={tw`bg-[#1C2125] rounded-xl px-12px py-16px gap-16px max-w-sm min-w-[300px]`}>
<ContentLoader
speed={2}
width={310.4}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function PoolCardStats({ bank, isInLendingMode, nativeSolBalance, bankFil
const isHigh = useMemo(() => bankFilled >= 0.9, [bankFilled]);

return (
<View style={tw`flex flex-row`}>
<View style={tw`flex flex-row h-60px`}>
<View style={tw`flex flex-col min-w-77px`}>
<Text style={tw`font-normal text-sm text-tertiary`}>Weight</Text>
<Text style={tw`font-medium text-base text-primary`}>{assetWeight}</Text>
Expand Down
88 changes: 46 additions & 42 deletions apps/marginfi-v2-xnft/src/screens/LendScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,52 +68,56 @@ export function LendScreen() {
<Text style={tw`text-base text-primary`}>Filter my positions</Text>
</View>
<Text style={tw`text-xl text-primary pl-12px`}>Global pools</Text>
{extendedBankInfos.length > 0 ? (
globalPools.length > 0 ? (
globalPools.map((extendedBankInfo, idx) => (
<PoolCard
key={idx}
bankInfo={extendedBankInfo}
nativeSolBalance={nativeSolBalance}
isInLendingMode={tabActive === "lend"}
marginfiAccount={selectedAccount}
reloadBanks={async () => {
if (!connection) return;
fetchMrgnlendState({ marginfiConfig: config.mfiConfig, connection, wallet });
}}
marginfiClient={marginfiClient}
></PoolCard>
))
<View style={tw`flex flex-row gap-2 flex-wrap`}>
{extendedBankInfos.length > 0 ? (
globalPools.length > 0 ? (
globalPools.map((extendedBankInfo, idx) => (
<PoolCard
key={idx}
bankInfo={extendedBankInfo}
nativeSolBalance={nativeSolBalance}
isInLendingMode={tabActive === "lend"}
marginfiAccount={selectedAccount}
reloadBanks={async () => {
if (!connection) return;
fetchMrgnlendState({ marginfiConfig: config.mfiConfig, connection, wallet });
}}
marginfiClient={marginfiClient}
></PoolCard>
))
) : (
<Text style={tw`text-sm text-secondary pl-12px`}>No Global Pools Found</Text>
)
) : (
<Text style={tw`text-sm text-secondary pl-12px`}>No Global Pools Found</Text>
)
) : (
<PoolCardSkeleton />
)}
<PoolCardSkeleton />
)}
</View>

<Text style={tw`text-xl text-primary pl-12px`}>Isolated pools</Text>
{extendedBankInfos.length > 0 ? (
isolatedPools.length > 0 ? (
isolatedPools.map((extendedBankInfo, idx) => (
<PoolCard
key={idx}
bankInfo={extendedBankInfo}
nativeSolBalance={nativeSolBalance}
isInLendingMode={tabActive === "lend"}
marginfiAccount={selectedAccount}
reloadBanks={async () => {
if (!connection) return;
fetchMrgnlendState({ marginfiConfig: config.mfiConfig, connection, wallet });
}}
marginfiClient={marginfiClient}
></PoolCard>
))
<View style={tw`flex flex-row gap-2 flex-wrap`}>
{extendedBankInfos.length > 0 ? (
isolatedPools.length > 0 ? (
isolatedPools.map((extendedBankInfo, idx) => (
<PoolCard
key={idx}
bankInfo={extendedBankInfo}
nativeSolBalance={nativeSolBalance}
isInLendingMode={tabActive === "lend"}
marginfiAccount={selectedAccount}
reloadBanks={async () => {
if (!connection) return;
fetchMrgnlendState({ marginfiConfig: config.mfiConfig, connection, wallet });
}}
marginfiClient={marginfiClient}
></PoolCard>
))
) : (
<Text style={tw`text-sm text-secondary pl-12px`}>No Isolated Pools Found</Text>
)
) : (
<Text style={tw`text-sm text-secondary pl-12px`}>No Isolated Pools Found</Text>
)
) : (
<PoolCardSkeleton />
)}
<PoolCardSkeleton />
)}
</View>
</View>
</View>
</Screen>
Expand Down