From 96ff914eb5c63eb197ff272f6d9b430d38d67fb4 Mon Sep 17 00:00:00 2001 From: Piotr Matlak Date: Fri, 12 Apr 2024 15:35:59 +0200 Subject: [PATCH 1/2] fix crashing app when spamming on fee tier --- .../FarmsList/StakeTile/StakeTile.tsx | 12 +++++-- src/components/Swap/Swap.tsx | 12 +++++-- .../NewPositionWrapper/NewPositionWrapper.tsx | 33 +++++++++++++++---- src/containers/WrappedSwap/WrappedSwap.tsx | 12 +++++-- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/components/FarmsList/StakeTile/StakeTile.tsx b/src/components/FarmsList/StakeTile/StakeTile.tsx index be9566b62..d353ee634 100644 --- a/src/components/FarmsList/StakeTile/StakeTile.tsx +++ b/src/components/FarmsList/StakeTile/StakeTile.tsx @@ -46,6 +46,9 @@ export const StakeTile: React.FC = ({ const [progress, setProgress] = useState('none') useEffect(() => { + let timeout1: any + let timeout2: any + if (typeof stakeStatus === 'undefined') { return } @@ -53,16 +56,21 @@ export const StakeTile: React.FC = ({ if (!stakeStatus.inProgress && progress === 'progress') { setProgress(stakeStatus.success ? 'approvedWithSuccess' : 'approvedWithFail') - setTimeout(() => { + timeout1 = setTimeout(() => { setProgress(stakeStatus.success ? 'success' : 'failed') }, 1500) - setTimeout(() => { + timeout2 = setTimeout(() => { setProgress('none') }, 3000) } else if (stakeStatus.inProgress && progress !== 'progress') { setProgress('progress') } + + return () => { + clearTimeout(timeout1) + clearTimeout(timeout2) + } }, [stakeStatus]) const data = xToY diff --git a/src/components/Swap/Swap.tsx b/src/components/Swap/Swap.tsx index 7f2fcae09..89d6200e9 100644 --- a/src/components/Swap/Swap.tsx +++ b/src/components/Swap/Swap.tsx @@ -423,9 +423,17 @@ export const Swap: React.FC = ({ setDetailsOpen(!detailsOpen) } - React.useEffect(() => { + useEffect(() => { + let timerId: any + if (lockAnimation) { - setTimeout(() => setLockAnimation(false), 500) + timerId = setTimeout(() => setLockAnimation(false), 500) + } + + return () => { + if (timerId) { + clearTimeout(timerId) + } } }, [lockAnimation]) diff --git a/src/containers/NewPositionWrapper/NewPositionWrapper.tsx b/src/containers/NewPositionWrapper/NewPositionWrapper.tsx index 992e23d2a..e7c5f32a3 100644 --- a/src/containers/NewPositionWrapper/NewPositionWrapper.tsx +++ b/src/containers/NewPositionWrapper/NewPositionWrapper.tsx @@ -36,7 +36,7 @@ import { PublicKey } from '@solana/web3.js' import { getCurrentSolanaConnection, networkTypetoProgramNetwork } from '@web3/connection' import { openWalletSelectorModal } from '@web3/selector' import { History } from 'history' -import React, { useEffect, useMemo, useState } from 'react' +import React, { useEffect, useMemo, useRef, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' export interface IProps { @@ -89,6 +89,9 @@ export const NewPositionWrapper: React.FC = ({ }, [poolIndex]) useEffect(() => { + let timerId1: any + let timerId2: any + if (!inProgress && progress === 'progress') { setProgress(success ? 'approvedWithSuccess' : 'approvedWithFail') @@ -104,14 +107,19 @@ export const NewPositionWrapper: React.FC = ({ ) } - setTimeout(() => { + timerId1 = setTimeout(() => { setProgress(success ? 'success' : 'failed') }, 1500) - setTimeout(() => { + timerId2 = setTimeout(() => { setProgress('none') }, 3000) } + + return () => { + clearTimeout(timerId1) + clearTimeout(timerId2) + } }, [success, inProgress]) const isXtoY = useMemo(() => { @@ -427,6 +435,15 @@ export const NewPositionWrapper: React.FC = ({ return poolAddress } + const isMountedRef = useRef(false) + + useEffect(() => { + // prevent update state on unmounted component + isMountedRef.current = true + return () => { + isMountedRef.current = false + } + }, []) return ( = ({ fee.eq(ALL_FEE_TIERS_DATA[feeTierIndex].tier.fee) ) ) { - setPoolIndex(index !== -1 ? index : null) - setCurrentPairReversed(null) + if (isMountedRef.current) { + setPoolIndex(index !== -1 ? index : null) + setCurrentPairReversed(null) + } } else if ( tokenAIndex === tokenB && tokenBIndex === tokenA && fee.eq(ALL_FEE_TIERS_DATA[feeTierIndex].tier.fee) ) { - setCurrentPairReversed(currentPairReversed === null ? true : !currentPairReversed) + if (isMountedRef.current) { + setCurrentPairReversed(currentPairReversed === null ? true : !currentPairReversed) + } } if (index !== -1 && index !== poolIndex) { diff --git a/src/containers/WrappedSwap/WrappedSwap.tsx b/src/containers/WrappedSwap/WrappedSwap.tsx index 26af9d7b5..8c21bf687 100644 --- a/src/containers/WrappedSwap/WrappedSwap.tsx +++ b/src/containers/WrappedSwap/WrappedSwap.tsx @@ -48,17 +48,25 @@ export const WrappedSwap = () => { const [tokenTo, setTokenTo] = useState(null) useEffect(() => { + let timerId1: any + let timerId2: any + if (!inProgress && progress === 'progress') { setProgress(success ? 'approvedWithSuccess' : 'approvedWithFail') - setTimeout(() => { + timerId1 = setTimeout(() => { setProgress(success ? 'success' : 'failed') }, 1500) - setTimeout(() => { + timerId2 = setTimeout(() => { setProgress('none') }, 3000) } + + return () => { + clearTimeout(timerId1) + clearTimeout(timerId2) + } }, [success, inProgress]) useEffect(() => { From 06918ca85c3a64d487be57bcd4494dbda1a6e90e Mon Sep 17 00:00:00 2001 From: Piotr Matlak Date: Mon, 15 Apr 2024 19:45:14 +0200 Subject: [PATCH 2/2] fix crashing app when spamming on fee tier --- .../RangeSelector/RangeSelector.tsx | 19 ++- .../NewPositionWrapper/NewPositionWrapper.tsx | 124 +++++++++--------- 2 files changed, 77 insertions(+), 66 deletions(-) diff --git a/src/components/NewPosition/RangeSelector/RangeSelector.tsx b/src/components/NewPosition/RangeSelector/RangeSelector.tsx index 647ea7593..5bc506f2e 100644 --- a/src/components/NewPosition/RangeSelector/RangeSelector.tsx +++ b/src/components/NewPosition/RangeSelector/RangeSelector.tsx @@ -1,5 +1,5 @@ import { Button, Grid, Tooltip, Typography } from '@material-ui/core' -import React, { useState, useEffect, useMemo } from 'react' +import React, { useState, useEffect, useMemo, useRef } from 'react' import PriceRangePlot, { TickPlotPositionData } from '@components/PriceRangePlot/PriceRangePlot' import RangeInput from '@components/Inputs/RangeInput/RangeInput' import { @@ -87,6 +87,15 @@ export const RangeSelector: React.FC = ({ const [concentrationIndex, setConcentrationIndex] = useState(0) + const isMountedRef = useRef(false) + + useEffect(() => { + isMountedRef.current = true + return () => { + isMountedRef.current = false + } + }, []) + const zoomMinus = () => { const diff = plotMax - plotMin const newMin = plotMin - diff / 4 @@ -204,13 +213,13 @@ export const RangeSelector: React.FC = ({ } useEffect(() => { - if (currentPairReversed !== null) { + if (currentPairReversed !== null && isMountedRef.current) { reversePlot() } }, [currentPairReversed]) useEffect(() => { - if (ticksLoading) { + if (ticksLoading && isMountedRef.current) { resetPlot() } }, [ticksLoading, midPrice]) @@ -264,7 +273,7 @@ export const RangeSelector: React.FC = ({ ) useEffect(() => { - if (isConcentrated) { + if (isConcentrated && isMountedRef.current) { setConcentrationIndex(0) const { leftRange, rightRange } = calculateConcentrationRange( @@ -280,7 +289,7 @@ export const RangeSelector: React.FC = ({ }, [isConcentrated]) useEffect(() => { - if (isConcentrated && !ticksLoading) { + if (isConcentrated && !ticksLoading && isMountedRef.current) { const index = concentrationIndex > concentrationArray.length - 1 ? concentrationArray.length - 1 diff --git a/src/containers/NewPositionWrapper/NewPositionWrapper.tsx b/src/containers/NewPositionWrapper/NewPositionWrapper.tsx index e7c5f32a3..ccc52929c 100644 --- a/src/containers/NewPositionWrapper/NewPositionWrapper.tsx +++ b/src/containers/NewPositionWrapper/NewPositionWrapper.tsx @@ -14,7 +14,6 @@ import { printBN } from '@consts/utils' import { MAX_TICK, Pair, calculatePriceSqrt, getMarketAddress } from '@invariant-labs/sdk' -import { Decimal } from '@invariant-labs/sdk/lib/market' import { DECIMAL } from '@invariant-labs/sdk/lib/utils' import { getLiquidityByX, getLiquidityByY } from '@invariant-labs/sdk/src/math' import { feeToTickSpacing } from '@invariant-labs/sdk/src/utils' @@ -73,8 +72,6 @@ export const NewPositionWrapper: React.FC = ({ const [poolIndex, setPoolIndex] = useState(null) - const [liquidity, setLiquidity] = useState({ v: new BN(0) }) - const [progress, setProgress] = useState('none') const [tokenAIndex, setTokenAIndex] = useState(null) @@ -84,6 +81,17 @@ export const NewPositionWrapper: React.FC = ({ const [globalPrice, setGlobalPrice] = useState(undefined) + const isMountedRef = useRef(false) + + useEffect(() => { + isMountedRef.current = true + return () => { + isMountedRef.current = false + } + }, []) + + const liquidityRef = useRef({ v: new BN(0) }) + useEffect(() => { setProgress('none') }, [poolIndex]) @@ -435,15 +443,58 @@ export const NewPositionWrapper: React.FC = ({ return poolAddress } - const isMountedRef = useRef(false) - useEffect(() => { - // prevent update state on unmounted component - isMountedRef.current = true - return () => { - isMountedRef.current = false + const calcAmount = (amount: BN, left: number, right: number, tokenAddress: PublicKey) => { + if (tokenAIndex === null || tokenBIndex === null || isNaN(left) || isNaN(right)) { + return new BN(0) } - }, []) + + const byX = tokenAddress.equals( + isXtoY ? tokens[tokenAIndex].assetAddress : tokens[tokenBIndex].assetAddress + ) + const lowerTick = Math.min(left, right) + const upperTick = Math.max(left, right) + + try { + if (byX) { + const result = getLiquidityByX( + amount, + lowerTick, + upperTick, + poolIndex !== null ? allPools[poolIndex].sqrtPrice : calculatePriceSqrt(midPrice.index), + true + ) + if (isMountedRef.current) { + liquidityRef.current = result.liquidity + } + return result.y + } + const result = getLiquidityByY( + amount, + lowerTick, + upperTick, + poolIndex !== null ? allPools[poolIndex].sqrtPrice : calculatePriceSqrt(midPrice.index), + true + ) + if (isMountedRef.current) { + liquidityRef.current = result.liquidity + } + return result.x + } catch (error) { + const result = (byX ? getLiquidityByY : getLiquidityByX)( + amount, + lowerTick, + upperTick, + poolIndex !== null ? allPools[poolIndex].sqrtPrice : calculatePriceSqrt(midPrice.index), + true + ) + if (isMountedRef.current) { + liquidityRef.current = result.liquidity + } + } + + return new BN(0) + } return ( = ({ fee, lowerTick, upperTick, - liquidityDelta: liquidity, + liquidityDelta: liquidityRef.current, initPool: poolIndex === null, initTick: poolIndex === null ? midPrice.index : undefined, xAmount: Math.floor(xAmount), @@ -566,56 +617,7 @@ export const NewPositionWrapper: React.FC = ({ ) }} isCurrentPoolExisting={poolIndex !== null} - calcAmount={(amount, left, right, tokenAddress) => { - if (tokenAIndex === null || tokenBIndex === null || isNaN(left) || isNaN(right)) { - return new BN(0) - } - - const byX = tokenAddress.equals( - isXtoY ? tokens[tokenAIndex].assetAddress : tokens[tokenBIndex].assetAddress - ) - const lowerTick = Math.min(left, right) - const upperTick = Math.max(left, right) - - try { - if (byX) { - const result = getLiquidityByX( - amount, - lowerTick, - upperTick, - poolIndex !== null - ? allPools[poolIndex].sqrtPrice - : calculatePriceSqrt(midPrice.index), - true - ) - setLiquidity(result.liquidity) - - return result.y - } - - const result = getLiquidityByY( - amount, - lowerTick, - upperTick, - poolIndex !== null ? allPools[poolIndex].sqrtPrice : calculatePriceSqrt(midPrice.index), - true - ) - setLiquidity(result.liquidity) - - return result.x - } catch (error) { - const result = (byX ? getLiquidityByY : getLiquidityByX)( - amount, - lowerTick, - upperTick, - poolIndex !== null ? allPools[poolIndex].sqrtPrice : calculatePriceSqrt(midPrice.index), - true - ) - setLiquidity(result.liquidity) - } - - return new BN(0) - }} + calcAmount={calcAmount} ticksLoading={ticksLoading} showNoConnected={walletStatus !== Status.Initialized} noConnectedBlockerProps={{