11"use client" ;
2- import React , { useState , useCallback , useEffect } from "react" ;
2+ import React , { useState , useCallback , useEffect , useMemo } from "react" ;
33import styles from "./WithdrawRepay.module.css" ;
44import SubmitButton from "@/components/SubmitButton/SubmitButton" ;
55import PercentagePicker from "@/components/PercentagePicker/PercentagePicker" ;
@@ -14,7 +14,7 @@ import { tokenInput } from "liquidops";
1414import { useGetPosition } from "@/hooks/LiquidOpsData/useGetPosition" ;
1515import { useLoadingScreen } from "@/components/LoadingScreen/useLoadingScreen" ;
1616import { useValueLimit } from "@/hooks/data/useValueLimit" ;
17- import { useProtocolStats } from "@/hooks/LiquidOpsData/useProtocolStats" ;
17+ import { useInfo , useProtocolStats } from "@/hooks/LiquidOpsData/useProtocolStats" ;
1818import { AnimatePresence , motion } from "framer-motion" ;
1919import { warningVariants } from "@/components/DropDown/FramerMotion" ;
2020import { useCooldown } from "@/hooks/data/useCooldown" ;
@@ -42,9 +42,14 @@ const WithdrawRepay: React.FC<WithdrawRepayProps> = ({
4242 const { data : oTokenBalance , isLoading : isLoadingOTokenBalance } =
4343 useUserBalance ( oTokenAddress ) ;
4444
45- const currentBalance = mode === "withdraw" ? lentBalance : positionBalance ;
46- const isLoadingCurrentBalance =
47- mode === "withdraw" ? isLoadingBalance : isLoadingPosition ;
45+ const currentBalance = useMemo (
46+ ( ) => mode === "withdraw" ? lentBalance : positionBalance ,
47+ [ mode , lentBalance , positionBalance ]
48+ ) ;
49+ const isLoadingCurrentBalance = useMemo (
50+ ( ) => mode === "withdraw" ? isLoadingBalance : isLoadingPosition ,
51+ [ mode , isLoadingBalance , isLoadingPosition ]
52+ ) ;
4853
4954 const { unlend, isUnlending, unlendError } = useLend ( {
5055 onSuccess : onClose ,
@@ -57,9 +62,6 @@ const WithdrawRepay: React.FC<WithdrawRepayProps> = ({
5762
5863 const [ inputValue , setInputValue ] = useState ( "" ) ;
5964 const [ isFocused , setIsFocused ] = useState ( false ) ;
60- const [ selectedPercentage , setSelectedPercentage ] = useState < number | null > (
61- null ,
62- ) ;
6365 const [ notLoadedBalance , setNotLoadedBalance ] = useState < string | boolean > (
6466 false ,
6567 ) ;
@@ -87,7 +89,6 @@ const WithdrawRepay: React.FC<WithdrawRepayProps> = ({
8789 // Reset input callback
8890 const resetInput = useCallback ( ( ) => {
8991 setInputValue ( "" ) ;
90- setSelectedPercentage ( null ) ;
9192 } , [ ] ) ;
9293
9394 // Initialize loading screen hook
@@ -105,64 +106,57 @@ const WithdrawRepay: React.FC<WithdrawRepayProps> = ({
105106 protocolStats ,
106107 ) ;
107108
108- const calculateMaxAmount = ( ) => {
109- if ( mode === "withdraw" ) {
110- return isLoadingOTokenBalance ? null : oTokenBalance ;
111- }
112- return isLoadingCurrentBalance ? null : currentBalance ;
113- } ;
114-
115109 const handleMaxClick = ( ) => {
116110 setHasUserInteracted ( true ) ;
117- const maxAmount = calculateMaxAmount ( ) ;
118- if ( ! maxAmount ) {
111+ if ( ! currentBalance ) {
119112 // Set loading state only in event handlers, not during render
120113 setNotLoadedBalance ( mode === "withdraw" ? "oToken" : "token" ) ;
121114 return ;
122115 }
123- setInputValue ( maxAmount . toString ( ) ) ;
116+ setInputValue ( currentBalance . toString ( ) ) ;
124117 } ;
125118
126119 const handlePercentageClick = ( percentage : number ) => {
127120 setHasUserInteracted ( true ) ;
128- const maxAmount = calculateMaxAmount ( ) ;
129- if ( ! maxAmount ) {
121+ if ( ! currentBalance ) {
130122 // Set loading state only in event handlers, not during render
131123 setNotLoadedBalance ( mode === "withdraw" ? "oToken" : "token" ) ;
132124 return ;
133125 }
134126
135127 const amount = Quantity . __div (
136- Quantity . __mul ( maxAmount , new Quantity ( 0n , 12n ) . fromNumber ( percentage ) ) ,
128+ Quantity . __mul ( currentBalance , new Quantity ( 0n , 12n ) . fromNumber ( percentage ) ) ,
137129 new Quantity ( 0n , 12n ) . fromNumber ( 100 ) ,
138130 ) ;
139131 setInputValue ( amount . toString ( ) ) ;
140- setSelectedPercentage ( percentage ) ;
141132 } ;
142133
143- const getCurrentPercentage = ( ) => {
144- const maxAmount = calculateMaxAmount ( ) ;
134+ const currentPercentage = useMemo (
135+ ( ) => {
136+ // no data
137+ if (
138+ ! currentBalance ||
139+ ! inputValue ||
140+ Quantity . eq ( currentBalance , new Quantity ( 0n , 12n ) )
141+ ) {
142+ return 0 ;
143+ }
145144
146- // Return 0 if data is still loading or no input
147- if (
148- ! maxAmount ||
149- ! inputValue ||
150- Quantity . eq ( maxAmount , new Quantity ( 0n , 12n ) )
151- ) {
152- return 0 ;
153- }
145+ if ( isNaN ( Number ( inputValue . replace ( / , / g, "" ) ) ) ) return 0 ;
154146
155- if ( isNaN ( Number ( inputValue . replace ( / , / g, "" ) ) ) ) return 0 ;
147+ const percentage = Quantity . __div (
148+ Quantity . __mul (
149+ new Quantity ( 0n , currentBalance . denomination ) . fromString ( inputValue ) ,
150+ new Quantity ( 0n , currentBalance . denomination ) . fromNumber ( 100 ) ,
151+ ) ,
152+ currentBalance ,
153+ ) ;
154+ return Math . min ( 100 , Math . max ( 0 , percentage . toNumber ( ) ) ) ;
155+ } ,
156+ [ currentBalance , inputValue ]
157+ ) ;
156158
157- const percentage = Quantity . __div (
158- Quantity . __mul (
159- new Quantity ( 0n , maxAmount . denomination ) . fromString ( inputValue ) ,
160- new Quantity ( 0n , maxAmount . denomination ) . fromNumber ( 100 ) ,
161- ) ,
162- maxAmount ,
163- ) ;
164- return Math . min ( 100 , Math . max ( 0 , percentage . toNumber ( ) ) ) ;
165- } ;
159+ const { data : tokenInfo , isLoading : isLoadingTokenInfo } = useInfo ( ticker . toUpperCase ( ) ) ;
166160
167161 const handleSubmit = ( ) => {
168162 setHasUserInteracted ( true ) ;
@@ -177,16 +171,27 @@ const WithdrawRepay: React.FC<WithdrawRepayProps> = ({
177171
178172 // If unlending (withdrawing), multiply by the oToken rate to send correct oToken amount to protocol
179173 if ( mode === "withdraw" ) {
180- if ( isLoadingOTokenBalance ) {
181- setNotLoadedBalance ( "oToken" ) ;
182- return ;
174+ if ( lentBalance && oTokenBalance && Quantity . le ( lentBalance , quantity ) ) {
175+ quantity = oTokenBalance ;
176+ } else {
177+ if ( isLoadingTokenInfo || ! tokenInfo ) return ;
178+
179+ // x _underlying_ = x * totalSupply / totalPooled _oToken_
180+ const { collateralDenomination, denomination } = tokenInfo ;
181+ const totalPooled = new Quantity (
182+ BigInt ( tokenInfo . cash ) + BigInt ( tokenInfo . totalBorrows ) - BigInt ( tokenInfo . totalReserves ) ,
183+ BigInt ( collateralDenomination )
184+ ) ;
185+ const totalSupply = new Quantity ( tokenInfo . totalSupply , BigInt ( denomination ) ) ;
186+ quantity = Quantity . __convert (
187+ Quantity . __div ( Quantity . __mul ( quantity , totalSupply ) , totalPooled ) ,
188+ BigInt ( denomination )
189+ ) ;
190+
191+ if ( oTokenBalance ) {
192+ quantity = Quantity . min ( quantity , oTokenBalance ) ! ;
193+ }
183194 }
184-
185- const userOTokenRate = Number ( oTokenBalance ) / Number ( lentBalance ) ;
186- const oTokenAmount = Number ( quantity ) * userOTokenRate ;
187- quantity = new Quantity ( 0n , currentBalance ?. denomination ) . fromNumber (
188- oTokenAmount ,
189- ) ;
190195 }
191196
192197 const params = {
@@ -225,16 +230,14 @@ const WithdrawRepay: React.FC<WithdrawRepayProps> = ({
225230 ticker = { ticker }
226231 tokenPrice = { tokenPrice }
227232 // @ts -ignore, logic relies on undefined to show skeleton loading
228- walletBalance = { mode === "withdraw" ? oTokenBalance : currentBalance }
233+ walletBalance = { currentBalance }
229234 onMaxClick = { handleMaxClick }
230235 denomination = { currentBalance ?. denomination || 12n }
231- oToken = { mode === "withdraw" ? true : false }
232236 />
233237
234238 < PercentagePicker
235239 mode = { mode }
236- selectedPercentage = { selectedPercentage }
237- currentPercentage = { getCurrentPercentage ( ) }
240+ currentPercentage = { currentPercentage }
238241 onPercentageClick = { handlePercentageClick }
239242 // @ts -ignore, logic relies on undefined to disable percentage picker
240243 walletBalance = { currentBalance }
@@ -343,7 +346,8 @@ const WithdrawRepay: React.FC<WithdrawRepayProps> = ({
343346 parseFloat ( inputValue ) <= 0 ||
344347 loadingScreenState . submitStatus === "loading" ||
345348 ( mode === "withdraw" && valueLimitReached ) ||
346- cooldownData ?. onCooldown
349+ cooldownData ?. onCooldown ||
350+ ( ! tokenInfo && mode === "withdraw" )
347351 }
348352 loading = { isRepaying || isUnlending }
349353 submitText = { mode === "withdraw" ? "Withdraw" : "Repay" }
0 commit comments