diff --git a/src/actions/borrow.tsx b/src/actions/borrow.tsx index 0045551..3e12405 100644 --- a/src/actions/borrow.tsx +++ b/src/actions/borrow.tsx @@ -125,9 +125,7 @@ export const borrow = async ( signers ) : undefined; - - - + let amountLamports: number = 0; let fromLamports: number = 0; if (amountType === BorrowAmountType.LiquidityBorrowAmount) { @@ -197,7 +195,6 @@ export const borrow = async ( instructions = []; cleanupInstructions = [...finalCleanupInstructions]; - // create approval for transfer transactions const transferAuthority = approve( instructions, diff --git a/src/components/BorrowInput/index.tsx b/src/components/BorrowInput/index.tsx index 66fe316..0f411fa 100644 --- a/src/components/BorrowInput/index.tsx +++ b/src/components/BorrowInput/index.tsx @@ -195,6 +195,7 @@ export const BorrowInput = (props: { onCollateralReserve={(key) => { setCollateralReserveKey(key); }} + useFirstReserve={true} /> @@ -204,7 +205,7 @@ export const BorrowInput = (props: { flexDirection: "row", justifyContent: "space-evenly", alignItems: "center", - marginBottom: 20 + marginBottom: 20, }} > void; onInputChange: (value: number | null) => void; hideBalance?: boolean; + useWalletBalance?: boolean; + useFirstReserve?: boolean; showLeverageSelector?: boolean; leverage?: number; }) { + const { balance: tokenBalance } = useUserBalance(props.reserve.liquidityMint); const { reserveAccounts } = useLendingReserves(); const { tokenMap } = useConnectionConfig(); const [collateralReserve, setCollateralReserve] = useState(); @@ -38,21 +45,26 @@ export default function CollateralInput(props: { const userDeposits = useUserDeposits(); useEffect(() => { - const id: string = - cache - .byParser(LendingReserveParser) - .find((acc) => acc === collateralReserve) || ""; - const parser = cache.get(id) as ParsedAccount; - if (parser) { - const collateralDeposit = userDeposits.userDeposits.find( - (u) => - u.reserve.info.liquidityMint.toBase58() === - parser.info.liquidityMint.toBase58() - ); - if (collateralDeposit) setBalance(collateralDeposit.info.amount); - else setBalance(0); + if (props.useWalletBalance) { + setBalance(tokenBalance); + } else { + const id: string = + cache + .byParser(LendingReserveParser) + .find((acc) => acc === collateralReserve) || ""; + const parser = cache.get(id) as ParsedAccount; + + if (parser) { + const collateralDeposit = userDeposits.userDeposits.find( + (u) => + u.reserve.info.liquidityMint.toBase58() === + parser.info.liquidityMint.toBase58() + ); + if (collateralDeposit) setBalance(collateralDeposit.info.amount); + else setBalance(0); + } } - }, [collateralReserve, userDeposits]); + }, [collateralReserve, userDeposits, tokenBalance, props.useWalletBalance]); const market = cache.get(props.reserve.lendingMarket) as ParsedAccount< LendingMarket @@ -63,26 +75,35 @@ export default function CollateralInput(props: { market?.info?.quoteMint ); - const renderReserveAccounts = reserveAccounts + const filteredReserveAccounts = reserveAccounts .filter((reserve) => reserve.info !== props.reserve) .filter( (reserve) => !onlyQuoteAllowed || reserve.info.liquidityMint.equals(market.info.quoteMint) - ) - .map((reserve) => { - const mint = reserve.info.liquidityMint.toBase58(); - const address = reserve.pubkey.toBase58(); - const name = getTokenName(tokenMap, mint); - return ( - - ); - }); + ); + + if ( + !collateralReserve && + props.useFirstReserve && + filteredReserveAccounts.length + ) { + const address = filteredReserveAccounts[0].pubkey.toBase58(); + setCollateralReserve(address); + } + const renderReserveAccounts = filteredReserveAccounts.map((reserve) => { + const mint = reserve.info.liquidityMint.toBase58(); + const address = reserve.pubkey.toBase58(); + const name = getTokenName(tokenMap, mint); + return ( + + ); + }); return ( ; - withdrawReserve?: ParsedAccount; + withdrawReserve: ParsedAccount; obligation: EnrichedLendingObligation; }) => { const connection = useConnection(); const { wallet } = useWallet(); const { repayReserve, withdrawReserve, obligation } = props; + const [lastTyped, setLastTyped] = useState("liquidate"); const [pendingTx, setPendingTx] = useState(false); const [showConfirmation, setShowConfirmation] = useState(false); + const [collateralValue, setCollateralValue] = useState(""); - const { accounts: fromAccounts } = useUserBalance( + const liquidityMint = useMint(repayReserve.info.liquidityMint); + const { accounts: fromAccounts, balance: tokenBalance } = useUserBalance( repayReserve?.info.liquidityMint ); + const borrowAmountLamports = wadToLamports( + obligation.info.borrowAmountWad + ).toNumber(); + + const borrowAmount = fromLamports(borrowAmountLamports, liquidityMint); + + const convert = useCallback( + (val: string | number) => { + const minAmount = Math.min(tokenBalance || Infinity, borrowAmount); + setLastTyped("liquidate"); + if (typeof val === "string") { + return (parseFloat(val) / minAmount) * 100; + } else { + return (val * minAmount) / 100; + } + }, + [borrowAmount, tokenBalance] + ); + + const { value, setValue, pct, setPct, type } = useSliderInput(convert); const onLiquidate = useCallback(() => { if (!withdrawReserve) { @@ -40,20 +70,33 @@ export const LiquidateInput = (props: { (async () => { try { + const toLiquidateLamports = + type === InputType.Percent && tokenBalance >= borrowAmount + ? (pct * borrowAmountLamports) / 100 + : Math.ceil( + borrowAmountLamports * (parseFloat(value) / borrowAmount) + ); await liquidate( connection, wallet, fromAccounts[0], // TODO: ensure user has available amount - wadToLamports(obligation.info.borrowAmountWad).toNumber(), + toLiquidateLamports, obligation.account, repayReserve, withdrawReserve ); + setValue(""); + setCollateralValue(""); setShowConfirmation(true); - } catch { + } catch (error) { // TODO: + notify({ + message: "Unable to liquidate loan.", + type: "error", + description: error.message, + }); } finally { setPendingTx(false); } @@ -65,8 +108,64 @@ export const LiquidateInput = (props: { repayReserve, wallet, connection, + value, + setValue, + borrowAmount, + borrowAmountLamports, + pct, + tokenBalance, + type, ]); + const collateralPrice = useMidPriceInUSD( + withdrawReserve?.info.liquidityMint.toBase58() + )?.price; + + useEffect(() => { + if (withdrawReserve && lastTyped === "liquidate") { + const collateralInQuote = obligation.info.collateralInQuote; + const collateral = collateralInQuote / collateralPrice; + if (value) { + const borrowRatio = (parseFloat(value) / borrowAmount) * 100; + const collateralAmount = (borrowRatio * collateral) / 100; + setCollateralValue(collateralAmount.toString()); + } else { + setCollateralValue(""); + } + } + }, [ + borrowAmount, + collateralPrice, + withdrawReserve, + lastTyped, + obligation.info.collateralInQuote, + value, + ]); + + useEffect(() => { + if (withdrawReserve && lastTyped === "collateral") { + const collateralInQuote = obligation.info.collateralInQuote; + const collateral = collateralInQuote / collateralPrice; + if (collateralValue) { + const collateralRatio = + (parseFloat(collateralValue) / collateral) * 100; + const borrowValue = (collateralRatio * borrowAmount) / 100; + setValue(borrowValue.toString()); + } else { + setValue(""); + } + } + }, [ + borrowAmount, + collateralPrice, + withdrawReserve, + collateralValue, + lastTyped, + obligation.info.collateralInQuote, + setValue, + ]); + + if (!withdrawReserve) return null; const bodyStyle: React.CSSProperties = { display: "flex", flex: 1, @@ -87,23 +186,58 @@ export const LiquidateInput = (props: { justifyContent: "space-around", }} > -
- {LABELS.SELECT_COLLATERAL} +
{LABELS.LIQUIDATE_QUESTION}
+
+ { + setValue(val?.toString() || ""); + setLastTyped("liquidate"); + }} + disabled={true} + useWalletBalance={true} + />
- - - +
)}
diff --git a/src/components/LoanInfoLine/index.tsx b/src/components/LoanInfoLine/index.tsx index f896743..14f2c20 100644 --- a/src/components/LoanInfoLine/index.tsx +++ b/src/components/LoanInfoLine/index.tsx @@ -70,7 +70,7 @@ export const LoanInfoLine = (props: { (
diff --git a/src/components/RepayInput/index.tsx b/src/components/RepayInput/index.tsx index 6c759b8..190b449 100644 --- a/src/components/RepayInput/index.tsx +++ b/src/components/RepayInput/index.tsx @@ -38,6 +38,9 @@ export const RepayInput = (props: { const obligation = props.obligation; const liquidityMint = useMint(repayReserve.info.liquidityMint); + const { balance: tokenBalance } = useUserBalance( + repayReserve.info.liquidityMint + ); const borrowAmountLamports = wadToLamports( obligation.info.borrowAmountWad @@ -53,14 +56,15 @@ export const RepayInput = (props: { const convert = useCallback( (val: string | number) => { + const minAmount = Math.min(tokenBalance || Infinity, borrowAmount); setLastTyped("repay"); if (typeof val === "string") { - return (parseFloat(val) / borrowAmount) * 100; + return (parseFloat(val) / minAmount) * 100; } else { - return (val * borrowAmount) / 100; + return (val * minAmount) / 100; } }, - [borrowAmount] + [borrowAmount, tokenBalance] ); const { value, setValue, pct, setPct, type } = useSliderInput(convert); @@ -211,7 +215,7 @@ export const RepayInput = (props: { setLastTyped("repay"); }} disabled={true} - hideBalance={true} + useWalletBalance={true} />
@@ -221,7 +225,7 @@ export const RepayInput = (props: { flexDirection: "row", justifyContent: "space-evenly", alignItems: "center", - marginBottom: 20 + marginBottom: 20, }} > , parser?: AccountParser ) => { - if(obj.data.length === 0) { + if (obj.data.length === 0) { return; } diff --git a/src/models/account.ts b/src/models/account.ts index c9fa53d..022a077 100644 --- a/src/models/account.ts +++ b/src/models/account.ts @@ -39,7 +39,7 @@ export function approve( ) ); - if(autoRevoke) { + if (autoRevoke) { cleanupInstructions.push( Token.createRevokeInstruction(tokenProgram, account, owner, []) ); diff --git a/src/views/borrow/index.tsx b/src/views/borrow/index.tsx index e3f13a8..339fe7a 100644 --- a/src/views/borrow/index.tsx +++ b/src/views/borrow/index.tsx @@ -18,7 +18,11 @@ export const BorrowView = () => {
{reserveAccounts.map((account) => ( - + ))}
diff --git a/src/views/dashboard/deposit/index.tsx b/src/views/dashboard/deposit/index.tsx index cdb590d..8b49430 100644 --- a/src/views/dashboard/deposit/index.tsx +++ b/src/views/dashboard/deposit/index.tsx @@ -33,7 +33,10 @@ export const DashboardDeposits = () => {
{userDeposits.map((deposit) => ( - + ))} ); diff --git a/src/views/dashboard/index.tsx b/src/views/dashboard/index.tsx index bdac203..a2236f0 100644 --- a/src/views/dashboard/index.tsx +++ b/src/views/dashboard/index.tsx @@ -23,9 +23,7 @@ export const DashboardView = () => { /> {LABELS.DASHBOARD_INFO} - ): - userDeposits.length === 0 && userObligations.length === 0 ? - ( + ) : userDeposits.length === 0 && userObligations.length === 0 ? (
{ /> {LABELS.NO_LOANS_NO_DEPOSITS}
- ): ( + ) : ( - {userDeposits.length > 0 ? - : - {LABELS.NO_DEPOSITS} } + {userDeposits.length > 0 ? ( + + ) : ( + {LABELS.NO_DEPOSITS} + )} - {userObligations.length > 0 ? - : - {LABELS.NO_LOANS} } + {userObligations.length > 0 ? ( + + ) : ( + {LABELS.NO_LOANS} + )} )} diff --git a/src/views/dashboard/obligation/index.tsx b/src/views/dashboard/obligation/index.tsx index 3671df9..ce89ca0 100644 --- a/src/views/dashboard/obligation/index.tsx +++ b/src/views/dashboard/obligation/index.tsx @@ -35,9 +35,12 @@ export const DashboardObligations = () => {
{userObligations.map((item) => { - return ; + return ( + + ); })} ); diff --git a/src/views/deposit/view/index.tsx b/src/views/deposit/view/index.tsx index 63d746b..dfb8fc0 100644 --- a/src/views/deposit/view/index.tsx +++ b/src/views/deposit/view/index.tsx @@ -17,7 +17,11 @@ export const DepositView = () => {
{reserveAccounts.map((account) => ( - + ))} diff --git a/src/views/liquidateReserve/index.tsx b/src/views/liquidateReserve/index.tsx index b722035..ade0c70 100644 --- a/src/views/liquidateReserve/index.tsx +++ b/src/views/liquidateReserve/index.tsx @@ -13,7 +13,6 @@ import { LiquidateInput } from "../../components/LiquidateInput"; import "./style.less"; import { Col, Row } from "antd"; import { GUTTER } from "../../constants"; -import { BorrowInput } from "../../components/BorrowInput"; export const LiquidateReserveView = () => { const { id } = useParams<{ id: string }>();