From a210bc24a87953044e74a1aaec7541559fe3cf19 Mon Sep 17 00:00:00 2001 From: borcherd Date: Mon, 13 Jan 2025 15:39:15 +0100 Subject: [PATCH 01/11] feat: reworked & rebased v1 branch --- .../Portfolio/PositionActionButtons.tsx | 22 +- .../components/action-box-v2/action-box.tsx | 42 +- .../components/action-box-v2/actions/index.ts | 1 + .../components/action-input/action-input.tsx | 126 ++++ .../components/bank-select/bank-select.tsx | 61 ++ .../components/bank-list/bank-list.tsx | 232 ++++++++ .../bank-select/components/bank-list/index.ts | 1 + .../components/bank-trigger/bank-trigger.tsx | 43 ++ .../components/bank-trigger/index.ts | 1 + .../bank-select/components/index.ts | 2 + .../components/bank-select/index.ts | 1 + .../action-input/components/index.ts | 2 + .../components/lending-action/index.ts | 1 + .../lending-action/swap-lend-action.tsx | 102 ++++ .../components/action-input/index.ts | 1 + .../actions/swap-lend-box/components/index.ts | 2 + .../swap-lend-box/components/preview/index.ts | 1 + .../components/preview/preview.tsx | 105 ++++ .../actions/swap-lend-box/hooks/index.ts | 1 + .../hooks/use-swap-lend-simulation.hooks.ts | 230 ++++++++ .../actions/swap-lend-box/index.ts | 1 + .../actions/swap-lend-box/store/index.ts | 16 + .../store/swap-lend-box-store.ts | 181 ++++++ .../actions/swap-lend-box/swap-lend-box.tsx | 542 ++++++++++++++++++ .../actions/swap-lend-box/utils/index.ts | 2 + .../utils/swap-lend-action.utils.ts | 316 ++++++++++ .../utils/swap-lend-simulation.utils.ts | 48 ++ .../collateral-progress-bar.tsx | 48 ++ .../action-collateral-progress-bar/index.ts | 1 + .../action-box-v2/components/index.ts | 1 + .../action-box-v2/types/action-box.types.ts | 18 +- .../mrgn-utils/src/action-message.utils.ts | 39 ++ packages/mrgn-utils/src/actions/actions.ts | 17 +- .../mrgn-utils/src/actions/individualFlows.ts | 62 ++ packages/mrgn-utils/src/actions/types.ts | 4 + packages/mrgn-utils/src/errors.ts | 14 + packages/mrgn-utils/src/types.ts | 14 +- 37 files changed, 2290 insertions(+), 11 deletions(-) create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/action-input.tsx create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/bank-select.tsx create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/swap-lend-action.tsx create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/preview.tsx create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/swap-lend-box-store.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/swap-lend-box.tsx create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/index.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-simulation.utils.ts create mode 100644 packages/mrgn-ui/src/components/action-box-v2/components/action-collateral-progress-bar/collateral-progress-bar.tsx create mode 100644 packages/mrgn-ui/src/components/action-box-v2/components/action-collateral-progress-bar/index.ts diff --git a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx index 759a357e18..f8ab910566 100644 --- a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx +++ b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx @@ -12,7 +12,7 @@ import { ActionBox, ActionBoxProvider } from "~/components/action-box-v2"; import { Button } from "~/components/ui/button"; import { MarginfiAccountWrapper, MarginfiClient } from "@mrgnlabs/marginfi-client-v2"; -import { ArenaPoolV2Extended } from "~/types/trade-store.types"; +import { ArenaPoolV2Extended, GroupStatus } from "~/types/trade-store.types"; import { ClosePosition } from "./components"; type PositionActionButtonsProps = { @@ -37,10 +37,11 @@ export const PositionActionButtons = ({ const { connection } = useConnection(); const { wallet, connected } = useWallet(); - const [refreshGroup, nativeSolBalance, positionsByGroupPk] = useTradeStoreV2((state) => [ + const [refreshGroup, nativeSolBalance, positionsByGroupPk, banksByBankPk] = useTradeStoreV2((state) => [ state.refreshGroup, state.nativeSolBalance, state.positionsByGroupPk, + state.banksByBankPk, ]); const depositBanks = React.useMemo(() => { @@ -64,9 +65,16 @@ export const PositionActionButtons = ({ return borrowBank; }, [arenaPool]); + const extendedBankInfos = React.useMemo(() => { + const banks = Object.values(banksByBankPk); + const uniqueBanksMap = new Map(banks.map((bank) => [bank.info.state.mint.toBase58(), bank])); + const uniqueBanks = Array.from(uniqueBanksMap.values()); + return uniqueBanks; + }, [banksByBankPk]); + return (
- { capture("position_add_btn_click", { diff --git a/packages/mrgn-ui/src/components/action-box-v2/action-box.tsx b/packages/mrgn-ui/src/components/action-box-v2/action-box.tsx index 80223f882b..0c14116c4d 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/action-box.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/action-box.tsx @@ -2,7 +2,17 @@ import React from "react"; import { ActionType } from "@mrgnlabs/marginfi-v2-ui-state"; -import { LendBox, LendBoxProps, LoopBox, LoopBoxProps, RepayCollatBox, StakeBox, StakeBoxProps } from "./actions"; +import { + LendBox, + LendBoxProps, + LoopBox, + LoopBoxProps, + RepayCollatBox, + StakeBox, + StakeBoxProps, + SwapLendBox, + SwapLendBoxProps, +} from "./actions"; import { ActionDialogWrapper, ActionBoxWrapper, ActionBoxNavigator } from "./components"; import { useActionBoxContext, useStakeBoxContext } from "./contexts"; import { @@ -14,6 +24,7 @@ import { RequiredStakeBoxProps, RequiredRepayBoxProps, RequiredLoopBoxProps, + RequiredSwapLendBoxProps, } from "./types"; const ActionBox: ActionBoxComponent = (props) => { @@ -63,6 +74,35 @@ const Lend = (props: ActionBoxProps & { lendProps: RequiredLendBoxProps | LendBo }; ActionBox.Lend = Lend; +const SwapLend = ( + props: ActionBoxProps & { swapLendProps: RequiredSwapLendBoxProps | SwapLendBoxProps; useProvider?: boolean } +) => { + const contextProps = useActionBoxContext(); + const { swapLendProps, useProvider, ...actionBoxProps } = props; + + let combinedProps: SwapLendBoxProps; + + if (useProvider && contextProps) { + combinedProps = { + ...contextProps, + ...(swapLendProps as RequiredSwapLendBoxProps), + }; + } else { + combinedProps = swapLendProps as SwapLendBoxProps; + } + + return ( + + + + + + + + ); +}; +ActionBox.SwapLend = SwapLend; + const BorrowLend = ( props: ActionBoxProps & { lendProps: RequiredLendBoxProps | LendBoxProps; useProvider?: boolean } ) => { diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/index.ts index 336b4d46c2..421930f951 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/index.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/index.ts @@ -2,3 +2,4 @@ export * from "./lend-box"; export * from "./repay-collat-box"; export * from "./loop-box"; export * from "./stake-box"; +export * from "./swap-lend-box"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/action-input.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/action-input.tsx new file mode 100644 index 0000000000..942e23e467 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/action-input.tsx @@ -0,0 +1,126 @@ +import React from "react"; + +import { ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; +import { formatAmount } from "@mrgnlabs/mrgn-utils"; +import { usdFormatter, tokenPriceFormatter } from "@mrgnlabs/mrgn-common"; + +import { Input } from "~/components/ui/input"; + +import { SwapLendAction, BankSelect } from "./components"; + +type ActionInputProps = { + amountRaw: string; + amount: number | null; + nativeSolBalance: number; + walletAmount: number | undefined; + maxAmount: number; + banks: ExtendedBankInfo[]; + selectedBank: ExtendedBankInfo | null; + lendMode: ActionType; + + connected: boolean; + showCloseBalance?: boolean; + isDialog?: boolean; + showTokenSelection?: boolean; + showTokenSelectionGroups?: boolean; + isMini?: boolean; + + isInputDisabled?: boolean; + + setAmountRaw: (amount: string) => void; + setSelectedBank: (bank: ExtendedBankInfo | null) => void; +}; + +export const ActionInput = ({ + banks, + nativeSolBalance, + walletAmount, + maxAmount, + showCloseBalance, + connected, + isDialog, + showTokenSelection, + showTokenSelectionGroups, + amountRaw, + amount, + selectedBank, + lendMode, + isInputDisabled: _isInputDisabled, + setAmountRaw, + setSelectedBank, +}: ActionInputProps) => { + const amountInputRef = React.useRef(null); + + const numberFormater = React.useMemo(() => new Intl.NumberFormat("en-US", { maximumFractionDigits: 10 }), []); + + const isInputDisabled = React.useMemo( + () => (maxAmount === 0 && !showCloseBalance) || _isInputDisabled, + [maxAmount, showCloseBalance, _isInputDisabled] + ); + + const formatAmountCb = React.useCallback( + (newAmount: string, bank: ExtendedBankInfo | null) => { + return formatAmount(newAmount, maxAmount, bank, numberFormater); + }, + [maxAmount, numberFormater] + ); + + const handleInputChange = React.useCallback( + (newAmount: string) => { + setAmountRaw(formatAmountCb(newAmount, selectedBank)); + }, + [formatAmountCb, setAmountRaw, selectedBank] + ); + + const isTokenSelectionAvailable = React.useMemo(() => { + if (showTokenSelection === undefined) return !isDialog; + else return showTokenSelection; + }, [showTokenSelection, isDialog]); + + return ( +
+
+
+ { + setSelectedBank(bank); + }} + isSelectable={isTokenSelectionAvailable} + showTokenSelectionGroups={showTokenSelectionGroups} + banks={banks} + nativeSolBalance={nativeSolBalance} + lendMode={lendMode} + connected={connected} + /> +
+
+ handleInputChange(e.target.value)} + placeholder="0" + className="bg-transparent shadow-none min-w-[130px] text-right h-auto py-0 pr-0 outline-none focus-visible:outline-none focus-visible:ring-0 border-none text-base font-medium" + /> + {amount !== null && amount > 0 && selectedBank && ( + + {tokenPriceFormatter(amount * selectedBank.info.oraclePrice.priceRealtime.price.toNumber())} + + )} +
+
+ {!isInputDisabled && ( + handleInputChange(amount)} + selectedBank={selectedBank} + lendMode={lendMode} + /> + )} +
+ ); +}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/bank-select.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/bank-select.tsx new file mode 100644 index 0000000000..0b1aebdb83 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/bank-select.tsx @@ -0,0 +1,61 @@ +import React from "react"; + +import { ExtendedBankInfo, ActionType } from "@mrgnlabs/marginfi-v2-ui-state"; +import { computeBankRate, LendingModes } from "@mrgnlabs/mrgn-utils"; + +import { SelectedBankItem, BankListWrapper } from "~/components/action-box-v2/components"; + +import { BankTrigger, BankList } from "./components"; + +type BankSelectProps = { + selectedBank: ExtendedBankInfo | null; + banks: ExtendedBankInfo[]; + nativeSolBalance: number; + lendMode: ActionType; + connected: boolean; + isSelectable?: boolean; + showTokenSelectionGroups?: boolean; + setSelectedBank: (selectedBank: ExtendedBankInfo | null) => void; +}; + +export const BankSelect = ({ + selectedBank, + banks, + nativeSolBalance, + lendMode, + connected, + isSelectable = true, + showTokenSelectionGroups, + setSelectedBank, +}: BankSelectProps) => { + // idea check list if banks[] == 1 make it unselectable + const [isOpen, setIsOpen] = React.useState(false); + + const lendingMode = React.useMemo( + () => + lendMode === ActionType.Deposit || lendMode === ActionType.Withdraw ? LendingModes.LEND : LendingModes.BORROW, + [lendMode] + ); + return ( + <> + } + Content={ + setIsOpen(false)} + selectedBank={selectedBank} + onSetSelectedBank={setSelectedBank} + lendMode={lendMode} + banks={banks} + nativeSolBalance={nativeSolBalance} + connected={connected} + showTokenSelectionGroups={showTokenSelectionGroups} + /> + } + /> + + ); +}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx new file mode 100644 index 0000000000..4592cf66ec --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx @@ -0,0 +1,232 @@ +import React from "react"; + +import { WSOL_MINT } from "@mrgnlabs/mrgn-common"; +import { ExtendedBankInfo, ActionType } from "@mrgnlabs/marginfi-v2-ui-state"; +import { LendingModes, cn, computeBankRate } from "@mrgnlabs/mrgn-utils"; + +import { CommandEmpty, CommandGroup, CommandItem } from "~/components/ui/command"; +import { BankItem, BankListCommand } from "~/components/action-box-v2/components"; + +type BankListProps = { + selectedBank: ExtendedBankInfo | null; + banks: ExtendedBankInfo[]; + nativeSolBalance: number; + isOpen: boolean; + lendMode: ActionType; + connected: boolean; + showTokenSelectionGroups?: boolean; + onSetSelectedBank: (selectedTokenBank: ExtendedBankInfo | null) => void; + onClose: () => void; +}; + +export const BankList = ({ + selectedBank, + banks, + nativeSolBalance, + lendMode, + connected, + showTokenSelectionGroups = true, + onSetSelectedBank, + isOpen, + onClose, +}: BankListProps) => { + const lendingMode = React.useMemo( + () => + lendMode === ActionType.Deposit || lendMode === ActionType.Withdraw ? LendingModes.LEND : LendingModes.BORROW, + [lendMode] + ); + + const [searchQuery, setSearchQuery] = React.useState(""); + + const calculateRate = React.useCallback( + (bank: ExtendedBankInfo) => { + return computeBankRate(bank, lendingMode); + }, + [lendingMode] + ); + + const hasTokens = React.useMemo(() => { + const hasBankTokens = !!banks.filter( + (bank) => bank.userInfo.tokenAccount.balance !== 0 || bank.meta.tokenSymbol === "SOL" + ); + + return hasBankTokens; + }, [banks]); + + /////// FILTERS + + // filter on balance + const balanceFilter = React.useCallback( + (bankInfo: ExtendedBankInfo) => { + const isWSOL = bankInfo.info.state.mint?.equals ? bankInfo.info.state.mint.equals(WSOL_MINT) : false; + const balance = isWSOL + ? bankInfo.userInfo.tokenAccount.balance + nativeSolBalance + : bankInfo.userInfo.tokenAccount.balance; + return balance > 0; + }, + [nativeSolBalance] + ); + + // filter on search + const searchFilter = React.useCallback( + (bankInfo: ExtendedBankInfo) => { + const lowerCaseSearchQuery = searchQuery.toLowerCase(); + return bankInfo.meta.tokenSymbol.toLowerCase().includes(lowerCaseSearchQuery); + }, + [searchQuery] + ); + + // filter on positions + const positionFilter = React.useCallback( + (bankInfo: ExtendedBankInfo, filterActive?: boolean) => + bankInfo.isActive ? lendingMode === LendingModes.LEND && bankInfo.position.isLending : filterActive, + [lendingMode] + ); + + /////// BANKS + // wallet banks + const filteredBanksUserOwns = React.useMemo(() => { + return ( + banks + .filter(balanceFilter) + .filter(searchFilter) + // .filter((bank) => positionFilter(bank, true)) + .sort((a, b) => { + const isFirstWSOL = a.info.state.mint?.equals ? a.info.state.mint.equals(WSOL_MINT) : false; + const isSecondWSOL = b.info.state.mint?.equals ? b.info.state.mint.equals(WSOL_MINT) : false; + const firstBalance = + (isFirstWSOL ? a.userInfo.tokenAccount.balance + nativeSolBalance : a.userInfo.tokenAccount.balance) * + a.info.state.price; + const secondBalance = + (isSecondWSOL ? b.userInfo.tokenAccount.balance + nativeSolBalance : b.userInfo.tokenAccount.balance) * + b.info.state.price; + return secondBalance - firstBalance; + }) + ); + }, [banks, balanceFilter, searchFilter, nativeSolBalance]); + + // other banks without positions + const filteredBanks = React.useMemo(() => { + return banks.filter(searchFilter); + }, [banks, searchFilter]); + + const globalBanks = React.useMemo(() => filteredBanks.filter((bank) => !bank.info.state.isIsolated), [filteredBanks]); + const isolatedBanks = React.useMemo( + () => filteredBanks.filter((bank) => bank.info.state.isIsolated), + [filteredBanks] + ); + + React.useEffect(() => { + if (!isOpen) { + setSearchQuery(""); + } + }, [isOpen]); + + return ( + <> + + {!hasTokens && ( +
+ You don't own any supported tokens in marginfi. Check out what marginfi supports. +
+ )} + No tokens found. + + {/* LENDING */} + {lendingMode === LendingModes.LEND && connected && filteredBanksUserOwns.length > 0 && onSetSelectedBank && ( + + {filteredBanksUserOwns + .slice(0, searchQuery.length === 0 ? filteredBanksUserOwns.length : 3) + .map((bank, index) => { + return ( + { + onSetSelectedBank( + banks.find((bankInfo) => bankInfo.address.toString().toLowerCase() === currentValue) ?? null + ); + onClose(); + }} + className="cursor-pointer h-[55px] px-3 font-medium flex items-center justify-between gap-2 data-[selected=true]:bg-mfi-action-box-accent data-[selected=true]:text-mfi-action-box-accent-foreground" + > + + + ); + })} + + )} + + {/* GLOBAL & ISOLATED */} + {globalBanks.length > 0 && onSetSelectedBank && showTokenSelectionGroups && ( + + {globalBanks.map((bank, index) => { + return ( + { + onSetSelectedBank( + banks.find((bankInfo) => bankInfo.address.toString().toLowerCase() === currentValue) ?? null + ); + onClose(); + }} + className={cn( + "cursor-pointer font-medium flex items-center justify-between gap-2 data-[selected=true]:bg-mfi-action-box-accent data-[selected=true]:text-mfi-action-box-accent-foreground hover:bg-mfi-action-box-accent hover:text-mfi-action-box-accent-foreground", + lendingMode === LendingModes.LEND && "py-2", + lendingMode === LendingModes.BORROW && "h-[60px]" + )} + > + + + ); + })} + + )} + {isolatedBanks.length > 0 && onSetSelectedBank && showTokenSelectionGroups && ( + + {isolatedBanks.map((bank, index) => { + return ( + { + onSetSelectedBank( + banks.find((bankInfo) => bankInfo.address.toString().toLowerCase() === currentValue) ?? null + ); + onClose(); + }} + className={cn( + "cursor-pointer font-medium flex items-center justify-between gap-2 data-[selected=true]:bg-mfi-action-box-accent data-[selected=true]:text-mfi-action-box-accent-foreground hover:bg-mfi-action-box-accent hover:text-mfi-action-box-accent-foreground", + lendingMode === LendingModes.LEND && "py-2", + lendingMode === LendingModes.BORROW && "h-[60px]" + )} + > + + + ); + })} + + )} +
+ + ); +}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/index.ts new file mode 100644 index 0000000000..1baf175b0c --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/index.ts @@ -0,0 +1 @@ +export * from "./bank-list"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx new file mode 100644 index 0000000000..ee6675eb8e --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx @@ -0,0 +1,43 @@ +import React from "react"; + +import { IconChevronDown } from "@tabler/icons-react"; + +import { ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; +import { computeBankRate, cn, LendingModes } from "@mrgnlabs/mrgn-utils"; + +import { Button } from "~/components/ui/button"; +import { SelectedBankItem } from "~/components/action-box-v2/components"; + +type BankTriggerProps = { + selectedBank: ExtendedBankInfo | null; + lendingMode: LendingModes; + isOpen?: boolean; +}; + +export const BankTrigger = React.forwardRef( + ({ selectedBank, lendingMode, isOpen }, ref) => { + const calculateRate = React.useCallback( + (bank: ExtendedBankInfo) => computeBankRate(bank, lendingMode), + [lendingMode] + ); + + return ( + + ); + } +); + +BankTrigger.displayName = "BankTrigger"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/index.ts new file mode 100644 index 0000000000..55a8db9990 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/index.ts @@ -0,0 +1 @@ +export * from "./bank-trigger"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/index.ts new file mode 100644 index 0000000000..8fbda9140b --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/index.ts @@ -0,0 +1,2 @@ +export * from "./bank-list"; +export * from "./bank-trigger"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/index.ts new file mode 100644 index 0000000000..cfe746365c --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/index.ts @@ -0,0 +1 @@ +export * from "./bank-select"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/index.ts new file mode 100644 index 0000000000..c0ebc59db7 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/index.ts @@ -0,0 +1,2 @@ +export * from "./lending-action"; +export * from "./bank-select"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/index.ts new file mode 100644 index 0000000000..363c4b2d8c --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/index.ts @@ -0,0 +1 @@ +export * from "./swap-lend-action"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/swap-lend-action.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/swap-lend-action.tsx new file mode 100644 index 0000000000..093fa616fc --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/swap-lend-action.tsx @@ -0,0 +1,102 @@ +import React from "react"; + +import { ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; +import { dynamicNumeralFormatter } from "@mrgnlabs/mrgn-common"; + +type SwapLendActionProps = { + walletAmount: number | undefined; + maxAmount: number; + showLendingHeader?: boolean; + lendMode: ActionType; + selectedBank: ExtendedBankInfo | null; + + onSetAmountRaw: (amount: string) => void; +}; + +export const SwapLendAction = ({ + maxAmount, + walletAmount, + onSetAmountRaw, + selectedBank, + lendMode, +}: SwapLendActionProps) => { + const numberFormater = React.useMemo(() => new Intl.NumberFormat("en-US", { maximumFractionDigits: 10 }), []); + + const maxLabel = React.useMemo((): { + amount: string; + showWalletIcon?: boolean; + label?: string; + } => { + if (!selectedBank) { + return { + amount: "-", + showWalletIcon: false, + }; + } + + const formatAmount = (amount?: number, symbol?: string) => + amount !== undefined ? `${dynamicNumeralFormatter(amount)} ${symbol}` : "-"; + + switch (lendMode) { + case ActionType.Deposit: + return { + label: "Wallet: ", + amount: formatAmount(walletAmount, selectedBank?.meta.tokenSymbol), + }; + case ActionType.Borrow: + return { + label: "Max Borrow: ", + amount: formatAmount(selectedBank.userInfo.maxBorrow, selectedBank?.meta.tokenSymbol), + }; + + case ActionType.Withdraw: + return { + amount: formatAmount( + selectedBank?.isActive ? selectedBank.position.amount : undefined, + selectedBank?.meta.tokenSymbol + ), + label: "Supplied: ", + }; + + case ActionType.Repay: + return { + amount: formatAmount( + selectedBank?.isActive ? selectedBank.position.amount : undefined, + selectedBank?.meta.tokenSymbol + ), + label: "Borrowed: ", + }; + + default: + return { amount: "-" }; + } + }, [selectedBank, lendMode, walletAmount]); + + // const isMaxButtonVisible = React.useMemo(() => lendMode === ActionType.Repay, [lendMode]); + + // Section above the input + return ( + <> + {selectedBank && ( +
    +
  • + {maxLabel.label} +
    + {/* {selectedBank?.isActive &&
    {clampedNumeralFormatter(selectedBank.position.amount)}
    } + {selectedBank?.isActive && } */} +
    {maxLabel.amount}
    + + +
    +
  • +
+ )} + + ); +}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/index.ts new file mode 100644 index 0000000000..73b44a602a --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/index.ts @@ -0,0 +1 @@ +export * from "./action-input"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/index.ts new file mode 100644 index 0000000000..d69b32869a --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/index.ts @@ -0,0 +1,2 @@ +export * from "./action-input"; +export * from "./preview"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/index.ts new file mode 100644 index 0000000000..54b20cb400 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/index.ts @@ -0,0 +1 @@ +export * from "./preview"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/preview.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/preview.tsx new file mode 100644 index 0000000000..0d9520646f --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/preview.tsx @@ -0,0 +1,105 @@ +import React from "react"; + +import { ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; +import { cn } from "@mrgnlabs/mrgn-utils"; + +import { ActionStatItem } from "~/components/action-box-v2/components"; +import type { HidePoolStats } from "~/components/action-box-v2"; +import { + getAmountStat, + getAmountUsdStat, + getHealthStat, + getLiquidationStat, + getPoolSizeStat, + getBankTypeStat, + getOracleStat, + ActionSummary, +} from "~/components/action-box-v2/utils"; + +interface PreviewProps { + selectedBank: ExtendedBankInfo | null; + isLoading: boolean; + lendMode: ActionType; + actionSummary?: ActionSummary; + hidePoolStats?: HidePoolStats; +} + +export const Preview = ({ actionSummary, selectedBank, isLoading, lendMode, hidePoolStats }: PreviewProps) => { + const isLending = React.useMemo( + () => lendMode === ActionType.Deposit || lendMode === ActionType.Withdraw, + [lendMode] + ); + + const stats = React.useMemo( + () => + actionSummary && selectedBank + ? generateLendingStats(actionSummary, selectedBank, isLending, isLoading, hidePoolStats) + : null, + [actionSummary, selectedBank, isLending, isLoading, hidePoolStats] + ); + + return ( + <> + {stats && selectedBank && ( +
+ {stats.map((stat, idx) => ( + + + + ))} +
+ )} + + ); +}; + +function generateLendingStats( + summary: ActionSummary, + bank: ExtendedBankInfo, + isLending: boolean, + isLoading: boolean, + hidePoolStats?: HidePoolStats +) { + const stats = []; + + if (!hidePoolStats?.includes("amount")) { + stats.push( + getAmountStat( + summary.actionPreview.positionAmount, + bank.meta.tokenSymbol, + summary.simulationPreview?.positionAmount + ), + getAmountUsdStat( + summary.actionPreview.positionAmount, + bank.meta.tokenSymbol, + bank.info.oraclePrice.priceRealtime.price.toNumber(), + summary.simulationPreview?.positionAmount + ) + ); + } + + if (!hidePoolStats?.includes("health")) { + stats.push(getHealthStat(summary.actionPreview.health, false, summary.simulationPreview?.health)); + } + + if (summary.actionPreview.bankCap && !hidePoolStats?.includes("size")) { + stats.push(getPoolSizeStat(summary.actionPreview.bankCap, bank, isLending)); + } + + if (!hidePoolStats?.includes("oracle")) { + stats.push(getOracleStat(bank)); + } + + return stats; +} diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/index.ts new file mode 100644 index 0000000000..d1a7029923 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/index.ts @@ -0,0 +1 @@ +export * from "./use-swap-lend-simulation.hooks"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts new file mode 100644 index 0000000000..5e3d293c68 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts @@ -0,0 +1,230 @@ +import React from "react"; + +import { Transaction, VersionedTransaction } from "@solana/web3.js"; + +import { AccountSummary, ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; +import { MarginfiAccountWrapper, MarginfiClient, SimulationResult } from "@mrgnlabs/marginfi-client-v2"; +import { + ActionMessageType, + ActionTxns, + DYNAMIC_SIMULATION_ERRORS, + extractErrorString, + STATIC_SIMULATION_ERRORS, + SwapLendActionTxns, + usePrevious, +} from "@mrgnlabs/mrgn-utils"; +import { SimulationStatus } from "~/components/action-box-v2/utils"; +import { + calculateSummary, + generateSwapLendTxns, + GenerateSwapLendTxnsProps, + getSimulationResult, + SimulateActionProps, +} from "../utils"; + +type SwapLendSimulationProps = { + debouncedAmount: number; + selectedAccount: MarginfiAccountWrapper | null; + marginfiClient: MarginfiClient | null; + accountSummary?: AccountSummary; + depositBank: ExtendedBankInfo | null; + swapBank: ExtendedBankInfo | null; + actionTxns: SwapLendActionTxns; + simulationResult: SimulationResult | null; + setSimulationResult: (result: SimulationResult | null) => void; + setActionTxns: (actionTxns: SwapLendActionTxns) => void; + setErrorMessage: (error: ActionMessageType | null) => void; + setIsLoading: ({ isLoading, status }: { isLoading: boolean; status: SimulationStatus }) => void; +}; + +export function useSwapLendSimulation({ + debouncedAmount, + selectedAccount, + accountSummary, + marginfiClient, + depositBank, + swapBank, + actionTxns, + simulationResult, + setSimulationResult, + setActionTxns, + setErrorMessage, + setIsLoading, +}: SwapLendSimulationProps) { + const prevDebouncedAmount = usePrevious(debouncedAmount); + const prevDepositBank = usePrevious(depositBank); + const prevSwapBank = usePrevious(swapBank); + + const handleError = ( + actionMessage: ActionMessageType | string, + callbacks: { + setErrorMessage: (error: ActionMessageType | null) => void; + setSimulationResult: (result: SimulationResult | null) => void; + setActionTxns: (actionTxns: SwapLendActionTxns) => void; + setIsLoading: ({ isLoading, status }: { isLoading: boolean; status: SimulationStatus }) => void; + } + ) => { + if (typeof actionMessage === "string") { + const errorMessage = extractErrorString(actionMessage); + const _actionMessage: ActionMessageType = { + isEnabled: true, + description: errorMessage, + }; + callbacks.setErrorMessage(_actionMessage); + } else { + callbacks.setErrorMessage(actionMessage); + } + callbacks.setSimulationResult(null); + callbacks.setActionTxns({ actionTxn: null, additionalTxns: [], actionQuote: null }); + console.error( + "Error simulating transaction", + typeof actionMessage === "string" ? extractErrorString(actionMessage) : actionMessage.description + ); + callbacks.setIsLoading({ isLoading: false, status: SimulationStatus.COMPLETE }); + }; + + const simulationAction = async (props: SimulateActionProps) => { + if (props.txns.length > 0) { + const simulationResult = await getSimulationResult(props); + + console.log("simulationResult", simulationResult); + + if (simulationResult.actionMethod) { + return { simulationResult: null, actionMessage: simulationResult.actionMethod }; + } else if (simulationResult.simulationResult) { + return { simulationResult: simulationResult.simulationResult, actionMessage: null }; + } else { + const errorMessage = DYNAMIC_SIMULATION_ERRORS.TRADE_FAILED_CHECK(); + return { simulationResult: null, actionMessage: errorMessage }; + } + } else { + throw new Error("account, bank or transactions are null"); + } + }; + + const fetchSwapLendActionTxns = async ( + props: GenerateSwapLendTxnsProps + ): Promise<{ actionTxns: SwapLendActionTxns | null; actionMessage: ActionMessageType | null }> => { + try { + const swapLendActionTxns = await generateSwapLendTxns(props); + if (swapLendActionTxns && "actionTxn" in swapLendActionTxns) { + return { + actionTxns: { ...swapLendActionTxns, actionQuote: swapLendActionTxns.actionQuote }, + actionMessage: null, + }; + } else { + const errorMessage = swapLendActionTxns ?? STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED; + return { + actionTxns: null, + actionMessage: errorMessage, + }; + } + } catch (error) { + console.error("Error fetching swap lend action txns", error); + return { + actionTxns: null, + actionMessage: STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED, + }; + } + }; + + const handleSimulation = React.useCallback( + async (amount: number) => { + try { + if (amount === 0 || !depositBank || !selectedAccount || !marginfiClient) { + // TODO: will there be cases where the account isnt defined? In arena esp? + setActionTxns({ actionTxn: null, additionalTxns: [], actionQuote: null }); + return; + } + + setIsLoading({ isLoading: true, status: SimulationStatus.SIMULATING }); + + const props: GenerateSwapLendTxnsProps = { + marginfiAccount: selectedAccount ?? undefined, + depositBank: depositBank, + swapBank: swapBank, + amount: amount, + marginfiClient: marginfiClient, + slippageBps: 50, + }; + + const swapLendActionTxns = await fetchSwapLendActionTxns(props); + + if (swapLendActionTxns.actionMessage || swapLendActionTxns.actionTxns === null) { + handleError(swapLendActionTxns.actionMessage ?? STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED, { + setErrorMessage, + setSimulationResult, + setActionTxns, + setIsLoading, + }); + return; + } + + const simulationResult = await simulationAction({ + txns: [ + ...(swapLendActionTxns?.actionTxns?.additionalTxns ?? []), + ...(swapLendActionTxns?.actionTxns?.actionTxn ? [swapLendActionTxns?.actionTxns?.actionTxn] : []), + ], + account: selectedAccount, + bank: depositBank, + }); + + if (simulationResult.actionMessage || simulationResult.simulationResult === null) { + handleError(simulationResult.actionMessage ?? STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED, { + setErrorMessage, + setSimulationResult, + setActionTxns, + setIsLoading, + }); + return; + } else if (simulationResult.simulationResult) { + setSimulationResult(simulationResult.simulationResult); + setActionTxns(swapLendActionTxns.actionTxns); + } else { + throw new Error("Unknown error"); + } + } catch (error) { + console.error("Error simulating transaction", error); + setSimulationResult(null); + } finally { + setIsLoading({ isLoading: false, status: SimulationStatus.COMPLETE }); + } + }, + [depositBank, marginfiClient, selectedAccount, setActionTxns, setIsLoading, setSimulationResult, swapBank] + ); + + React.useEffect(() => { + if (prevDebouncedAmount !== debouncedAmount || prevDepositBank !== depositBank || prevSwapBank !== swapBank) { + if (debouncedAmount > 0) { + handleSimulation(debouncedAmount); + } + } + }, [debouncedAmount, depositBank, handleSimulation, prevDebouncedAmount, prevDepositBank, prevSwapBank, swapBank]); + + const refreshSimulation = React.useCallback(async () => { + await handleSimulation(debouncedAmount ?? 0); + }, [handleSimulation, debouncedAmount]); + + const handleActionSummary = React.useCallback( + (summary?: AccountSummary, result?: SimulationResult) => { + if (summary && depositBank) { + return calculateSummary({ + simulationResult: result ?? undefined, + bank: depositBank, + actionMode: ActionType.Deposit, + accountSummary: summary, + }); + } + }, + [depositBank] + ); + + const actionSummary = React.useMemo(() => { + return handleActionSummary(accountSummary, simulationResult ?? undefined); + }, [accountSummary, simulationResult, handleActionSummary]); + + return { + actionSummary, + refreshSimulation, + }; +} diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/index.ts new file mode 100644 index 0000000000..1536a2d0e0 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/index.ts @@ -0,0 +1 @@ +export * from "./swap-lend-box"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/index.ts new file mode 100644 index 0000000000..aa4f993823 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/index.ts @@ -0,0 +1,16 @@ +import { StoreApi, UseBoundStore } from "zustand"; + +import { createSwapLendBoxStore, SwapLendBoxState } from "./swap-lend-box-store"; + +const useSwapLendBoxGeneralStore: UseBoundStore> = createSwapLendBoxStore(); +const useSwapLendBoxDialogStore: UseBoundStore> = createSwapLendBoxStore(); + +const useSwapLendBoxStore = (isDialog?: boolean): UseBoundStore> => { + if (!isDialog) { + return useSwapLendBoxGeneralStore; + } else { + return useSwapLendBoxDialogStore; + } +}; + +export { useSwapLendBoxStore }; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/swap-lend-box-store.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/swap-lend-box-store.ts new file mode 100644 index 0000000000..0ef2acffcc --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/swap-lend-box-store.ts @@ -0,0 +1,181 @@ +import { create, StateCreator } from "zustand"; + +import { ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; +import { ActionMessageType, ActionTxns, SwapLendActionTxns } from "@mrgnlabs/mrgn-utils"; +import { SimulationResult } from "@mrgnlabs/marginfi-client-v2"; + +interface SwapLendBoxState { + // State + amountRaw: string; + + lendMode: ActionType; + selectedDepositBank: ExtendedBankInfo | null; + selectedSwapBank: ExtendedBankInfo | null; + + simulationResult: SimulationResult | null; + actionTxns: SwapLendActionTxns; + + errorMessage: ActionMessageType | null; + + // Actions + refreshState: (actionMode?: ActionType) => void; + refreshBanks: (banks: ExtendedBankInfo[]) => void; + fetchActionBoxState: (args: { + requestedLendType?: ActionType; + depositBank?: ExtendedBankInfo; + swapBank?: ExtendedBankInfo; + }) => void; + setAmountRaw: (amountRaw: string, maxAmount?: number) => void; + setSimulationResult: (simulationResult: SimulationResult | null) => void; + setActionTxns: (actionTxns: SwapLendActionTxns) => void; + setSelectedDepositBank: (bank: ExtendedBankInfo | null) => void; + setSelectedSwapBank: (bank: ExtendedBankInfo | null) => void; + setErrorMessage: (errorMessage: ActionMessageType | null) => void; +} + +function createSwapLendBoxStore() { + return create(stateCreator); +} + +const initialState = { + amountRaw: "", + simulationResult: null, + lendMode: ActionType.Deposit, + selectedDepositBank: null, + selectedSwapBank: null, + actionTxns: { actionTxn: null, additionalTxns: [], actionQuote: null }, + errorMessage: null, +}; + +const stateCreator: StateCreator = (set, get) => ({ + // State + ...initialState, + + refreshState(lendMode?: ActionType) { + if (lendMode) { + set({ ...initialState, lendMode }); + } else { + set({ ...initialState }); + } + }, + + fetchActionBoxState(args) { + let requestedAction: ActionType; + let requestedDepositBank: ExtendedBankInfo | null = null; + let requestedSwapBank: ExtendedBankInfo | null = null; + const lendMode = get().lendMode; + + if (args.requestedLendType) { + requestedAction = args.requestedLendType; + } else { + requestedAction = initialState.lendMode; + } + + if (args.depositBank) { + requestedDepositBank = args.depositBank; + } else { + requestedDepositBank = null; + } + + if (args.swapBank) { + requestedSwapBank = args.swapBank; + } else { + requestedSwapBank = null; + } + + const depositBank = get().selectedDepositBank; + const swapBank = get().selectedSwapBank; + + const needRefresh = + !depositBank || + !swapBank || + !requestedAction || + lendMode !== requestedAction || + (requestedDepositBank && !requestedDepositBank.address.equals(depositBank.address)) || + (requestedSwapBank && !requestedSwapBank.address.equals(swapBank.address)); + + if (needRefresh) + set({ + ...initialState, + lendMode: requestedAction, + selectedDepositBank: requestedDepositBank, + selectedSwapBank: requestedSwapBank, + }); + }, + + async setAmountRaw(amountRaw, maxAmount) { + if (!maxAmount) { + set({ amountRaw }); + } else { + const strippedAmount = amountRaw.replace(/,/g, ""); + let amount = isNaN(Number.parseFloat(strippedAmount)) ? 0 : Number.parseFloat(strippedAmount); + const numberFormatter = new Intl.NumberFormat("en-US", { maximumFractionDigits: 10 }); + + if (amount && amount > maxAmount) { + amount = maxAmount; + } + + set({ amountRaw: numberFormatter.format(amount) }); + } + }, + + refreshBanks(banks: ExtendedBankInfo[]) { + const depositBank = get().selectedDepositBank; + const swapBank = get().selectedSwapBank; + + if (depositBank) { + const updatedBank = banks.find((v) => v.address.equals(depositBank.address)); + if (updatedBank) { + set({ selectedDepositBank: updatedBank }); + } + } + + if (swapBank) { + const updatedBank = banks.find((v) => v.address.equals(swapBank.address)); + if (updatedBank) { + set({ selectedSwapBank: updatedBank }); + } + } + }, + + setSelectedDepositBank(depositBank) { + const selectedBank = get().selectedDepositBank; + const hasBankChanged = !depositBank || !selectedBank || !depositBank.address.equals(selectedBank.address); + + if (hasBankChanged) { + set({ + selectedDepositBank: depositBank, + // amountRaw: "", // TODO: will this mess up something? + errorMessage: null, + }); + } + }, + + setSelectedSwapBank(swapBank) { + const selectedBank = get().selectedSwapBank; + const hasBankChanged = !swapBank || !selectedBank || !swapBank.address.equals(selectedBank.address); + + if (hasBankChanged) { + set({ + selectedSwapBank: swapBank, + amountRaw: "", + errorMessage: null, + }); + } + }, + + setActionTxns(actionTxns) { + set({ actionTxns }); + }, + + setSimulationResult(simulationResult) { + set({ simulationResult }); + }, + + setErrorMessage(errorMessage) { + set({ errorMessage }); + }, +}); + +export { createSwapLendBoxStore }; +export type { SwapLendBoxState }; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/swap-lend-box.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/swap-lend-box.tsx new file mode 100644 index 0000000000..01c400d6c3 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/swap-lend-box.tsx @@ -0,0 +1,542 @@ +import React from "react"; + +import { + ActiveBankInfo, + ExtendedBankInfo, + ActionType, + AccountSummary, + computeAccountSummary, + DEFAULT_ACCOUNT_SUMMARY, +} from "@mrgnlabs/marginfi-v2-ui-state"; + +import { MarginfiAccountWrapper, MarginfiClient } from "@mrgnlabs/marginfi-client-v2"; +import { + ActionMessageType, + checkSwapLendActionAvailable, + ExecuteSwapLendActionProps, + IndividualFlowError, + MultiStepToastHandle, + PreviousTxn, + SwapLendActionTxns, +} from "@mrgnlabs/mrgn-utils"; + +import { ActionButton, ActionCollateralProgressBar } from "~/components/action-box-v2/components"; +import { useActionAmounts } from "~/components/action-box-v2/hooks"; +import { LSTDialog, LSTDialogVariants } from "~/components/LSTDialog"; +import { ActionMessage } from "~/components"; + +import { ActionSimulationStatus } from "../../components"; +import { SimulationStatus } from "../../utils"; +import { useSwapLendSimulation } from "./hooks"; +import { useActionBoxStore } from "../../store"; +import { useActionContext, HidePoolStats } from "../../contexts"; + +import { handleExecuteSwapLendAction } from "./utils"; +import { useSwapLendBoxStore } from "./store"; +import { ActionInput, Preview } from "./components"; +import { nativeToUi } from "@mrgnlabs/mrgn-common"; +import { Tooltip, TooltipContent, TooltipTrigger } from "~/components/ui/tooltip"; +import { TooltipProvider } from "~/components/ui/tooltip"; +import { IconInfoCircle } from "~/components/ui/icons"; + +export type SwapLendBoxProps = { + nativeSolBalance: number; + connected: boolean; + + marginfiClient: MarginfiClient | null; + selectedAccount: MarginfiAccountWrapper | null; + banks: ExtendedBankInfo[]; + requestedDepositBank?: ExtendedBankInfo; + requestedSwapBank?: ExtendedBankInfo; + accountSummaryArg?: AccountSummary; + isDialog?: boolean; + showAvailableCollateral?: boolean; + showTokenSelection?: boolean; + showTokenSelectionGroups?: boolean; + hidePoolStats?: HidePoolStats; + + onComplete?: (previousTxn: PreviousTxn) => void; + captureEvent?: (event: string, properties?: Record) => void; +}; + +export const SwapLendBox = ({ + nativeSolBalance, + connected, + marginfiClient, + banks, + selectedAccount, + accountSummaryArg, + isDialog, + showTokenSelection, + showAvailableCollateral = true, + showTokenSelectionGroups, + requestedDepositBank, + requestedSwapBank, + onComplete, + captureEvent, + hidePoolStats, +}: SwapLendBoxProps) => { + const [ + amountRaw, + lendMode, + actionTxns, + selectedDepositBank, + selectedSwapBank, + simulationResult, + errorMessage, + + refreshState, + fetchActionBoxState, + setAmountRaw, + setSelectedDepositBank, + setSelectedSwapBank, + refreshBanks, + setSimulationResult, + setActionTxns, + setErrorMessage, + ] = useSwapLendBoxStore(isDialog)((state) => [ + state.amountRaw, + state.lendMode, + state.actionTxns, + state.selectedDepositBank, + state.selectedSwapBank, + state.simulationResult, + state.errorMessage, + + state.refreshState, + state.fetchActionBoxState, + state.setAmountRaw, + state.setSelectedDepositBank, + state.setSelectedSwapBank, + state.refreshBanks, + state.setSimulationResult, + state.setActionTxns, + state.setErrorMessage, + ]); + + const [isTransactionExecuting, setIsTransactionExecuting] = React.useState(false); + const [isSimulating, setIsSimulating] = React.useState<{ + isLoading: boolean; + status: SimulationStatus; + }>({ + isLoading: false, + status: SimulationStatus.IDLE, + }); + const isLoading = React.useMemo( + () => isTransactionExecuting || isSimulating.isLoading, + [isTransactionExecuting, isSimulating.isLoading] + ); + + const { broadcastType, priorityFees } = useActionContext() || { broadcastType: null, priorityFees: null }; + + const accountSummary = React.useMemo(() => { + return ( + accountSummaryArg ?? (selectedAccount ? computeAccountSummary(selectedAccount, banks) : DEFAULT_ACCOUNT_SUMMARY) + ); + }, [accountSummaryArg, selectedAccount, banks]); + + const [setPreviousTxn, setIsActionComplete] = useActionBoxStore((state) => [ + state.setPreviousTxn, + state.setIsActionComplete, + ]); + + const { amount, debouncedAmount, walletAmount, maxAmount } = useActionAmounts({ + amountRaw, + selectedBank: selectedSwapBank ?? selectedDepositBank, + nativeSolBalance, + actionMode: lendMode, + }); + const { actionSummary, refreshSimulation } = useSwapLendSimulation({ + debouncedAmount: debouncedAmount ?? 0, + selectedAccount, + accountSummary, + depositBank: selectedDepositBank ?? null, + swapBank: selectedSwapBank ?? null, + actionTxns, + simulationResult, + setSimulationResult, + setActionTxns, + setErrorMessage, + setIsLoading: setIsSimulating, + marginfiClient, + }); + + const [lstDialogCallback, setLSTDialogCallback] = React.useState<(() => void) | null>(null); + const [additionalActionMessages, setAdditionalActionMessages] = React.useState([]); + + // Cleanup the store when the wallet disconnects + React.useEffect(() => { + if (!connected) { + refreshState(lendMode); + } + }, [refreshState, connected, lendMode]); + + React.useEffect(() => { + return () => { + refreshState(); + }; + }, [refreshState]); + + //clean state + React.useEffect(() => { + if (debouncedAmount === 0 && simulationResult) { + setActionTxns({ actionTxn: null, additionalTxns: [], actionQuote: null }); + setSimulationResult(null); + } + }, [simulationResult, debouncedAmount, setActionTxns, setSimulationResult]); + + React.useEffect(() => { + fetchActionBoxState({ + requestedLendType: ActionType.Deposit, + depositBank: requestedDepositBank, + swapBank: requestedSwapBank, + }); + }, [requestedDepositBank, requestedSwapBank, fetchActionBoxState]); + + React.useEffect(() => { + if (errorMessage && errorMessage.description) { + setAdditionalActionMessages([errorMessage]); + } else { + setAdditionalActionMessages([]); + } + }, [errorMessage]); + + const isDust = React.useMemo( + () => selectedDepositBank?.isActive && selectedDepositBank?.position.isDust, + [selectedDepositBank] + ); + const showCloseBalance = React.useMemo( + () => (lendMode === ActionType.Withdraw && isDust) || false, + [lendMode, isDust] + ); + + const actionMessages = React.useMemo(() => { + setAdditionalActionMessages([]); + return checkSwapLendActionAvailable({ + amount, + connected, + showCloseBalance, + depositBank: selectedDepositBank, + swapBank: selectedSwapBank, + banks, + marginfiAccount: selectedAccount, + nativeSolBalance, + lendMode, + }); + }, [ + amount, + connected, + showCloseBalance, + selectedDepositBank, + selectedSwapBank, + banks, + selectedAccount, + nativeSolBalance, + lendMode, + ]); + + const buttonLabel = React.useMemo(() => (showCloseBalance ? "Close" : lendMode), [showCloseBalance, lendMode]); + + /////////////////////// + // Swap-Lend Actions // + /////////////////////// + const executeAction = async ( + params: ExecuteSwapLendActionProps, + callbacks: { + captureEvent?: (event: string, properties?: Record) => void; + setIsLoading: (loading: boolean) => void; + handleOnComplete: (txnSigs: string[]) => void; + retryCallback: (txns: SwapLendActionTxns, multiStepToast: MultiStepToastHandle) => void; + } + ) => { + const action = async (params: ExecuteSwapLendActionProps) => { + handleExecuteSwapLendAction({ + params, + captureEvent: (event, properties) => { + callbacks.captureEvent && callbacks.captureEvent(event, properties); + }, + setIsLoading: callbacks.setIsLoading, + setIsComplete: callbacks.handleOnComplete, + setError: (error: IndividualFlowError) => { + const toast = error.multiStepToast as MultiStepToastHandle; + const txs = error.actionTxns as SwapLendActionTxns; + const errorMessage = error.message; + let retry = undefined; + if (error.retry && toast && txs) { + retry = () => callbacks.retryCallback(txs, toast); + } + toast?.setFailed(errorMessage, retry); + callbacks.setIsLoading(false); + }, + }); + }; + await action(params); + }; + + const retrySwapLendAction = React.useCallback( + async (params: ExecuteSwapLendActionProps, swapBank: ExtendedBankInfo | null) => { + executeAction(params, { + captureEvent: captureEvent, + setIsLoading: setIsTransactionExecuting, + handleOnComplete: (txnSigs: string[]) => { + setIsActionComplete(true); + setPreviousTxn({ + txn: txnSigs[txnSigs.length - 1] ?? "", + txnType: "SWAP_LEND", + swapLendOptions: { + depositBank: selectedDepositBank as ActiveBankInfo, + swapBank: selectedSwapBank as ActiveBankInfo, + depositAmount: actionTxns?.actionQuote + ? Number( + nativeToUi(actionTxns.actionQuote?.outAmount, selectedDepositBank?.info.rawBank.mintDecimals ?? 9) + ) + : debouncedAmount ?? 0, + swapAmount: actionTxns?.actionQuote ? debouncedAmount ?? 0 : 0, + }, + }); + onComplete && + onComplete({ + txn: txnSigs[txnSigs.length - 1] ?? "", + txnType: "SWAP_LEND", + swapLendOptions: { + depositBank: selectedDepositBank as ActiveBankInfo, + swapBank: selectedSwapBank as ActiveBankInfo, + depositAmount: actionTxns?.actionQuote + ? Number( + nativeToUi(actionTxns.actionQuote?.outAmount, selectedDepositBank?.info.rawBank.mintDecimals ?? 9) + ) + : debouncedAmount ?? 0, + swapAmount: actionTxns?.actionQuote ? debouncedAmount ?? 0 : 0, + }, + }); + }, + retryCallback: (txns: SwapLendActionTxns, multiStepToast: MultiStepToastHandle) => { + retrySwapLendAction({ ...params, actionTxns: txns, multiStepToast }, swapBank); + }, + }); + }, + [captureEvent, onComplete, selectedDepositBank, selectedSwapBank, setIsActionComplete, setPreviousTxn] + ); + + const handleSwapLendAction = React.useCallback( + async (_actionTxns?: SwapLendActionTxns, multiStepToast?: MultiStepToastHandle) => { + if (!actionTxns || !marginfiClient || !debouncedAmount || debouncedAmount === 0) { + console.log({ actionTxns, marginfiClient, selectedSwapBank }); + return; + } + + const params = { + marginfiClient: marginfiClient, + actionTxns: _actionTxns ?? actionTxns, + bank: selectedDepositBank, + amount: debouncedAmount, + nativeSolBalance, + marginfiAccount: selectedAccount, + processOpts: { + ...priorityFees, + broadcastType, + }, + txOpts: {}, + multiStepToast, + actionType: lendMode, + swapBank: selectedSwapBank, + } as ExecuteSwapLendActionProps; + + await executeAction(params, { + captureEvent: captureEvent, + setIsLoading: setIsTransactionExecuting, + handleOnComplete: (txnSigs: string[]) => { + setIsActionComplete(true); + setPreviousTxn({ + txn: txnSigs[txnSigs.length - 1] ?? "", + txnType: "SWAP_LEND", + swapLendOptions: { + depositBank: selectedDepositBank as ActiveBankInfo, + swapBank: selectedSwapBank as ActiveBankInfo, + depositAmount: actionTxns?.actionQuote + ? Number( + nativeToUi(actionTxns.actionQuote?.outAmount, selectedDepositBank?.info.rawBank.mintDecimals ?? 9) + ) + : debouncedAmount, + swapAmount: actionTxns?.actionQuote ? debouncedAmount : 0, + }, + }); + onComplete && + onComplete({ + txn: txnSigs[txnSigs.length - 1] ?? "", + txnType: "SWAP_LEND", + swapLendOptions: { + depositBank: selectedDepositBank as ActiveBankInfo, + swapBank: selectedSwapBank as ActiveBankInfo, + depositAmount: actionTxns?.actionQuote + ? Number( + nativeToUi(actionTxns.actionQuote?.outAmount, selectedDepositBank?.info.rawBank.mintDecimals ?? 9) + ) + : debouncedAmount, + swapAmount: actionTxns?.actionQuote ? debouncedAmount : 0, + }, + }); + }, + retryCallback: (txns: SwapLendActionTxns, multiStepToast: MultiStepToastHandle) => { + retrySwapLendAction({ ...params, actionTxns: txns, multiStepToast }, selectedSwapBank); + }, + }); + }, + [ + actionTxns, + marginfiClient, + selectedSwapBank, + selectedDepositBank, + debouncedAmount, + nativeSolBalance, + selectedAccount, + priorityFees, + broadcastType, + captureEvent, + setIsActionComplete, + setPreviousTxn, + onComplete, + retrySwapLendAction, + ] + ); + + React.useEffect(() => { + if (marginfiClient) { + refreshBanks(banks); + } + }, [marginfiClient, banks, refreshBanks]); + + return ( + <> +
+ Deposit + +
+ + {!requestedDepositBank && ( +
+ + + + + ✨ Collateral Swap + + + +

Swap your prefered token and deposit it into any pool.

+
+
+
+ bank.info.rawBank.mint.toBase58() !== selectedSwapBank?.info.rawBank.mint.toBase58() + )} + nativeSolBalance={nativeSolBalance} + walletAmount={walletAmount} + amountRaw={ + isNaN(Number(actionTxns?.actionQuote?.outAmount)) + ? "" + : nativeToUi( + Number(actionTxns?.actionQuote?.outAmount), + selectedDepositBank?.info.rawBank.mintDecimals ?? 9 + ).toString() + } // clean + amount={ + isNaN(Number(actionTxns?.actionQuote?.outAmount)) + ? 0 + : nativeToUi( + Number(actionTxns?.actionQuote?.outAmount), + selectedDepositBank?.info.rawBank.mintDecimals ?? 9 + ) + } // clean + maxAmount={maxAmount} + connected={connected} + selectedBank={selectedDepositBank ?? null} + lendMode={lendMode} + isDialog={isDialog} + showTokenSelection={showTokenSelection} + showTokenSelectionGroups={showTokenSelectionGroups} + setAmountRaw={setAmountRaw} + setSelectedBank={setSelectedDepositBank} + isInputDisabled={true} + /> +
+ )} + + {additionalActionMessages.concat(actionMessages).map( + (actionMessage, idx) => + actionMessage.description && ( +
+ +
+ ) + )} + + {showAvailableCollateral && ( +
+ +
+ )} + +
+ value.isEnabled === false).length + } + connected={connected} + // showCloseBalance={showCloseBalance} + handleAction={() => { + handleSwapLendAction(); + }} + buttonLabel={buttonLabel} + /> +
+ + 0} + isActive={selectedDepositBank && amount > 0 ? true : false} + /> + + + + { + if (lstDialogCallback) { + lstDialogCallback(); + setLSTDialogCallback(null); + } + }} + banks={banks} + /> + + ); +}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/index.ts new file mode 100644 index 0000000000..5901c7b75f --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/index.ts @@ -0,0 +1,2 @@ +export * from "./swap-lend-action.utils"; +export * from "./swap-lend-simulation.utils"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts new file mode 100644 index 0000000000..adaecd1b2e --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts @@ -0,0 +1,316 @@ +import { BN } from "@coral-xyz/anchor"; +import BigNumber from "bignumber.js"; +import { createJupiterApiClient, QuoteResponse } from "@jup-ag/api"; +import { + BalanceRaw, + MarginfiAccount, + MarginfiAccountRaw, + MarginfiAccountWrapper, + MarginfiClient, + SimulationResult, +} from "@mrgnlabs/marginfi-client-v2"; +import { AccountSummary, ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; +import { + addTransactionMetadata, + bigNumberToWrappedI80F48, + LUT_PROGRAM_AUTHORITY_INDEX, + nativeToUi, + SolanaTransaction, + uiToNative, +} from "@mrgnlabs/mrgn-common"; +import { + ActionMessageType, + ActionTxns, + deserializeInstruction, + executeSwapLendAction, + getAdressLookupTableAccounts, + getSwapQuoteWithRetry, + handleSimulationError, + IndividualFlowError, + MarginfiActionParams, + STATIC_SIMULATION_ERRORS, + SwapLendActionTxns, +} from "@mrgnlabs/mrgn-utils"; +import { Keypair, PublicKey, Transaction, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; +import { ActionSummary, CalculatePreviewProps, SimulatedActionPreview } from "../../lend-box/utils"; +import { + ActionPreview, + simulatedCollateral, + simulatedHealthFactor, + simulatedPositionSize, +} from "~/components/action-box-v2/utils"; + +export interface GenerateSwapLendTxnsProps { + marginfiAccount: MarginfiAccountWrapper; + depositBank: ExtendedBankInfo; + swapBank?: ExtendedBankInfo | null; + amount: number; + marginfiClient: MarginfiClient; + slippageBps: number; +} + +export async function generateSwapLendTxns( + props: GenerateSwapLendTxnsProps +): Promise { + let swapTx: { quote?: QuoteResponse; tx?: SolanaTransaction; error?: ActionMessageType } | undefined; + + if (props.swapBank && props.swapBank.meta.tokenSymbol !== props.depositBank.meta.tokenSymbol) { + console.log("Creating Quote swap transaction..."); + try { + swapTx = await createSwapTx(props); + if (swapTx.error) { + console.error("Error creating swap transaction:", swapTx.error); + return swapTx.error; + } else { + if (!swapTx.tx || !swapTx.quote) { + return STATIC_SIMULATION_ERRORS.CREATE_SWAP_FAILED; + } + } + } catch (error) { + console.error("Error creating swap transaction:", error); + return STATIC_SIMULATION_ERRORS.CREATE_SWAP_FAILED; + } + } + + // Marginfi Account + let hasMarginfiAccount = !!props.marginfiAccount; + const hasBalances = props.marginfiAccount?.activeBalances?.length ?? 0 > 0; + + if (hasMarginfiAccount && !hasBalances && props.marginfiAccount) { + const accountInfo = await props.marginfiClient.provider.connection.getAccountInfo(props.marginfiAccount.address); + hasMarginfiAccount = accountInfo !== null; + } + + let accountCreationTx: SolanaTransaction[] = []; + let finalAccount: MarginfiAccountWrapper | null = props.marginfiAccount; + if (!hasMarginfiAccount) { + const { account, tx } = await createMarginfiAccountTx(props); + finalAccount = account; + accountCreationTx.push(tx); + } + + let finalDepositAmount = props.amount; + + if (props.swapBank && !swapTx?.quote) { + return STATIC_SIMULATION_ERRORS.CREATE_SWAP_FAILED; + } else if (props.swapBank && swapTx?.quote) { + finalDepositAmount = Number(nativeToUi(swapTx?.quote?.outAmount, props.depositBank.info.state.mintDecimals)); + } + + const depositTx = await props.marginfiAccount.makeDepositTx(finalDepositAmount, props.depositBank.address); + + return { + actionTxn: depositTx, + additionalTxns: [...(swapTx?.tx ? [swapTx.tx] : [])], + actionQuote: swapTx?.quote ?? null, + }; +} + +export async function createSwapTx(props: GenerateSwapLendTxnsProps) { + if (!props.swapBank) { + console.error("Swap bank is required"); + throw new Error("Swap bank is required"); + } + + try { + const jupiterQuoteApi = createJupiterApiClient(); + + const swapQuote = await getSwapQuoteWithRetry({ + swapMode: "ExactIn", + amount: uiToNative(props.amount, props.swapBank.info.state.mintDecimals).toNumber(), + inputMint: props.swapBank.info.state.mint.toBase58(), + outputMint: props.depositBank.info.state.mint.toBase58(), + slippageBps: props.slippageBps, + }); + + if (!swapQuote) { + return { error: STATIC_SIMULATION_ERRORS.CREATE_SWAP_FAILED }; + } + + const { + computeBudgetInstructions, + swapInstruction, + setupInstructions, + cleanupInstruction, + addressLookupTableAddresses, + } = await jupiterQuoteApi.swapInstructionsPost({ + swapRequest: { + quoteResponse: swapQuote, + userPublicKey: props.marginfiAccount.authority.toBase58(), + programAuthorityId: LUT_PROGRAM_AUTHORITY_INDEX, + }, + }); + + const swapIx = deserializeInstruction(swapInstruction); + const setupInstructionsIxs = setupInstructions.map((value) => deserializeInstruction(value)); + const cuInstructionsIxs = computeBudgetInstructions.map((value) => deserializeInstruction(value)); + // const cleanupInstructionIx = deserializeInstruction(cleanupInstruction); + const addressLookupAccounts = await getAdressLookupTableAccounts( + props.marginfiClient.provider.connection, + addressLookupTableAddresses + ); + const finalBlockhash = (await props.marginfiClient.provider.connection.getLatestBlockhash()).blockhash; + + const swapMessage = new TransactionMessage({ + payerKey: props.marginfiAccount.authority, + recentBlockhash: finalBlockhash, + instructions: [...cuInstructionsIxs, ...setupInstructionsIxs, swapIx], + }); + const swapTx = addTransactionMetadata( + new VersionedTransaction(swapMessage.compileToV0Message(addressLookupAccounts)), + { + addressLookupTables: addressLookupAccounts, + type: "SWAP", + } + ); + + return { quote: swapQuote, tx: swapTx }; + } catch (error) { + console.error("Error creating swap transaction:", error); + return { error: STATIC_SIMULATION_ERRORS.CREATE_SWAP_FAILED }; + } +} + +async function createMarginfiAccountTx( + props: GenerateSwapLendTxnsProps +): Promise<{ account: MarginfiAccountWrapper; tx: SolanaTransaction }> { + // if no marginfi account, we need to create one + console.log("Creating new marginfi account transaction..."); + const authority = props.marginfiAccount?.authority ?? props.marginfiClient.provider.publicKey; + + const marginfiAccountKeypair = Keypair.generate(); + + const dummyWrappedI80F48 = bigNumberToWrappedI80F48(new BigNumber(0)); + + const dummyBalances: BalanceRaw[] = Array(15).fill({ + active: false, + bankPk: new PublicKey("11111111111111111111111111111111"), + assetShares: dummyWrappedI80F48, + liabilityShares: dummyWrappedI80F48, + emissionsOutstanding: dummyWrappedI80F48, + lastUpdate: new BN(0), + }); + + const rawAccount: MarginfiAccountRaw = { + group: props.marginfiClient.group.address, + authority: authority, + lendingAccount: { balances: dummyBalances }, + accountFlags: new BN([0, 0, 0]), + }; + + const account = new MarginfiAccount(marginfiAccountKeypair.publicKey, rawAccount); + + const wrappedAccount = new MarginfiAccountWrapper(marginfiAccountKeypair.publicKey, props.marginfiClient, account); + + return { + account: wrappedAccount, + tx: await props.marginfiClient.createMarginfiAccountTx({ accountKeypair: marginfiAccountKeypair }), + }; +} + +export interface SimulateActionProps { + txns: (VersionedTransaction | Transaction)[]; + bank: ExtendedBankInfo; + account: MarginfiAccountWrapper; +} + +export const getSimulationResult = async (props: SimulateActionProps) => { + let actionMethod: ActionMessageType | undefined = undefined; + let simulationResult: SimulationResult | null = null; + + try { + simulationResult = await simulateFlashLoan(props); + } catch (error: any) { + const actionString = "Looping"; + actionMethod = handleSimulationError(error, props.bank, true, actionString); + } + + return { simulationResult, actionMethod }; +}; + +async function simulateFlashLoan({ account, bank, txns }: SimulateActionProps) { + let simulationResult: SimulationResult; + + if (txns.length > 0) { + simulationResult = await account.simulateBorrowLendTransaction(txns, [bank.address]); + return simulationResult; + } else { + console.error("Failed to simulate flashloan"); + throw new Error("Failed to simulate flashloan"); + } +} + +export function calculateSummary({ + simulationResult, + bank, + actionMode, + accountSummary, +}: CalculatePreviewProps): ActionSummary { + let simulationPreview: SimulatedActionPreview | null = null; + + if (simulationResult) { + simulationPreview = calculateSimulatedActionPreview(simulationResult, bank); + } + + const actionPreview = calculateActionPreview(bank, actionMode, accountSummary); + + return { + actionPreview, + simulationPreview, + } as ActionSummary; +} + +function calculateActionPreview( + bank: ExtendedBankInfo, + actionMode: ActionType, + accountSummary: AccountSummary +): ActionPreview { + const isLending = [ActionType.Deposit, ActionType.Withdraw].includes(actionMode); + const positionAmount = bank?.isActive ? bank.position.amount : 0; + const health = accountSummary.balance && accountSummary.healthFactor ? accountSummary.healthFactor : 1; + const liquidationPrice = + bank.isActive && bank.position.liquidationPrice && bank.position.liquidationPrice > 0.01 + ? bank.position.liquidationPrice + : null; + + const poolSize = isLending + ? bank.info.state.totalDeposits + : Math.max( + 0, + Math.min(bank.info.state.totalDeposits, bank.info.rawBank.config.borrowLimit.toNumber()) - + bank.info.state.totalBorrows + ); + const bankCap = nativeToUi( + isLending ? bank.info.rawBank.config.depositLimit : bank.info.rawBank.config.borrowLimit, + bank.info.state.mintDecimals + ); + + return { + positionAmount, + health, + liquidationPrice, + poolSize, + bankCap, + } as ActionPreview; +} + +function calculateSimulatedActionPreview( + simulationResult: SimulationResult, + bank: ExtendedBankInfo +): SimulatedActionPreview { + const health = simulatedHealthFactor(simulationResult); + const positionAmount = simulatedPositionSize(simulationResult, bank); + const availableCollateral = simulatedCollateral(simulationResult); + + const liquidationPrice = simulationResult.marginfiAccount.computeLiquidationPriceForBank(bank.address); + const { lendingRate, borrowingRate } = simulationResult.banks.get(bank.address.toBase58())!.computeInterestRates(); + + return { + health, + liquidationPrice, + depositRate: lendingRate.toNumber(), + borrowRate: borrowingRate.toNumber(), + positionAmount, + availableCollateral, + }; +} diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-simulation.utils.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-simulation.utils.ts new file mode 100644 index 0000000000..3d9602dd51 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-simulation.utils.ts @@ -0,0 +1,48 @@ +import { v4 as uuidv4 } from "uuid"; + +import { + executeSwapLendAction, + ExecuteSwapLendActionProps, + IndividualFlowError, + MarginfiActionParams, +} from "@mrgnlabs/mrgn-utils"; +import { ExecuteActionsCallbackProps } from "~/components/action-box-v2/types/actions.types"; +import { ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; + +interface _ExecuteSwapLendActionProps extends ExecuteActionsCallbackProps { + params: ExecuteSwapLendActionProps; +} + +export const handleExecuteSwapLendAction = async ({ + params, + captureEvent, + setIsLoading, + setIsComplete, + setError, +}: _ExecuteSwapLendActionProps) => { + try { + setIsLoading(true); + const attemptUuid = uuidv4(); + captureEvent(`user_swap_lend_initiate`, { + uuid: attemptUuid, + depositToken: params.bank.meta.tokenSymbol, + swapToken: params.swapBank ? params.swapBank.meta.tokenSymbol : "NO_SWAP", + amount: params.amount, + }); + const txnSig = await executeSwapLendAction({ ...params, swapBank: params.swapBank }); + + setIsLoading(false); + if (txnSig) { + setIsComplete(txnSig ?? ""); + captureEvent(`user_swap_lend`, { + uuid: attemptUuid, + txn: txnSig, + depositToken: params.bank.meta.tokenSymbol, + swapToken: params.swapBank ? params.swapBank.meta.tokenSymbol : "NO_SWAP", + amount: params.amount, + }); + } + } catch (error) { + setError(error as IndividualFlowError); + } +}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/components/action-collateral-progress-bar/collateral-progress-bar.tsx b/packages/mrgn-ui/src/components/action-box-v2/components/action-collateral-progress-bar/collateral-progress-bar.tsx new file mode 100644 index 0000000000..99f57667bd --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/components/action-collateral-progress-bar/collateral-progress-bar.tsx @@ -0,0 +1,48 @@ +import React from "react"; + +import { MarginfiAccountWrapper, MarginRequirementType } from "@mrgnlabs/marginfi-client-v2"; + +import { ActionSummary } from "~/components/action-box-v2/utils"; +import { ActionProgressBar } from "~/components/action-box-v2/components"; + +type ActionCollateralProgressBarProps = { + selectedAccount: MarginfiAccountWrapper | null; + actionSummary?: ActionSummary; +}; + +export const ActionCollateralProgressBar = ({ selectedAccount, actionSummary }: ActionCollateralProgressBarProps) => { + const availableCollateral = React.useMemo(() => { + if (!selectedAccount) return null; + + if (actionSummary?.simulationPreview?.availableCollateral) { + return actionSummary.simulationPreview.availableCollateral; + } + + const collateralAmount = selectedAccount?.computeFreeCollateral().toNumber(); + const collateralRatio = + collateralAmount / selectedAccount.computeHealthComponents(MarginRequirementType.Initial).assets.toNumber(); + + return { + amount: collateralAmount, + ratio: collateralRatio, + }; + }, [actionSummary, selectedAccount]); + + return ( + <> + {availableCollateral && ( + +

Available collateral is the USD value of your collateral not actively backing a loan.

+

It can be used to open additional borrows or withdraw part of your collateral.

+
+ } + /> + )} + + ); +}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/components/action-collateral-progress-bar/index.ts b/packages/mrgn-ui/src/components/action-box-v2/components/action-collateral-progress-bar/index.ts new file mode 100644 index 0000000000..71efc24801 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/components/action-collateral-progress-bar/index.ts @@ -0,0 +1 @@ +export * from "./collateral-progress-bar"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/components/index.ts index d0390ef9aa..cc287331f9 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/components/index.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/components/index.ts @@ -5,3 +5,4 @@ export * from "./action-button"; export * from "./action-wrappers"; export * from "./action-settings-button"; export * from "./action-simulation-status"; +export * from "./action-collateral-progress-bar"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/types/action-box.types.ts b/packages/mrgn-ui/src/components/action-box-v2/types/action-box.types.ts index 6d39dff892..7271faf14b 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/types/action-box.types.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/types/action-box.types.ts @@ -1,4 +1,10 @@ -import { LendBoxProps, LoopBoxProps, RepayCollatBoxProps, StakeBoxProps } from "~/components/action-box-v2/actions"; +import { + LendBoxProps, + LoopBoxProps, + RepayCollatBoxProps, + StakeBoxProps, + SwapLendBoxProps, +} from "~/components/action-box-v2/actions"; import { ActionDialogProps } from "~/components/action-box-v2/components"; type ActionBoxDialogProps = { @@ -57,12 +63,21 @@ interface RequiredLoopBoxProps interface RequiredStakeBoxProps extends Pick {} +interface RequiredSwapLendBoxProps + extends Pick< + SwapLendBoxProps, + "onComplete" | "captureEvent" | "connected" | "showTokenSelection" | "requestedDepositBank" + > {} + interface ActionBoxComponent extends React.FC { Lend: React.FC; BorrowLend: React.FC; Repay: React.FC; Loop: React.FC; Stake: React.FC; + SwapLend: React.FC< + ActionBoxProps & { swapLendProps: SwapLendBoxProps | RequiredSwapLendBoxProps; useProvider?: boolean } + >; } export type { @@ -73,5 +88,6 @@ export type { RequiredRepayBoxProps, RequiredLoopBoxProps, RequiredStakeBoxProps, + RequiredSwapLendBoxProps, }; export { isDialogWrapperProps }; diff --git a/packages/mrgn-utils/src/action-message.utils.ts b/packages/mrgn-utils/src/action-message.utils.ts index 55718c740f..2c86843d44 100644 --- a/packages/mrgn-utils/src/action-message.utils.ts +++ b/packages/mrgn-utils/src/action-message.utils.ts @@ -227,3 +227,42 @@ export function checkStakeActionAvailable({ return checks; } + +interface CheckSwapLendActionAvailableProps { + amount: number | null; + connected: boolean; + nativeSolBalance: number; + showCloseBalance?: boolean; + depositBank: ExtendedBankInfo | null; + swapBank: ExtendedBankInfo | null; + banks: ExtendedBankInfo[]; + marginfiAccount: MarginfiAccountWrapper | null; + lendMode: ActionType; +} + +export function checkSwapLendActionAvailable({ + amount, + nativeSolBalance, + connected, + showCloseBalance, + depositBank, + swapBank, + banks, + marginfiAccount, + lendMode, +}: CheckSwapLendActionAvailableProps): ActionMessageType[] { + let checks: ActionMessageType[] = []; + + const requiredCheck = getRequiredCheck(connected, depositBank); + if (requiredCheck) return [requiredCheck]; + + const generalChecks = getGeneralChecks(amount ?? 0, showCloseBalance); + if (generalChecks) checks.push(...generalChecks); + + if (checks.length === 0) + checks.push({ + isEnabled: true, + }); + + return checks; +} diff --git a/packages/mrgn-utils/src/actions/actions.ts b/packages/mrgn-utils/src/actions/actions.ts index 6a8f3ea542..cf3aa6a713 100644 --- a/packages/mrgn-utils/src/actions/actions.ts +++ b/packages/mrgn-utils/src/actions/actions.ts @@ -1,7 +1,7 @@ import { WalletContextState } from "@solana/wallet-adapter-react"; import { MarginfiClient, ProcessTransactionsClientOpts } from "@mrgnlabs/marginfi-client-v2"; -import { FEE_MARGIN, ActionType } from "@mrgnlabs/marginfi-v2-ui-state"; +import { FEE_MARGIN, ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; import { TransactionOptions, WSOL_MINT } from "@mrgnlabs/mrgn-common"; import { MultiStepToastHandle, showErrorToast } from "../toasts"; @@ -13,6 +13,7 @@ import { LoopingProps, TradeActionTxns, ClosePositionActionTxns, + SwapLendActionTxns, } from "./types"; import { WalletContextStateOverride } from "../wallet"; import { @@ -29,6 +30,7 @@ import { mintLstToken, mintLstStakeToStake, closePosition, + swapLend, } from "./individualFlows"; import { STATIC_SIMULATION_ERRORS } from "../errors"; @@ -152,6 +154,19 @@ export async function executeTradeAction(params: ExecuteTradeActionProps) { return txnSig; } +export interface ExecuteSwapLendActionProps extends MarginfiActionParams { + swapBank: ExtendedBankInfo | null; + actionTxns: SwapLendActionTxns; +} + +export async function executeSwapLendAction(params: ExecuteSwapLendActionProps) { + let txnSig: string[] | undefined; + + txnSig = await swapLend(params); + + return txnSig; +} + export interface ExecuteClosePositionActionProps { marginfiClient: MarginfiClient; actionTxns: ClosePositionActionTxns; diff --git a/packages/mrgn-utils/src/actions/individualFlows.ts b/packages/mrgn-utils/src/actions/individualFlows.ts index ac9ed4e116..667fe14b09 100644 --- a/packages/mrgn-utils/src/actions/individualFlows.ts +++ b/packages/mrgn-utils/src/actions/individualFlows.ts @@ -50,6 +50,7 @@ import { import { captureSentryException } from "../sentry.utils"; import { loopingBuilder, repayWithCollatBuilder } from "./flashloans"; import { STATIC_SIMULATION_ERRORS } from "../errors"; +import { ExecuteSwapLendActionProps } from "./actions"; //-----------------------// // Local utils functions // @@ -333,6 +334,67 @@ export async function deposit({ } } +export async function swapLend({ + marginfiAccount, + marginfiClient, + bank, + amount, + actionTxns, + processOpts, + txOpts, + multiStepToast, + swapBank, +}: ExecuteSwapLendActionProps) { + console.log(marginfiAccount); + if (!multiStepToast) { + const steps = getSteps(actionTxns); + + multiStepToast = new MultiStepToastHandle("Deposit", [ + ...steps, + { label: `Depositing ${amount} ${swapBank ? swapBank.meta.tokenSymbol : bank.meta.tokenSymbol}` }, + ]); + multiStepToast.start(); + } else { + multiStepToast.resetAndStart(); + } + + try { + let sigs: string[] = []; + if (actionTxns?.actionTxn && marginfiClient) { + sigs = await marginfiClient.processTransactions( + [...actionTxns.additionalTxns, actionTxns.actionTxn], + { + ...processOpts, + callback: (index, success, sig, stepsToAdvance) => + success && multiStepToast.setSuccessAndNext(stepsToAdvance, sig, composeExplorerUrl(sig)), + }, + txOpts + ); + } else { + throw new Error("Marginfi account not ready."); + } + + multiStepToast.setSuccess(sigs[sigs.length - 1], composeExplorerUrl(sigs[sigs.length - 1])); + return sigs; + } catch (error: any) { + console.log(`Error while Depositing`); + console.log(error); + if (!(error instanceof ProcessTransactionError || error instanceof SolanaJSONRPCError)) { + captureSentryException(error, JSON.stringify(error), { + action: "deposit", + wallet: marginfiAccount?.authority?.toBase58(), + bank: bank.meta.tokenSymbol, + }); + } + + handleIndividualFlowError({ + error, + actionTxns, + multiStepToast, + }); + } +} + export async function borrow({ marginfiClient, marginfiAccount, diff --git a/packages/mrgn-utils/src/actions/types.ts b/packages/mrgn-utils/src/actions/types.ts index 2267eafa6a..188da7cbef 100644 --- a/packages/mrgn-utils/src/actions/types.ts +++ b/packages/mrgn-utils/src/actions/types.ts @@ -96,6 +96,10 @@ export interface StakeActionTxns extends ActionTxns { lastValidBlockHeight?: number; } // TOOD: implement this as actionSummary type +export interface SwapLendActionTxns extends ActionTxns { + actionQuote: QuoteResponse | null; +} + export interface CalculateLoopingProps extends Pick { targetLeverage: number; diff --git a/packages/mrgn-utils/src/errors.ts b/packages/mrgn-utils/src/errors.ts index ced41eb088..83edb478b4 100644 --- a/packages/mrgn-utils/src/errors.ts +++ b/packages/mrgn-utils/src/errors.ts @@ -205,6 +205,20 @@ export const STATIC_SIMULATION_ERRORS: { [key: string]: ActionMessageType } = { "Transaction is not ready to execute yet. Please ensure it has been fully simulated before proceeding.", code: 134, }, + DEPOSIT_FAILED: { + isEnabled: false, + actionMethod: "WARNING", + description: "Failed to deposit funds. Please try again.", + retry: true, + code: 135, + }, + CREATE_SWAP_FAILED: { + isEnabled: false, + actionMethod: "WARNING", + description: "Failed to fetch Jupiter quote. Please try again.", + retry: true, + code: 136, + }, }; const createRepayCollatFailedCheck = (tokenSymbol?: string): ActionMessageType => ({ diff --git a/packages/mrgn-utils/src/types.ts b/packages/mrgn-utils/src/types.ts index a4c79b94a5..d2186a0e18 100644 --- a/packages/mrgn-utils/src/types.ts +++ b/packages/mrgn-utils/src/types.ts @@ -67,6 +67,17 @@ interface PreviousTxnPositionClosed { }; } +interface PreviousTxnSwapLend { + txn: string; + txnType: "SWAP_LEND"; + swapLendOptions: { + depositBank: ActiveBankInfo; + swapBank: ActiveBankInfo; + depositAmount: number; + swapAmount: number; + }; +} + export interface PreviousTxnTradingOptions { initDepositAmount: string; depositAmount: number; @@ -90,4 +101,5 @@ export type PreviousTxn = | PreviousTxnLending | PreviousTxnTrading | PreviousTxnPositionClosed - | PreviousTxnStake; + | PreviousTxnStake + | PreviousTxnSwapLend; From 09a4ee24171ad398eb076fe123bfc32397381293 Mon Sep 17 00:00:00 2001 From: borcherd Date: Mon, 13 Jan 2025 15:56:17 +0100 Subject: [PATCH 02/11] chore: fix redirect on p/img click --- .../PositionList/PositionListItem.tsx | 5 +++- .../components/bank-list/bank-list.tsx | 1 + .../action-input/bank-item/bank-item.tsx | 26 ++++++++++++++----- .../selected-bank-item/selected-bank-item.tsx | 14 ++++++++-- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionList/PositionListItem.tsx b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionList/PositionListItem.tsx index 20b4fba75f..c10829e4bf 100644 --- a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionList/PositionListItem.tsx +++ b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionList/PositionListItem.tsx @@ -42,9 +42,12 @@ export const PositionListItem = ({ arenaPool }: props) => { e.target instanceof HTMLAnchorElement || e.target instanceof SVGElement || (e.target instanceof Element && - (e.target.hasAttribute("data-state") || e.target.closest("[data-command-item]"))) + (e.target.hasAttribute("data-state") || + e.target.closest("[data-command-item]") || + e.target.closest("[data-router-ignore]"))) ) return; + router.push(`/trade/${arenaPool.groupPk.toBase58()}`); }} > diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx index 4592cf66ec..34650c6f32 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx @@ -151,6 +151,7 @@ export const BankList = ({ className="cursor-pointer h-[55px] px-3 font-medium flex items-center justify-between gap-2 data-[selected=true]:bg-mfi-action-box-accent data-[selected=true]:text-mfi-action-box-accent-foreground" > -
- {bank.meta.tokenName} +
+ {bank.meta.tokenName}
-

+

{bank.meta.tokenSymbol} {!available && (currently unavailable)}

{lendingMode && (

0) || showBalanceOverride) && (

-

+

{dynamicNumeralFormatter(balance, { tokenPrice: bank.info.oraclePrice.priceRealtime.price.toNumber() })}

-

{balancePrice}

+

+ {balancePrice} +

)} {isRepay && openPosition > 0 && (
-

+

{dynamicNumeralFormatter(openPosition, { tokenPrice: bank.info.oraclePrice.priceRealtime.price.toNumber(), })}

-

{openPositionPrice}

+

+ {openPositionPrice} +

)} diff --git a/packages/mrgn-ui/src/components/action-box-v2/components/action-input/selected-bank-item/selected-bank-item.tsx b/packages/mrgn-ui/src/components/action-box-v2/components/action-input/selected-bank-item/selected-bank-item.tsx index 1a0446e1b2..33f25a1885 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/components/action-input/selected-bank-item/selected-bank-item.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/components/action-input/selected-bank-item/selected-bank-item.tsx @@ -14,11 +14,21 @@ type SelectedBankItemProps = { export const SelectedBankItem = ({ rate, bank, lendingMode }: SelectedBankItemProps) => { return ( <> - {bank.meta.tokenName} + {bank.meta.tokenName}
-

{bank.meta.tokenSymbol}

+

+ {bank.meta.tokenSymbol} +

{lendingMode && rate && (

Date: Tue, 14 Jan 2025 09:24:37 +0100 Subject: [PATCH 03/11] chore: (re)move redirect on row click --- .../PositionList/PositionListItem.tsx | 26 +++++-------------- .../components/bank-list/bank-list.tsx | 1 - .../action-input/bank-item/bank-item.tsx | 26 +++++-------------- .../selected-bank-item/selected-bank-item.tsx | 14 ++-------- 4 files changed, 16 insertions(+), 51 deletions(-) diff --git a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionList/PositionListItem.tsx b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionList/PositionListItem.tsx index c10829e4bf..76699b93a8 100644 --- a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionList/PositionListItem.tsx +++ b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionList/PositionListItem.tsx @@ -34,23 +34,7 @@ export const PositionListItem = ({ arenaPool }: props) => { }); return ( - { - if ( - e.target instanceof HTMLButtonElement || - e.target instanceof HTMLAnchorElement || - e.target instanceof SVGElement || - (e.target instanceof Element && - (e.target.hasAttribute("data-state") || - e.target.closest("[data-command-item]") || - e.target.closest("[data-router-ignore]"))) - ) - return; - - router.push(`/trade/${arenaPool.groupPk.toBase58()}`); - }} - > + {arenaPool.status === GroupStatus.LONG ? ( long @@ -58,8 +42,12 @@ export const PositionListItem = ({ arenaPool }: props) => { short )} - -

+ { + router.push(`/trade/${arenaPool.groupPk.toBase58()}`); + }} + > +
{/* eslint-disable-next-line @next/next/no-img-element */} -
- {bank.meta.tokenName} +
+ {bank.meta.tokenName}
-

+

{bank.meta.tokenSymbol} {!available && (currently unavailable)}

{lendingMode && (

0) || showBalanceOverride) && (

-

+

{dynamicNumeralFormatter(balance, { tokenPrice: bank.info.oraclePrice.priceRealtime.price.toNumber() })}

-

- {balancePrice} -

+

{balancePrice}

)} {isRepay && openPosition > 0 && (
-

+

{dynamicNumeralFormatter(openPosition, { tokenPrice: bank.info.oraclePrice.priceRealtime.price.toNumber(), })}

-

- {openPositionPrice} -

+

{openPositionPrice}

)} diff --git a/packages/mrgn-ui/src/components/action-box-v2/components/action-input/selected-bank-item/selected-bank-item.tsx b/packages/mrgn-ui/src/components/action-box-v2/components/action-input/selected-bank-item/selected-bank-item.tsx index 33f25a1885..1a0446e1b2 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/components/action-input/selected-bank-item/selected-bank-item.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/components/action-input/selected-bank-item/selected-bank-item.tsx @@ -14,21 +14,11 @@ type SelectedBankItemProps = { export const SelectedBankItem = ({ rate, bank, lendingMode }: SelectedBankItemProps) => { return ( <> - {bank.meta.tokenName} + {bank.meta.tokenName}
-

- {bank.meta.tokenSymbol} -

+

{bank.meta.tokenSymbol}

{lendingMode && rate && (

Date: Tue, 14 Jan 2025 09:32:53 +0100 Subject: [PATCH 04/11] chore: correct input amount after swap --- .../swap-lend-box/utils/swap-lend-action.utils.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts index adaecd1b2e..a6736745b6 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts @@ -58,6 +58,7 @@ export async function generateSwapLendTxns( console.log("Creating Quote swap transaction..."); try { swapTx = await createSwapTx(props); + console.log("swapTx", swapTx); if (swapTx.error) { console.error("Error creating swap transaction:", swapTx.error); return swapTx.error; @@ -90,11 +91,18 @@ export async function generateSwapLendTxns( } let finalDepositAmount = props.amount; + console.log("finalDepositAmount", finalDepositAmount); if (props.swapBank && !swapTx?.quote) { return STATIC_SIMULATION_ERRORS.CREATE_SWAP_FAILED; } else if (props.swapBank && swapTx?.quote) { - finalDepositAmount = Number(nativeToUi(swapTx?.quote?.outAmount, props.depositBank.info.state.mintDecimals)); + finalDepositAmount = Number( + nativeToUi( + swapTx?.quote?.otherAmountThreshold ?? swapTx?.quote?.outAmount, + props.depositBank.info.state.mintDecimals + ) + ); + console.log("finalDepositAmount 2", finalDepositAmount); } const depositTx = await props.marginfiAccount.makeDepositTx(finalDepositAmount, props.depositBank.address); From c76a8ec9b22210c9d6d7412a74a43651f48a2ec4 Mon Sep 17 00:00:00 2001 From: borcherd Date: Tue, 14 Jan 2025 10:37:00 +0100 Subject: [PATCH 05/11] chore: swap-lend page mrgnlend --- apps/marginfi-v2-ui/src/pages/swap-lend.tsx | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 apps/marginfi-v2-ui/src/pages/swap-lend.tsx diff --git a/apps/marginfi-v2-ui/src/pages/swap-lend.tsx b/apps/marginfi-v2-ui/src/pages/swap-lend.tsx new file mode 100644 index 0000000000..d1dc76be3f --- /dev/null +++ b/apps/marginfi-v2-ui/src/pages/swap-lend.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { ActionBox as ActionBoxV2 } from "@mrgnlabs/mrgn-ui"; +import { capture } from "@mrgnlabs/mrgn-utils"; +import { useMrgnlendStore } from "~/store"; + +import { PageHeading } from "~/components/common/PageHeading"; + +import { Loader } from "~/components/ui/loader"; +import { useWallet } from "~/components/wallet-v2"; + +export default function SwapLendPage() { + const [initialized] = useMrgnlendStore((state) => [state.initialized, state.extendedBankInfos]); + const { connected } = useWallet(); + + return ( + <> + {!initialized && } + + {initialized && ( +

+ Swap and lend your assets to maximize yield.

} /> + { + capture(event, properties); + }, + }} + /> +
+ )} + + ); +} From 109f6bcaff1714538ef461fb6f327396e49932de Mon Sep 17 00:00:00 2001 From: borcherd Date: Tue, 14 Jan 2025 13:46:36 +0100 Subject: [PATCH 06/11] chore: remove duplicate component --- .../components/collateral/collateral.tsx | 48 ------------------- .../lend-box/components/collateral/index.ts | 1 - .../actions/lend-box/components/index.ts | 1 - .../actions/lend-box/lend-box.tsx | 6 +-- .../components/collateral/collateral.tsx | 48 ------------------- .../components/collateral/index.ts | 1 - .../repay-collat-box/components/index.ts | 1 - .../repay-collat-box/repay-collat-box.tsx | 6 +-- 8 files changed, 6 insertions(+), 106 deletions(-) delete mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/collateral/collateral.tsx delete mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/collateral/index.ts delete mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/collateral/collateral.tsx delete mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/collateral/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/collateral/collateral.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/collateral/collateral.tsx deleted file mode 100644 index d84236661d..0000000000 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/collateral/collateral.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from "react"; - -import { MarginfiAccountWrapper, MarginRequirementType } from "@mrgnlabs/marginfi-client-v2"; - -import { ActionSummary } from "~/components/action-box-v2/utils"; -import { ActionProgressBar } from "~/components/action-box-v2/components"; - -type CollateralProps = { - selectedAccount: MarginfiAccountWrapper | null; - actionSummary?: ActionSummary; -}; - -export const Collateral = ({ selectedAccount, actionSummary }: CollateralProps) => { - const availableCollateral = React.useMemo(() => { - if (!selectedAccount) return null; - - if (actionSummary?.simulationPreview?.availableCollateral) { - return actionSummary.simulationPreview.availableCollateral; - } - - const collateralAmount = selectedAccount?.computeFreeCollateral().toNumber(); - const collateralRatio = - collateralAmount / selectedAccount.computeHealthComponents(MarginRequirementType.Initial).assets.toNumber(); - - return { - amount: collateralAmount, - ratio: collateralRatio, - }; - }, [actionSummary, selectedAccount]); - - return ( - <> - {availableCollateral && ( - -

Available collateral is the USD value of your collateral not actively backing a loan.

-

It can be used to open additional borrows or withdraw part of your collateral.

-
- } - /> - )} - - ); -}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/collateral/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/collateral/index.ts deleted file mode 100644 index 45c255e7f4..0000000000 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/collateral/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./collateral"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/index.ts index 9ad1a8ac82..d69b32869a 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/index.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/index.ts @@ -1,3 +1,2 @@ export * from "./action-input"; -export * from "./collateral"; export * from "./preview"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/lend-box.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/lend-box.tsx index 19c248fe44..1d6e956379 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/lend-box.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/lend-box.tsx @@ -23,7 +23,7 @@ import { PreviousTxn, } from "@mrgnlabs/mrgn-utils"; -import { ActionButton } from "~/components/action-box-v2/components"; +import { ActionButton, ActionCollateralProgressBar } from "~/components/action-box-v2/components"; import { useActionAmounts } from "~/components/action-box-v2/hooks"; import { LSTDialog, LSTDialogVariants } from "~/components/LSTDialog"; import { WalletContextStateOverride } from "~/components/wallet-v2/hooks/use-wallet.hook"; @@ -32,7 +32,7 @@ import { ActionMessage } from "~/components"; import { useLendBoxStore } from "./store"; import { HandleCloseBalanceParamsProps, handleExecuteCloseBalance, handleExecuteLendingAction } from "./utils"; import { ActionSimulationStatus } from "../../components"; -import { Collateral, ActionInput, Preview } from "./components"; +import { ActionInput, Preview } from "./components"; import { SimulationStatus } from "../../utils"; import { useLendSimulation } from "./hooks"; import { useActionBoxStore } from "../../store"; @@ -517,7 +517,7 @@ export const LendBox = ({ {showAvailableCollateral && (
- +
)} diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/collateral/collateral.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/collateral/collateral.tsx deleted file mode 100644 index 267a534c45..0000000000 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/collateral/collateral.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from "react"; - -import { MarginfiAccountWrapper, MarginRequirementType } from "@mrgnlabs/marginfi-client-v2"; - -import { ActionSummary } from "~/components/action-box-v2/utils"; -import { ActionProgressBar } from "~/components/action-box-v2/components"; - -interface CollateralProps { - selectedAccount: MarginfiAccountWrapper | null; - actionSummary?: ActionSummary; -} - -export const Collateral = ({ selectedAccount, actionSummary }: CollateralProps) => { - const availableCollateral = React.useMemo(() => { - if (!selectedAccount) return null; - - if (actionSummary?.simulationPreview?.availableCollateral) { - return actionSummary.simulationPreview.availableCollateral; - } - - const collateralAmount = selectedAccount?.computeFreeCollateral().toNumber(); - const collateralRatio = - collateralAmount / selectedAccount.computeHealthComponents(MarginRequirementType.Initial).assets.toNumber(); - - return { - amount: collateralAmount, - ratio: collateralRatio, - }; - }, [actionSummary, selectedAccount]); - - return ( - <> - {availableCollateral && ( - -

Available collateral is the USD value of your collateral not actively backing a loan.

-

It can be used to open additional borrows or withdraw part of your collateral.

-
- } - /> - )} - - ); -}; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/collateral/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/collateral/index.ts deleted file mode 100644 index 45c255e7f4..0000000000 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/collateral/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./collateral"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/index.ts index c1c71f6756..22518670b2 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/index.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/components/index.ts @@ -1,3 +1,2 @@ export * from "./preview"; -export * from "./collateral"; export * from "./action-input"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/repay-collat-box.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/repay-collat-box.tsx index f5d739456d..4e9c88663a 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/repay-collat-box.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/repay-collat-box/repay-collat-box.tsx @@ -24,7 +24,7 @@ import { IconCheck } from "@tabler/icons-react"; import { CircularProgress } from "~/components/ui/circular-progress"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "~/components/ui/tooltip"; -import { ActionButton, ActionSettingsButton } from "~/components/action-box-v2/components"; +import { ActionButton, ActionCollateralProgressBar, ActionSettingsButton } from "~/components/action-box-v2/components"; import { useActionAmounts, usePollBlockHeight } from "~/components/action-box-v2/hooks"; import { ActionMessage } from "~/components"; import { IconLoader } from "~/components/ui/icons"; @@ -32,7 +32,7 @@ import { ActionSimulationStatus } from "../../components"; import { SimulationStatus } from "../../utils/simulation.utils"; import { handleExecuteRepayCollatAction } from "./utils"; -import { Collateral, ActionInput, Preview } from "./components"; +import { ActionInput, Preview } from "./components"; import { useRepayCollatBoxStore } from "./store"; import { useRepayCollatSimulation } from "./hooks"; @@ -438,7 +438,7 @@ export const RepayCollatBox = ({ {showAvailableCollateral && (
- +
)} From 4864cb519ebb8837d35908c02b4c179f6971b363 Mon Sep 17 00:00:00 2001 From: borcherd Date: Thu, 16 Jan 2025 09:56:52 +0100 Subject: [PATCH 07/11] chore: update mrgnlend page & add to banklist in lendbox --- apps/marginfi-v2-ui/src/pages/swap-lend.tsx | 2 +- .../bank-select/components/bank-list/bank-list.tsx | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/marginfi-v2-ui/src/pages/swap-lend.tsx b/apps/marginfi-v2-ui/src/pages/swap-lend.tsx index d1dc76be3f..714c8fb7c5 100644 --- a/apps/marginfi-v2-ui/src/pages/swap-lend.tsx +++ b/apps/marginfi-v2-ui/src/pages/swap-lend.tsx @@ -18,7 +18,7 @@ export default function SwapLendPage() { {initialized && (
- Swap and lend your assets to maximize yield.

} /> + Deposit any token and swap to your chosen collateral.

} /> ); })} +
+

Don't hold supported tokens?

+ +
)} {lendingMode === LendingModes.LEND && From 0e73ddc31bcb5f3a673d02749d7033c7c9d198e2 Mon Sep 17 00:00:00 2001 From: borcherd Date: Thu, 16 Jan 2025 15:15:18 +0100 Subject: [PATCH 08/11] chore: update copy & titles --- apps/marginfi-v2-ui/src/pages/swap-lend.tsx | 2 +- .../actions/swap-lend-box/swap-lend-box.tsx | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/marginfi-v2-ui/src/pages/swap-lend.tsx b/apps/marginfi-v2-ui/src/pages/swap-lend.tsx index 714c8fb7c5..8f4f7a09a0 100644 --- a/apps/marginfi-v2-ui/src/pages/swap-lend.tsx +++ b/apps/marginfi-v2-ui/src/pages/swap-lend.tsx @@ -18,7 +18,7 @@ export default function SwapLendPage() { {initialized && (
- Deposit any token and swap to your chosen collateral.

} /> + Swap any token and deposit in your chosen collateral.

} />
- Deposit + + {!requestedDepositBank || + (selectedDepositBank && + selectedSwapBank && + selectedDepositBank.meta.tokenSymbol === selectedSwapBank.meta.tokenSymbol) + ? "Deposit" + : "Swap & Deposit"} + - ✨ Collateral Swap + ✨ Collateral -

Swap your prefered token and deposit it into any pool.

+

This is the collateral you will be depositing into.

From b3c5016943666df1c9a9d6968adbcb7ddd63f860 Mon Sep 17 00:00:00 2001 From: borcherd Date: Thu, 16 Jan 2025 15:33:02 +0100 Subject: [PATCH 09/11] fix: refreshing simulation when not needed --- .../hooks/use-swap-lend-simulation.hooks.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts index 5e3d293c68..c0db44ba13 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts @@ -190,16 +190,25 @@ export function useSwapLendSimulation({ setIsLoading({ isLoading: false, status: SimulationStatus.COMPLETE }); } }, - [depositBank, marginfiClient, selectedAccount, setActionTxns, setIsLoading, setSimulationResult, swapBank] + [ + depositBank, + marginfiClient, + selectedAccount, + setActionTxns, + setErrorMessage, + setIsLoading, + setSimulationResult, + swapBank, + ] ); React.useEffect(() => { - if (prevDebouncedAmount !== debouncedAmount || prevDepositBank !== depositBank || prevSwapBank !== swapBank) { + if (prevDebouncedAmount !== debouncedAmount) { if (debouncedAmount > 0) { handleSimulation(debouncedAmount); } } - }, [debouncedAmount, depositBank, handleSimulation, prevDebouncedAmount, prevDepositBank, prevSwapBank, swapBank]); + }, [debouncedAmount, handleSimulation, prevDebouncedAmount]); const refreshSimulation = React.useCallback(async () => { await handleSimulation(debouncedAmount ?? 0); From 5a44ea93dd208634219315836752a29ed9686fec Mon Sep 17 00:00:00 2001 From: borcherd Date: Fri, 17 Jan 2025 13:00:39 +0100 Subject: [PATCH 10/11] chore: renaming to depositSwapBox --- .../Portfolio/PositionActionButtons.tsx | 4 +- .../pages/{swap-lend.tsx => deposit-swap.tsx} | 6 +- .../components/action-box-v2/action-box.tsx | 22 +++--- .../components/action-input/action-input.tsx | 4 +- .../components/bank-select/bank-select.tsx | 0 .../components/bank-list/bank-list.tsx | 0 .../bank-select/components/bank-list/index.ts | 0 .../components/bank-trigger/bank-trigger.tsx | 0 .../components/bank-trigger/index.ts | 0 .../bank-select/components/index.ts | 0 .../components/bank-select/index.ts | 0 .../action-input/components/index.ts | 0 .../components/lending-action/index.ts | 0 .../lending-action/swap-lend-action.tsx | 6 +- .../components/action-input/index.ts | 0 .../components/index.ts | 0 .../components/preview/index.ts | 0 .../components/preview/preview.tsx | 0 .../deposit-swap-box.tsx} | 78 ++++++++++--------- .../actions/deposit-swap-box/hooks/index.ts | 1 + .../use-deposit-swap-simulation.hooks.ts} | 48 ++++++------ .../actions/deposit-swap-box/index.ts | 1 + .../store/deposit-swap-box-store.ts} | 18 ++--- .../actions/deposit-swap-box/store/index.ts | 16 ++++ .../utils/deposit-swap-action.utils.ts} | 16 ++-- .../utils/deposit-swap-simulation.utils.ts} | 22 ++---- .../actions/deposit-swap-box/utils/index.ts | 2 + .../components/action-box-v2/actions/index.ts | 2 +- .../actions/swap-lend-box/hooks/index.ts | 1 - .../actions/swap-lend-box/index.ts | 1 - .../actions/swap-lend-box/store/index.ts | 16 ---- .../actions/swap-lend-box/utils/index.ts | 2 - .../action-box-v2/types/action-box.types.ts | 12 +-- .../mrgn-utils/src/action-message.utils.ts | 6 +- packages/mrgn-utils/src/actions/actions.ts | 12 +-- .../mrgn-utils/src/actions/individualFlows.ts | 7 +- packages/mrgn-utils/src/actions/types.ts | 2 +- packages/mrgn-utils/src/types.ts | 8 +- 38 files changed, 154 insertions(+), 159 deletions(-) rename apps/marginfi-v2-ui/src/pages/{swap-lend.tsx => deposit-swap.tsx} (91%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/action-input.tsx (97%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/bank-select/bank-select.tsx (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/bank-select/components/bank-list/bank-list.tsx (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/bank-select/components/bank-list/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/bank-select/components/bank-trigger/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/bank-select/components/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/bank-select/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/lending-action/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/components/lending-action/swap-lend-action.tsx (96%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/action-input/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/preview/index.ts (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box => deposit-swap-box}/components/preview/preview.tsx (100%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box/swap-lend-box.tsx => deposit-swap-box/deposit-swap-box.tsx} (89%) create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/hooks/index.ts rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts => deposit-swap-box/hooks/use-deposit-swap-simulation.hooks.ts} (81%) create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/index.ts rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box/store/swap-lend-box-store.ts => deposit-swap-box/store/deposit-swap-box-store.ts} (91%) create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/store/index.ts rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box/utils/swap-lend-action.utils.ts => deposit-swap-box/utils/deposit-swap-action.utils.ts} (96%) rename packages/mrgn-ui/src/components/action-box-v2/actions/{swap-lend-box/utils/swap-lend-simulation.utils.ts => deposit-swap-box/utils/deposit-swap-simulation.utils.ts} (59%) create mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/index.ts delete mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/index.ts delete mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/index.ts delete mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/index.ts delete mode 100644 packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/index.ts diff --git a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx index f8ab910566..22baeb10c3 100644 --- a/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx +++ b/apps/marginfi-v2-trading/src/components/common/Portfolio/PositionActionButtons.tsx @@ -84,10 +84,10 @@ export const PositionActionButtons = ({ hidePoolStats={["type"]} >
- [state.initialized, state.extendedBankInfos]); const { connected } = useWallet(); @@ -19,9 +19,9 @@ export default function SwapLendPage() { {initialized && (
Swap any token and deposit in your chosen collateral.

} /> - { @@ -74,34 +74,34 @@ const Lend = (props: ActionBoxProps & { lendProps: RequiredLendBoxProps | LendBo }; ActionBox.Lend = Lend; -const SwapLend = ( - props: ActionBoxProps & { swapLendProps: RequiredSwapLendBoxProps | SwapLendBoxProps; useProvider?: boolean } +const DepositSwap = ( + props: ActionBoxProps & { depositSwapProps: RequiredDepositSwapBoxProps | DepositSwapBoxProps; useProvider?: boolean } ) => { const contextProps = useActionBoxContext(); - const { swapLendProps, useProvider, ...actionBoxProps } = props; + const { depositSwapProps, useProvider, ...actionBoxProps } = props; - let combinedProps: SwapLendBoxProps; + let combinedProps: DepositSwapBoxProps; if (useProvider && contextProps) { combinedProps = { ...contextProps, - ...(swapLendProps as RequiredSwapLendBoxProps), + ...(depositSwapProps as RequiredDepositSwapBoxProps), }; } else { - combinedProps = swapLendProps as SwapLendBoxProps; + combinedProps = depositSwapProps as DepositSwapBoxProps; } return ( - + ); }; -ActionBox.SwapLend = SwapLend; +ActionBox.DepositSwap = DepositSwap; const BorrowLend = ( props: ActionBoxProps & { lendProps: RequiredLendBoxProps | LendBoxProps; useProvider?: boolean } diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/action-input.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/action-input.tsx similarity index 97% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/action-input.tsx rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/action-input.tsx index 942e23e467..ff63cc9c17 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/action-input.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/action-input.tsx @@ -6,7 +6,7 @@ import { usdFormatter, tokenPriceFormatter } from "@mrgnlabs/mrgn-common"; import { Input } from "~/components/ui/input"; -import { SwapLendAction, BankSelect } from "./components"; +import { BankSelect, DepositSwapAction } from "./components"; type ActionInputProps = { amountRaw: string; @@ -113,7 +113,7 @@ export const ActionInput = ({
{!isInputDisabled && ( - handleInputChange(amount)} diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/bank-select.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/bank-select.tsx similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/bank-select.tsx rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/bank-select.tsx diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/bank-list/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-list/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/bank-list/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/bank-trigger/bank-trigger.tsx diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/bank-trigger/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/bank-trigger/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/bank-trigger/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/components/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/components/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/bank-select/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/bank-select/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/swap-lend-action.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/swap-lend-action.tsx similarity index 96% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/swap-lend-action.tsx rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/swap-lend-action.tsx index 093fa616fc..cd0d1a4c3f 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/components/lending-action/swap-lend-action.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/swap-lend-action.tsx @@ -3,7 +3,7 @@ import React from "react"; import { ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; import { dynamicNumeralFormatter } from "@mrgnlabs/mrgn-common"; -type SwapLendActionProps = { +type DepositSwapActionProps = { walletAmount: number | undefined; maxAmount: number; showLendingHeader?: boolean; @@ -13,13 +13,13 @@ type SwapLendActionProps = { onSetAmountRaw: (amount: string) => void; }; -export const SwapLendAction = ({ +export const DepositSwapAction = ({ maxAmount, walletAmount, onSetAmountRaw, selectedBank, lendMode, -}: SwapLendActionProps) => { +}: DepositSwapActionProps) => { const numberFormater = React.useMemo(() => new Intl.NumberFormat("en-US", { maximumFractionDigits: 10 }), []); const maxLabel = React.useMemo((): { diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/action-input/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/preview/index.ts similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/index.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/preview/index.ts diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/preview.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/preview/preview.tsx similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/components/preview/preview.tsx rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/preview/preview.tsx diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/swap-lend-box.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/deposit-swap-box.tsx similarity index 89% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/swap-lend-box.tsx rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/deposit-swap-box.tsx index e31f34bf52..2f4b4e68c8 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/swap-lend-box.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/deposit-swap-box.tsx @@ -12,12 +12,12 @@ import { import { MarginfiAccountWrapper, MarginfiClient } from "@mrgnlabs/marginfi-client-v2"; import { ActionMessageType, - checkSwapLendActionAvailable, - ExecuteSwapLendActionProps, + ExecuteDepositSwapActionProps, IndividualFlowError, MultiStepToastHandle, PreviousTxn, - SwapLendActionTxns, + DepositSwapActionTxns, + checkDepositSwapActionAvailable, } from "@mrgnlabs/mrgn-utils"; import { ActionButton, ActionCollateralProgressBar } from "~/components/action-box-v2/components"; @@ -27,19 +27,20 @@ import { ActionMessage } from "~/components"; import { ActionSimulationStatus } from "../../components"; import { SimulationStatus } from "../../utils"; -import { useSwapLendSimulation } from "./hooks"; +import { useDepositSwapSimulation } from "./hooks"; import { useActionBoxStore } from "../../store"; import { useActionContext, HidePoolStats } from "../../contexts"; -import { handleExecuteSwapLendAction } from "./utils"; -import { useSwapLendBoxStore } from "./store"; +import { handleExecuteDepositSwapAction } from "./utils"; +import { useDepositSwapBoxStore } from "./store"; import { ActionInput, Preview } from "./components"; import { nativeToUi } from "@mrgnlabs/mrgn-common"; + import { Tooltip, TooltipContent, TooltipTrigger } from "~/components/ui/tooltip"; import { TooltipProvider } from "~/components/ui/tooltip"; import { IconInfoCircle } from "~/components/ui/icons"; -export type SwapLendBoxProps = { +export type DepositSwapBoxProps = { nativeSolBalance: number; connected: boolean; @@ -59,7 +60,7 @@ export type SwapLendBoxProps = { captureEvent?: (event: string, properties?: Record) => void; }; -export const SwapLendBox = ({ +export const DepositSwapBox = ({ nativeSolBalance, connected, marginfiClient, @@ -75,7 +76,7 @@ export const SwapLendBox = ({ onComplete, captureEvent, hidePoolStats, -}: SwapLendBoxProps) => { +}: DepositSwapBoxProps) => { const [ amountRaw, lendMode, @@ -94,7 +95,7 @@ export const SwapLendBox = ({ setSimulationResult, setActionTxns, setErrorMessage, - ] = useSwapLendBoxStore(isDialog)((state) => [ + ] = useDepositSwapBoxStore(isDialog)((state) => [ state.amountRaw, state.lendMode, state.actionTxns, @@ -146,7 +147,7 @@ export const SwapLendBox = ({ nativeSolBalance, actionMode: lendMode, }); - const { actionSummary, refreshSimulation } = useSwapLendSimulation({ + const { actionSummary, refreshSimulation } = useDepositSwapSimulation({ debouncedAmount: debouncedAmount ?? 0, selectedAccount, accountSummary, @@ -212,7 +213,7 @@ export const SwapLendBox = ({ const actionMessages = React.useMemo(() => { setAdditionalActionMessages([]); - return checkSwapLendActionAvailable({ + return checkDepositSwapActionAvailable({ amount, connected, showCloseBalance, @@ -241,16 +242,16 @@ export const SwapLendBox = ({ // Swap-Lend Actions // /////////////////////// const executeAction = async ( - params: ExecuteSwapLendActionProps, + params: ExecuteDepositSwapActionProps, callbacks: { captureEvent?: (event: string, properties?: Record) => void; setIsLoading: (loading: boolean) => void; handleOnComplete: (txnSigs: string[]) => void; - retryCallback: (txns: SwapLendActionTxns, multiStepToast: MultiStepToastHandle) => void; + retryCallback: (txns: DepositSwapActionTxns, multiStepToast: MultiStepToastHandle) => void; } ) => { - const action = async (params: ExecuteSwapLendActionProps) => { - handleExecuteSwapLendAction({ + const action = async (params: ExecuteDepositSwapActionProps) => { + handleExecuteDepositSwapAction({ params, captureEvent: (event, properties) => { callbacks.captureEvent && callbacks.captureEvent(event, properties); @@ -259,7 +260,7 @@ export const SwapLendBox = ({ setIsComplete: callbacks.handleOnComplete, setError: (error: IndividualFlowError) => { const toast = error.multiStepToast as MultiStepToastHandle; - const txs = error.actionTxns as SwapLendActionTxns; + const txs = error.actionTxns as DepositSwapActionTxns; const errorMessage = error.message; let retry = undefined; if (error.retry && toast && txs) { @@ -273,8 +274,8 @@ export const SwapLendBox = ({ await action(params); }; - const retrySwapLendAction = React.useCallback( - async (params: ExecuteSwapLendActionProps, swapBank: ExtendedBankInfo | null) => { + const retryDepositSwapAction = React.useCallback( + async (params: ExecuteDepositSwapActionProps, swapBank: ExtendedBankInfo | null) => { executeAction(params, { captureEvent: captureEvent, setIsLoading: setIsTransactionExecuting, @@ -282,8 +283,8 @@ export const SwapLendBox = ({ setIsActionComplete(true); setPreviousTxn({ txn: txnSigs[txnSigs.length - 1] ?? "", - txnType: "SWAP_LEND", - swapLendOptions: { + txnType: "DEPOSIT_SWAP", + depositSwapOptions: { depositBank: selectedDepositBank as ActiveBankInfo, swapBank: selectedSwapBank as ActiveBankInfo, depositAmount: actionTxns?.actionQuote @@ -297,8 +298,8 @@ export const SwapLendBox = ({ onComplete && onComplete({ txn: txnSigs[txnSigs.length - 1] ?? "", - txnType: "SWAP_LEND", - swapLendOptions: { + txnType: "DEPOSIT_SWAP", + depositSwapOptions: { depositBank: selectedDepositBank as ActiveBankInfo, swapBank: selectedSwapBank as ActiveBankInfo, depositAmount: actionTxns?.actionQuote @@ -310,16 +311,16 @@ export const SwapLendBox = ({ }, }); }, - retryCallback: (txns: SwapLendActionTxns, multiStepToast: MultiStepToastHandle) => { - retrySwapLendAction({ ...params, actionTxns: txns, multiStepToast }, swapBank); + retryCallback: (txns: DepositSwapActionTxns, multiStepToast: MultiStepToastHandle) => { + retryDepositSwapAction({ ...params, actionTxns: txns, multiStepToast }, swapBank); }, }); }, [captureEvent, onComplete, selectedDepositBank, selectedSwapBank, setIsActionComplete, setPreviousTxn] ); - const handleSwapLendAction = React.useCallback( - async (_actionTxns?: SwapLendActionTxns, multiStepToast?: MultiStepToastHandle) => { + const handleDepositSwapAction = React.useCallback( + async (_actionTxns?: DepositSwapActionTxns, multiStepToast?: MultiStepToastHandle) => { if (!actionTxns || !marginfiClient || !debouncedAmount || debouncedAmount === 0) { console.log({ actionTxns, marginfiClient, selectedSwapBank }); return; @@ -340,7 +341,7 @@ export const SwapLendBox = ({ multiStepToast, actionType: lendMode, swapBank: selectedSwapBank, - } as ExecuteSwapLendActionProps; + } as ExecuteDepositSwapActionProps; await executeAction(params, { captureEvent: captureEvent, @@ -349,8 +350,8 @@ export const SwapLendBox = ({ setIsActionComplete(true); setPreviousTxn({ txn: txnSigs[txnSigs.length - 1] ?? "", - txnType: "SWAP_LEND", - swapLendOptions: { + txnType: "DEPOSIT_SWAP", + depositSwapOptions: { depositBank: selectedDepositBank as ActiveBankInfo, swapBank: selectedSwapBank as ActiveBankInfo, depositAmount: actionTxns?.actionQuote @@ -364,8 +365,8 @@ export const SwapLendBox = ({ onComplete && onComplete({ txn: txnSigs[txnSigs.length - 1] ?? "", - txnType: "SWAP_LEND", - swapLendOptions: { + txnType: "DEPOSIT_SWAP", + depositSwapOptions: { depositBank: selectedDepositBank as ActiveBankInfo, swapBank: selectedSwapBank as ActiveBankInfo, depositAmount: actionTxns?.actionQuote @@ -377,26 +378,27 @@ export const SwapLendBox = ({ }, }); }, - retryCallback: (txns: SwapLendActionTxns, multiStepToast: MultiStepToastHandle) => { - retrySwapLendAction({ ...params, actionTxns: txns, multiStepToast }, selectedSwapBank); + retryCallback: (txns: DepositSwapActionTxns, multiStepToast: MultiStepToastHandle) => { + retryDepositSwapAction({ ...params, actionTxns: txns, multiStepToast }, selectedSwapBank); }, }); }, [ actionTxns, marginfiClient, - selectedSwapBank, - selectedDepositBank, debouncedAmount, + selectedDepositBank, nativeSolBalance, selectedAccount, priorityFees, broadcastType, + lendMode, + selectedSwapBank, captureEvent, setIsActionComplete, setPreviousTxn, onComplete, - retrySwapLendAction, + retryDepositSwapAction, ] ); @@ -513,7 +515,7 @@ export const SwapLendBox = ({ connected={connected} // showCloseBalance={showCloseBalance} handleAction={() => { - handleSwapLendAction(); + handleDepositSwapAction(); }} buttonLabel={buttonLabel} /> diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/hooks/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/hooks/index.ts new file mode 100644 index 0000000000..ec4f3dec7f --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/hooks/index.ts @@ -0,0 +1 @@ +export * from "./use-deposit-swap-simulation.hooks"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/hooks/use-deposit-swap-simulation.hooks.ts similarity index 81% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/hooks/use-deposit-swap-simulation.hooks.ts index c0db44ba13..91d13e3558 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/use-swap-lend-simulation.hooks.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/hooks/use-deposit-swap-simulation.hooks.ts @@ -10,34 +10,34 @@ import { DYNAMIC_SIMULATION_ERRORS, extractErrorString, STATIC_SIMULATION_ERRORS, - SwapLendActionTxns, + DepositSwapActionTxns, usePrevious, } from "@mrgnlabs/mrgn-utils"; import { SimulationStatus } from "~/components/action-box-v2/utils"; import { calculateSummary, - generateSwapLendTxns, - GenerateSwapLendTxnsProps, + generateDepositSwapTxns, + GenerateDepositSwapTxnsProps, getSimulationResult, SimulateActionProps, } from "../utils"; -type SwapLendSimulationProps = { +type DepositSwapSimulationProps = { debouncedAmount: number; selectedAccount: MarginfiAccountWrapper | null; marginfiClient: MarginfiClient | null; accountSummary?: AccountSummary; depositBank: ExtendedBankInfo | null; swapBank: ExtendedBankInfo | null; - actionTxns: SwapLendActionTxns; + actionTxns: DepositSwapActionTxns; simulationResult: SimulationResult | null; setSimulationResult: (result: SimulationResult | null) => void; - setActionTxns: (actionTxns: SwapLendActionTxns) => void; + setActionTxns: (actionTxns: DepositSwapActionTxns) => void; setErrorMessage: (error: ActionMessageType | null) => void; setIsLoading: ({ isLoading, status }: { isLoading: boolean; status: SimulationStatus }) => void; }; -export function useSwapLendSimulation({ +export function useDepositSwapSimulation({ debouncedAmount, selectedAccount, accountSummary, @@ -50,7 +50,7 @@ export function useSwapLendSimulation({ setActionTxns, setErrorMessage, setIsLoading, -}: SwapLendSimulationProps) { +}: DepositSwapSimulationProps) { const prevDebouncedAmount = usePrevious(debouncedAmount); const prevDepositBank = usePrevious(depositBank); const prevSwapBank = usePrevious(swapBank); @@ -60,7 +60,7 @@ export function useSwapLendSimulation({ callbacks: { setErrorMessage: (error: ActionMessageType | null) => void; setSimulationResult: (result: SimulationResult | null) => void; - setActionTxns: (actionTxns: SwapLendActionTxns) => void; + setActionTxns: (actionTxns: DepositSwapActionTxns) => void; setIsLoading: ({ isLoading, status }: { isLoading: boolean; status: SimulationStatus }) => void; } ) => { @@ -102,25 +102,25 @@ export function useSwapLendSimulation({ } }; - const fetchSwapLendActionTxns = async ( - props: GenerateSwapLendTxnsProps - ): Promise<{ actionTxns: SwapLendActionTxns | null; actionMessage: ActionMessageType | null }> => { + const fetchDepositSwapActionTxns = async ( + props: GenerateDepositSwapTxnsProps + ): Promise<{ actionTxns: DepositSwapActionTxns | null; actionMessage: ActionMessageType | null }> => { try { - const swapLendActionTxns = await generateSwapLendTxns(props); - if (swapLendActionTxns && "actionTxn" in swapLendActionTxns) { + const depositSwapActionTxns = await generateDepositSwapTxns(props); + if (depositSwapActionTxns && "actionTxn" in depositSwapActionTxns) { return { - actionTxns: { ...swapLendActionTxns, actionQuote: swapLendActionTxns.actionQuote }, + actionTxns: { ...depositSwapActionTxns, actionQuote: depositSwapActionTxns.actionQuote }, actionMessage: null, }; } else { - const errorMessage = swapLendActionTxns ?? STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED; + const errorMessage = depositSwapActionTxns ?? STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED; return { actionTxns: null, actionMessage: errorMessage, }; } } catch (error) { - console.error("Error fetching swap lend action txns", error); + console.error("Error fetching deposit swap action txns", error); return { actionTxns: null, actionMessage: STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED, @@ -139,7 +139,7 @@ export function useSwapLendSimulation({ setIsLoading({ isLoading: true, status: SimulationStatus.SIMULATING }); - const props: GenerateSwapLendTxnsProps = { + const props: GenerateDepositSwapTxnsProps = { marginfiAccount: selectedAccount ?? undefined, depositBank: depositBank, swapBank: swapBank, @@ -148,10 +148,10 @@ export function useSwapLendSimulation({ slippageBps: 50, }; - const swapLendActionTxns = await fetchSwapLendActionTxns(props); + const depositSwapActionTxns = await fetchDepositSwapActionTxns(props); - if (swapLendActionTxns.actionMessage || swapLendActionTxns.actionTxns === null) { - handleError(swapLendActionTxns.actionMessage ?? STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED, { + if (depositSwapActionTxns.actionMessage || depositSwapActionTxns.actionTxns === null) { + handleError(depositSwapActionTxns.actionMessage ?? STATIC_SIMULATION_ERRORS.DEPOSIT_FAILED, { setErrorMessage, setSimulationResult, setActionTxns, @@ -162,8 +162,8 @@ export function useSwapLendSimulation({ const simulationResult = await simulationAction({ txns: [ - ...(swapLendActionTxns?.actionTxns?.additionalTxns ?? []), - ...(swapLendActionTxns?.actionTxns?.actionTxn ? [swapLendActionTxns?.actionTxns?.actionTxn] : []), + ...(depositSwapActionTxns?.actionTxns?.additionalTxns ?? []), + ...(depositSwapActionTxns?.actionTxns?.actionTxn ? [depositSwapActionTxns?.actionTxns?.actionTxn] : []), ], account: selectedAccount, bank: depositBank, @@ -179,7 +179,7 @@ export function useSwapLendSimulation({ return; } else if (simulationResult.simulationResult) { setSimulationResult(simulationResult.simulationResult); - setActionTxns(swapLendActionTxns.actionTxns); + setActionTxns(depositSwapActionTxns.actionTxns); } else { throw new Error("Unknown error"); } diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/index.ts new file mode 100644 index 0000000000..0f13a277d4 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/index.ts @@ -0,0 +1 @@ +export * from "./deposit-swap-box"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/swap-lend-box-store.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/store/deposit-swap-box-store.ts similarity index 91% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/swap-lend-box-store.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/store/deposit-swap-box-store.ts index 0ef2acffcc..7b14b20008 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/swap-lend-box-store.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/store/deposit-swap-box-store.ts @@ -1,10 +1,10 @@ import { create, StateCreator } from "zustand"; import { ActionType, ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; -import { ActionMessageType, ActionTxns, SwapLendActionTxns } from "@mrgnlabs/mrgn-utils"; +import { ActionMessageType, DepositSwapActionTxns } from "@mrgnlabs/mrgn-utils"; import { SimulationResult } from "@mrgnlabs/marginfi-client-v2"; -interface SwapLendBoxState { +interface DepositSwapBoxState { // State amountRaw: string; @@ -13,7 +13,7 @@ interface SwapLendBoxState { selectedSwapBank: ExtendedBankInfo | null; simulationResult: SimulationResult | null; - actionTxns: SwapLendActionTxns; + actionTxns: DepositSwapActionTxns; errorMessage: ActionMessageType | null; @@ -27,14 +27,14 @@ interface SwapLendBoxState { }) => void; setAmountRaw: (amountRaw: string, maxAmount?: number) => void; setSimulationResult: (simulationResult: SimulationResult | null) => void; - setActionTxns: (actionTxns: SwapLendActionTxns) => void; + setActionTxns: (actionTxns: DepositSwapActionTxns) => void; setSelectedDepositBank: (bank: ExtendedBankInfo | null) => void; setSelectedSwapBank: (bank: ExtendedBankInfo | null) => void; setErrorMessage: (errorMessage: ActionMessageType | null) => void; } -function createSwapLendBoxStore() { - return create(stateCreator); +function createDepositSwapBoxStore() { + return create(stateCreator); } const initialState = { @@ -47,7 +47,7 @@ const initialState = { errorMessage: null, }; -const stateCreator: StateCreator = (set, get) => ({ +const stateCreator: StateCreator = (set, get) => ({ // State ...initialState, @@ -177,5 +177,5 @@ const stateCreator: StateCreator = (set, get) => ({ }, }); -export { createSwapLendBoxStore }; -export type { SwapLendBoxState }; +export { createDepositSwapBoxStore }; +export type { DepositSwapBoxState }; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/store/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/store/index.ts new file mode 100644 index 0000000000..78a21f8c01 --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/store/index.ts @@ -0,0 +1,16 @@ +import { StoreApi, UseBoundStore } from "zustand"; + +import { createDepositSwapBoxStore, DepositSwapBoxState } from "./deposit-swap-box-store"; + +const useDepositSwapBoxGeneralStore: UseBoundStore> = createDepositSwapBoxStore(); +const useDepositSwapBoxDialogStore: UseBoundStore> = createDepositSwapBoxStore(); + +const useDepositSwapBoxStore = (isDialog?: boolean): UseBoundStore> => { + if (!isDialog) { + return useDepositSwapBoxGeneralStore; + } else { + return useDepositSwapBoxDialogStore; + } +}; + +export { useDepositSwapBoxStore }; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/deposit-swap-action.utils.ts similarity index 96% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/deposit-swap-action.utils.ts index a6736745b6..a3e714a77a 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-action.utils.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/deposit-swap-action.utils.ts @@ -22,15 +22,15 @@ import { ActionMessageType, ActionTxns, deserializeInstruction, - executeSwapLendAction, getAdressLookupTableAccounts, getSwapQuoteWithRetry, handleSimulationError, IndividualFlowError, MarginfiActionParams, STATIC_SIMULATION_ERRORS, - SwapLendActionTxns, + DepositSwapActionTxns, } from "@mrgnlabs/mrgn-utils"; + import { Keypair, PublicKey, Transaction, TransactionMessage, VersionedTransaction } from "@solana/web3.js"; import { ActionSummary, CalculatePreviewProps, SimulatedActionPreview } from "../../lend-box/utils"; import { @@ -40,7 +40,7 @@ import { simulatedPositionSize, } from "~/components/action-box-v2/utils"; -export interface GenerateSwapLendTxnsProps { +export interface GenerateDepositSwapTxnsProps { marginfiAccount: MarginfiAccountWrapper; depositBank: ExtendedBankInfo; swapBank?: ExtendedBankInfo | null; @@ -49,9 +49,9 @@ export interface GenerateSwapLendTxnsProps { slippageBps: number; } -export async function generateSwapLendTxns( - props: GenerateSwapLendTxnsProps -): Promise { +export async function generateDepositSwapTxns( + props: GenerateDepositSwapTxnsProps +): Promise { let swapTx: { quote?: QuoteResponse; tx?: SolanaTransaction; error?: ActionMessageType } | undefined; if (props.swapBank && props.swapBank.meta.tokenSymbol !== props.depositBank.meta.tokenSymbol) { @@ -114,7 +114,7 @@ export async function generateSwapLendTxns( }; } -export async function createSwapTx(props: GenerateSwapLendTxnsProps) { +export async function createSwapTx(props: GenerateDepositSwapTxnsProps) { if (!props.swapBank) { console.error("Swap bank is required"); throw new Error("Swap bank is required"); @@ -180,7 +180,7 @@ export async function createSwapTx(props: GenerateSwapLendTxnsProps) { } async function createMarginfiAccountTx( - props: GenerateSwapLendTxnsProps + props: GenerateDepositSwapTxnsProps ): Promise<{ account: MarginfiAccountWrapper; tx: SolanaTransaction }> { // if no marginfi account, we need to create one console.log("Creating new marginfi account transaction..."); diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-simulation.utils.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/deposit-swap-simulation.utils.ts similarity index 59% rename from packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-simulation.utils.ts rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/deposit-swap-simulation.utils.ts index 3d9602dd51..b5fe659ef4 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/swap-lend-simulation.utils.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/deposit-swap-simulation.utils.ts @@ -1,40 +1,34 @@ import { v4 as uuidv4 } from "uuid"; -import { - executeSwapLendAction, - ExecuteSwapLendActionProps, - IndividualFlowError, - MarginfiActionParams, -} from "@mrgnlabs/mrgn-utils"; +import { ExecuteDepositSwapActionProps, executeDepositSwapAction, IndividualFlowError } from "@mrgnlabs/mrgn-utils"; import { ExecuteActionsCallbackProps } from "~/components/action-box-v2/types/actions.types"; -import { ExtendedBankInfo } from "@mrgnlabs/marginfi-v2-ui-state"; -interface _ExecuteSwapLendActionProps extends ExecuteActionsCallbackProps { - params: ExecuteSwapLendActionProps; +interface _ExecuteDepositSwapActionProps extends ExecuteActionsCallbackProps { + params: ExecuteDepositSwapActionProps; } -export const handleExecuteSwapLendAction = async ({ +export const handleExecuteDepositSwapAction = async ({ params, captureEvent, setIsLoading, setIsComplete, setError, -}: _ExecuteSwapLendActionProps) => { +}: _ExecuteDepositSwapActionProps) => { try { setIsLoading(true); const attemptUuid = uuidv4(); - captureEvent(`user_swap_lend_initiate`, { + captureEvent(`user_deposit_swap_initiate`, { uuid: attemptUuid, depositToken: params.bank.meta.tokenSymbol, swapToken: params.swapBank ? params.swapBank.meta.tokenSymbol : "NO_SWAP", amount: params.amount, }); - const txnSig = await executeSwapLendAction({ ...params, swapBank: params.swapBank }); + const txnSig = await executeDepositSwapAction({ ...params, swapBank: params.swapBank }); setIsLoading(false); if (txnSig) { setIsComplete(txnSig ?? ""); - captureEvent(`user_swap_lend`, { + captureEvent(`user_deposit_swap`, { uuid: attemptUuid, txn: txnSig, depositToken: params.bank.meta.tokenSymbol, diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/index.ts new file mode 100644 index 0000000000..63ceec0e9c --- /dev/null +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/utils/index.ts @@ -0,0 +1,2 @@ +export * from "./deposit-swap-action.utils"; +export * from "./deposit-swap-simulation.utils"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/index.ts index 421930f951..6dfbe4a873 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/index.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/index.ts @@ -2,4 +2,4 @@ export * from "./lend-box"; export * from "./repay-collat-box"; export * from "./loop-box"; export * from "./stake-box"; -export * from "./swap-lend-box"; +export * from "./deposit-swap-box"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/index.ts deleted file mode 100644 index d1a7029923..0000000000 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/hooks/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./use-swap-lend-simulation.hooks"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/index.ts deleted file mode 100644 index 1536a2d0e0..0000000000 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./swap-lend-box"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/index.ts deleted file mode 100644 index aa4f993823..0000000000 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/store/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { StoreApi, UseBoundStore } from "zustand"; - -import { createSwapLendBoxStore, SwapLendBoxState } from "./swap-lend-box-store"; - -const useSwapLendBoxGeneralStore: UseBoundStore> = createSwapLendBoxStore(); -const useSwapLendBoxDialogStore: UseBoundStore> = createSwapLendBoxStore(); - -const useSwapLendBoxStore = (isDialog?: boolean): UseBoundStore> => { - if (!isDialog) { - return useSwapLendBoxGeneralStore; - } else { - return useSwapLendBoxDialogStore; - } -}; - -export { useSwapLendBoxStore }; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/index.ts deleted file mode 100644 index 5901c7b75f..0000000000 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/swap-lend-box/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./swap-lend-action.utils"; -export * from "./swap-lend-simulation.utils"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/types/action-box.types.ts b/packages/mrgn-ui/src/components/action-box-v2/types/action-box.types.ts index 7271faf14b..c0b12cf087 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/types/action-box.types.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/types/action-box.types.ts @@ -3,7 +3,7 @@ import { LoopBoxProps, RepayCollatBoxProps, StakeBoxProps, - SwapLendBoxProps, + DepositSwapBoxProps, } from "~/components/action-box-v2/actions"; import { ActionDialogProps } from "~/components/action-box-v2/components"; @@ -63,9 +63,9 @@ interface RequiredLoopBoxProps interface RequiredStakeBoxProps extends Pick {} -interface RequiredSwapLendBoxProps +interface RequiredDepositSwapBoxProps extends Pick< - SwapLendBoxProps, + DepositSwapBoxProps, "onComplete" | "captureEvent" | "connected" | "showTokenSelection" | "requestedDepositBank" > {} @@ -75,8 +75,8 @@ interface ActionBoxComponent extends React.FC; Loop: React.FC; Stake: React.FC; - SwapLend: React.FC< - ActionBoxProps & { swapLendProps: SwapLendBoxProps | RequiredSwapLendBoxProps; useProvider?: boolean } + DepositSwap: React.FC< + ActionBoxProps & { depositSwapProps: DepositSwapBoxProps | RequiredDepositSwapBoxProps; useProvider?: boolean } >; } @@ -88,6 +88,6 @@ export type { RequiredRepayBoxProps, RequiredLoopBoxProps, RequiredStakeBoxProps, - RequiredSwapLendBoxProps, + RequiredDepositSwapBoxProps, }; export { isDialogWrapperProps }; diff --git a/packages/mrgn-utils/src/action-message.utils.ts b/packages/mrgn-utils/src/action-message.utils.ts index 2c86843d44..00d09c40af 100644 --- a/packages/mrgn-utils/src/action-message.utils.ts +++ b/packages/mrgn-utils/src/action-message.utils.ts @@ -228,7 +228,7 @@ export function checkStakeActionAvailable({ return checks; } -interface CheckSwapLendActionAvailableProps { +interface CheckDepositSwapActionAvailableProps { amount: number | null; connected: boolean; nativeSolBalance: number; @@ -240,7 +240,7 @@ interface CheckSwapLendActionAvailableProps { lendMode: ActionType; } -export function checkSwapLendActionAvailable({ +export function checkDepositSwapActionAvailable({ amount, nativeSolBalance, connected, @@ -250,7 +250,7 @@ export function checkSwapLendActionAvailable({ banks, marginfiAccount, lendMode, -}: CheckSwapLendActionAvailableProps): ActionMessageType[] { +}: CheckDepositSwapActionAvailableProps): ActionMessageType[] { let checks: ActionMessageType[] = []; const requiredCheck = getRequiredCheck(connected, depositBank); diff --git a/packages/mrgn-utils/src/actions/actions.ts b/packages/mrgn-utils/src/actions/actions.ts index cf3aa6a713..005756bda0 100644 --- a/packages/mrgn-utils/src/actions/actions.ts +++ b/packages/mrgn-utils/src/actions/actions.ts @@ -13,7 +13,7 @@ import { LoopingProps, TradeActionTxns, ClosePositionActionTxns, - SwapLendActionTxns, + DepositSwapActionTxns, } from "./types"; import { WalletContextStateOverride } from "../wallet"; import { @@ -30,7 +30,7 @@ import { mintLstToken, mintLstStakeToStake, closePosition, - swapLend, + depositSwap, } from "./individualFlows"; import { STATIC_SIMULATION_ERRORS } from "../errors"; @@ -154,15 +154,15 @@ export async function executeTradeAction(params: ExecuteTradeActionProps) { return txnSig; } -export interface ExecuteSwapLendActionProps extends MarginfiActionParams { +export interface ExecuteDepositSwapActionProps extends MarginfiActionParams { swapBank: ExtendedBankInfo | null; - actionTxns: SwapLendActionTxns; + actionTxns: DepositSwapActionTxns; } -export async function executeSwapLendAction(params: ExecuteSwapLendActionProps) { +export async function executeDepositSwapAction(params: ExecuteDepositSwapActionProps) { let txnSig: string[] | undefined; - txnSig = await swapLend(params); + txnSig = await depositSwap(params); return txnSig; } diff --git a/packages/mrgn-utils/src/actions/individualFlows.ts b/packages/mrgn-utils/src/actions/individualFlows.ts index 667fe14b09..89ab1caa70 100644 --- a/packages/mrgn-utils/src/actions/individualFlows.ts +++ b/packages/mrgn-utils/src/actions/individualFlows.ts @@ -50,7 +50,7 @@ import { import { captureSentryException } from "../sentry.utils"; import { loopingBuilder, repayWithCollatBuilder } from "./flashloans"; import { STATIC_SIMULATION_ERRORS } from "../errors"; -import { ExecuteSwapLendActionProps } from "./actions"; +import { ExecuteDepositSwapActionProps } from "./actions"; //-----------------------// // Local utils functions // @@ -334,7 +334,7 @@ export async function deposit({ } } -export async function swapLend({ +export async function depositSwap({ marginfiAccount, marginfiClient, bank, @@ -344,8 +344,7 @@ export async function swapLend({ txOpts, multiStepToast, swapBank, -}: ExecuteSwapLendActionProps) { - console.log(marginfiAccount); +}: ExecuteDepositSwapActionProps) { if (!multiStepToast) { const steps = getSteps(actionTxns); diff --git a/packages/mrgn-utils/src/actions/types.ts b/packages/mrgn-utils/src/actions/types.ts index 188da7cbef..fcdbaa4fc9 100644 --- a/packages/mrgn-utils/src/actions/types.ts +++ b/packages/mrgn-utils/src/actions/types.ts @@ -96,7 +96,7 @@ export interface StakeActionTxns extends ActionTxns { lastValidBlockHeight?: number; } // TOOD: implement this as actionSummary type -export interface SwapLendActionTxns extends ActionTxns { +export interface DepositSwapActionTxns extends ActionTxns { actionQuote: QuoteResponse | null; } diff --git a/packages/mrgn-utils/src/types.ts b/packages/mrgn-utils/src/types.ts index d2186a0e18..690d88df24 100644 --- a/packages/mrgn-utils/src/types.ts +++ b/packages/mrgn-utils/src/types.ts @@ -67,10 +67,10 @@ interface PreviousTxnPositionClosed { }; } -interface PreviousTxnSwapLend { +interface PreviousTxnDepositSwap { txn: string; - txnType: "SWAP_LEND"; - swapLendOptions: { + txnType: "DEPOSIT_SWAP"; + depositSwapOptions: { depositBank: ActiveBankInfo; swapBank: ActiveBankInfo; depositAmount: number; @@ -102,4 +102,4 @@ export type PreviousTxn = | PreviousTxnTrading | PreviousTxnPositionClosed | PreviousTxnStake - | PreviousTxnSwapLend; + | PreviousTxnDepositSwap; From a1c2692b6b84a15f329161dccc2f76dbb48af987 Mon Sep 17 00:00:00 2001 From: borcherd Date: Fri, 17 Jan 2025 14:14:27 +0100 Subject: [PATCH 11/11] chore: more renaming --- .../{swap-lend-action.tsx => deposit-lend-action.tsx} | 0 .../components/action-input/components/lending-action/index.ts | 2 +- .../action-box-v2/actions/deposit-swap-box/deposit-swap-box.tsx | 2 +- .../components/bank-select/components/bank-list/bank-list.tsx | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/{swap-lend-action.tsx => deposit-lend-action.tsx} (100%) diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/swap-lend-action.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/deposit-lend-action.tsx similarity index 100% rename from packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/swap-lend-action.tsx rename to packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/deposit-lend-action.tsx diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/index.ts b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/index.ts index 363c4b2d8c..00c2f896cd 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/index.ts +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/components/action-input/components/lending-action/index.ts @@ -1 +1 @@ -export * from "./swap-lend-action"; +export * from "./deposit-lend-action"; diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/deposit-swap-box.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/deposit-swap-box.tsx index 2f4b4e68c8..80ac296c08 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/deposit-swap-box.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/deposit-swap-box/deposit-swap-box.tsx @@ -239,7 +239,7 @@ export const DepositSwapBox = ({ const buttonLabel = React.useMemo(() => (showCloseBalance ? "Close" : lendMode), [showCloseBalance, lendMode]); /////////////////////// - // Swap-Lend Actions // + // Deposit-Swap Actions // /////////////////////// const executeAction = async ( params: ExecuteDepositSwapActionProps, diff --git a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx b/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx index 124541b5cc..eb67cac50f 100644 --- a/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx +++ b/packages/mrgn-ui/src/components/action-box-v2/actions/lend-box/components/action-input/components/bank-select/components/bank-list/bank-list.tsx @@ -175,7 +175,7 @@ export const BankList = ({

Don't hold supported tokens?