diff --git a/packages/app/package.json b/packages/app/package.json index 0ec26528..e709a6d0 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -29,7 +29,8 @@ "sharp": "^0.32.5", "soroban-client": "^1.0.0-beta.2", "styled-components": "^6.0.7", - "use-context-selector": "^1.4.1" + "use-context-selector": "^1.4.1", + "react-toastify": "11.0.5" }, "devDependencies": { "@svgr/webpack": "^6.2.1", diff --git a/packages/app/src/app/layout.tsx b/packages/app/src/app/layout.tsx index 8018d0bc..d0f16a20 100644 --- a/packages/app/src/app/layout.tsx +++ b/packages/app/src/app/layout.tsx @@ -5,6 +5,7 @@ import { MobilePlaceholder } from '@/global/mobile-placeholder/mobile-placeholde import { LayoutSwitcher } from '@/global/mobile-placeholder/layout-switcher'; import '@marginly/ui/styles/theme.css'; import { GlobalStyle } from '@/global/styles'; +import { ToasterContainer } from '@/global/providers/toaster-container'; export const metadata: Metadata = { title: 'Slender', @@ -23,6 +24,7 @@ export default function RootLayout({ children }: { children: React.ReactNode }) + } /> diff --git a/packages/app/src/features/liquidity-flow/hooks/use-lend-increase.tsx b/packages/app/src/features/liquidity-flow/hooks/use-lend-increase.tsx index e2e66ac3..36748333 100644 --- a/packages/app/src/features/liquidity-flow/hooks/use-lend-increase.tsx +++ b/packages/app/src/features/liquidity-flow/hooks/use-lend-increase.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import { PositionContext } from '@/entities/position/context/context'; import { useContextSelector } from 'use-context-selector'; import { SupportedTokenName } from '@/shared/stellar/constants/tokens'; +import { toast } from 'react-toastify'; import { useLiquidity } from './use-liquidity'; import { excludeSupportedTokens } from '../utils/exclude-supported-tokens'; import { useDepositUsd } from './use-deposit-usd'; @@ -10,6 +11,7 @@ import { LendIncreaseModal } from '../components/lend-increase-modal'; import { sumObj } from '../utils/sum-obj'; import { PositionUpdate } from '../types'; import { getCellByPositionUpdate } from '../soroban/get-cell-by-position-update'; +import { useTokenInfo } from './use-token-info'; export const useLendIncrease = (): { modal: JSX.Element | null; @@ -31,6 +33,10 @@ export const useLendIncrease = (): { } }; + const usdcDecimals = useTokenInfo('usdc').decimals; + const xlmDecimals = useTokenInfo('xlm').decimals; + const xrpDecimals = useTokenInfo('xrp').decimals; + const depositSumUsd = useDepositUsd(position?.deposits); const debtSumUsd = useDebtUsd(position?.debts); const send = useLiquidity('deposit'); @@ -51,10 +57,31 @@ export const useLendIncrease = (): { const newDeposits = getCellByPositionUpdate(finalDebtsObj); setModalToken(null); - await send({ - additionalDeposits: getCellByPositionUpdate(sendValue), - deposits: newDeposits, - }); + try { + await send({ + additionalDeposits: getCellByPositionUpdate(sendValue), + deposits: newDeposits, + }); + } catch (error) { + const getDecimals = () => { + if (sendValue.usdc) return usdcDecimals; + if (sendValue.xlm) return xlmDecimals; + if (sendValue.xrp) return xrpDecimals; + return 0; + }; + const errorMessage = error instanceof Error ? error.message : String(error); + const match = errorMessage.match( + /resulting balance is not within the allowed range[^\d]*(\d+)/, + ); + + if (match?.[1]) { + const decimals = getDecimals(); + const balance = parseInt(match[1], 10) / 10 ** decimals; + toast.error(`Wallet balance must not be below ${balance.toFixed(2)}`); + } else { + toast.error('Lend failed'); + } + } }; return ( diff --git a/packages/app/src/features/liquidity-flow/hooks/use-liquidity.ts b/packages/app/src/features/liquidity-flow/hooks/use-liquidity.ts index 85d0e1c7..5e3e5747 100644 --- a/packages/app/src/features/liquidity-flow/hooks/use-liquidity.ts +++ b/packages/app/src/features/liquidity-flow/hooks/use-liquidity.ts @@ -52,7 +52,7 @@ export function useLiquidity( return true; } catch (e) { logError(e); - return false; + throw new Error(e); } finally { setWaitModalIsOpen(false); } diff --git a/packages/app/src/global/providers/toaster-container.tsx b/packages/app/src/global/providers/toaster-container.tsx new file mode 100644 index 00000000..781fdc96 --- /dev/null +++ b/packages/app/src/global/providers/toaster-container.tsx @@ -0,0 +1,21 @@ +'use client'; + +import React from 'react'; +import { ToastContainer } from 'react-toastify'; + +export function ToasterContainer() { + return ( + + ); +} diff --git a/packages/app/src/widgets/market-section/components/market-card/market-card.tsx b/packages/app/src/widgets/market-section/components/market-card/market-card.tsx index bce97e64..9f6ce87a 100644 --- a/packages/app/src/widgets/market-section/components/market-card/market-card.tsx +++ b/packages/app/src/widgets/market-section/components/market-card/market-card.tsx @@ -56,8 +56,8 @@ export function MarketCard({ tokenName }: { tokenName: SupportedTokenName }) { const availablePercent = +Number((availableToBorrow / totalSupplied) * 100).toFixed() || 0; - const totalSuppliedAmount = Math.floor(totalSupplied); - const availableToBorrowAmount = Math.floor(availableToBorrow); + const totalSuppliedAmount = totalSupplied; + const availableToBorrowAmount = availableToBorrow; if (discount === undefined) { return 'Loading...'; diff --git a/packages/landing/src/widgets/landing/components/question-section/question-section.tsx b/packages/landing/src/widgets/landing/components/question-section/question-section.tsx index 6c1c99ac..6cd0d813 100644 --- a/packages/landing/src/widgets/landing/components/question-section/question-section.tsx +++ b/packages/landing/src/widgets/landing/components/question-section/question-section.tsx @@ -2,7 +2,7 @@ import { Title } from '../styled'; import { Wrapper } from './styled'; import { Faq } from './faq'; import { FaqLink } from './FaqLink'; -import { DISCORD_LINK, NOTION_LINK } from '../../links'; +import { NOTION_LINK } from '../../links'; export function QuestionSection({ subscriptionAnchor }: { subscriptionAnchor: string }) { return ( @@ -26,14 +26,6 @@ export function QuestionSection({ subscriptionAnchor }: { subscriptionAnchor: st Slender doesn’t have a token at this moment. We will introduce the SLNR token after carefully crafting the protocol tokenomics and launching it in production. - - - Subscribe to our newsletter - {' '} - and follow Slender project posts on{' '} - Stellar’s Discord. Our Social networks like Twitter - and Discord will launch a bit later. - ); diff --git a/yarn.lock b/yarn.lock index daa602cc..c1a0f9f9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6158,6 +6158,11 @@ clsx@^2.0.0: resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== +clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + co@^4.6.0: version "4.6.0" resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" @@ -11130,6 +11135,13 @@ react-style-singleton@^2.2.1: invariant "^2.2.4" tslib "^2.0.0" +react-toastify@11.0.5: + version "11.0.5" + resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-11.0.5.tgz#ce4c42d10eeb433988ab2264d3e445c4e9d13313" + integrity sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA== + dependencies: + clsx "^2.1.1" + react-transition-group@^4.4.5: version "4.4.5" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"