v3-migration.page.tsx
1 import { Trans } from '@lingui/macro'; 2 import { Box } from '@mui/material'; 3 import dynamic from 'next/dynamic'; 4 import { useRouter } from 'next/router'; 5 import { useEffect, useState } from 'react'; 6 import { ConnectWalletPaper } from 'src/components/ConnectWalletPaper'; 7 import { ContentContainer } from 'src/components/ContentContainer'; 8 import { getMarketInfoById } from 'src/components/MarketSwitcher'; 9 import { useUserMigrationReserves } from 'src/hooks/migration/useUserMigrationReserves'; 10 import { useUserSummaryAfterMigration } from 'src/hooks/migration/useUserSummaryAfterMigration'; 11 import { useUserPoolReservesHumanized } from 'src/hooks/pool/useUserPoolReserves'; 12 import { useUserSummaryAndIncentives } from 'src/hooks/pool/useUserSummaryAndIncentives'; 13 import { MainLayout } from 'src/layouts/MainLayout'; 14 import { useWeb3Context } from 'src/libs/hooks/useWeb3Context'; 15 import { DashboardContentNoData } from 'src/modules/dashboard/DashboardContentNoData'; 16 import { MigrationBottomPanel } from 'src/modules/migration/MigrationBottomPanel'; 17 import { MigrationListBorrowItem } from 'src/modules/migration/MigrationListBorrowItem'; 18 import { MigrationListItem } from 'src/modules/migration/MigrationListItem'; 19 import { MigrationListItemLoader } from 'src/modules/migration/MigrationListItemLoader'; 20 import { MigrationLists } from 'src/modules/migration/MigrationLists'; 21 import { MigrationTopPanel } from 'src/modules/migration/MigrationTopPanel'; 22 import { selectCurrentChainIdV3MarketData } from 'src/store/poolSelectors'; 23 import { useRootStore } from 'src/store/root'; 24 import { 25 CustomMarket, 26 getNetworkConfig, 27 MarketDataType, 28 marketsData, 29 } from 'src/utils/marketsAndNetworksConfig'; 30 import { useShallow } from 'zustand/shallow'; 31 32 const MigrateV3Modal = dynamic(() => 33 import('src/components/transactions/MigrateV3/MigrateV3Modal').then( 34 (module) => module.MigrateV3Modal 35 ) 36 ); 37 38 const AAVE_MARKETS_TO_MIGRATE = Object.keys(marketsData) 39 .map((key) => { 40 const market = marketsData[key]; 41 return { 42 ...market, 43 }; 44 }) 45 .filter((market) => market.addresses.V3_MIGRATOR); 46 47 const selectableMarkets = [ 48 { 49 title: 'Aave V2 Markets', 50 markets: AAVE_MARKETS_TO_MIGRATE, 51 }, 52 ]; 53 54 export default function V3Migration() { 55 const { currentAccount } = useWeb3Context(); 56 const router = useRouter(); 57 const [fromMarketData, setFromMarketData] = useState<MarketDataType>(() => { 58 if (router.query.market) { 59 const { market } = getMarketInfoById(router.query.market as CustomMarket); 60 const migrationMarket = AAVE_MARKETS_TO_MIGRATE.find( 61 (migrationMarket) => 62 migrationMarket.isFork === market.isFork && migrationMarket.chainId === market.chainId 63 ); 64 if (migrationMarket) { 65 return market; 66 } 67 } 68 return AAVE_MARKETS_TO_MIGRATE[0]; 69 }); 70 const [ 71 selectAllSupply, 72 selectAllBorrow, 73 toggleSelectedSupplyPosition, 74 selectedSupplyAssets, 75 toggleSelectedBorrowPosition, 76 selectedBorrowAssets, 77 resetMigrationSelectedAssets, 78 enforceAsCollateral, 79 ] = useRootStore( 80 useShallow((store) => [ 81 store.selectAllSupply, 82 store.selectAllBorrow, 83 store.toggleMigrationSelectedSupplyAsset, 84 store.selectedMigrationSupplyAssets, 85 store.toggleMigrationSelectedBorrowAsset, 86 store.selectedMigrationBorrowAssets, 87 store.resetMigrationSelectedAssets, 88 store.enforceAsCollateral, 89 ]) 90 ); 91 92 const toMarketData = selectCurrentChainIdV3MarketData( 93 fromMarketData.chainId, 94 getNetworkConfig(fromMarketData.chainId) 95 ); 96 97 const { data: userMigrationReserves, isPending: userMigrationReservesLoading } = 98 useUserMigrationReserves(fromMarketData, toMarketData); 99 100 const supplyReserves = userMigrationReserves?.supplyReserves || []; 101 const borrowReserves = userMigrationReserves?.borrowReserves || []; 102 const isolatedReserveV3 = userMigrationReserves?.isolatedReserveV3; 103 104 const { data: fromUserSummaryAndIncentives, isPending: fromUserSummaryAndIncentivesLoading } = 105 useUserSummaryAndIncentives(fromMarketData); 106 107 const { data: toUserReservesData, isPending: toUserReservesDataLoading } = 108 useUserPoolReservesHumanized(toMarketData); 109 const { data: toUserSummaryForMigration, isPending: toUserSummaryForMigrationLoading } = 110 useUserSummaryAndIncentives(toMarketData); 111 const toUserEModeCategoryId = toUserReservesData?.userEmodeCategoryId || 0; 112 113 const { data: userSummaryAfterMigration, isPending: userSummaryAfterMigrationLoading } = 114 useUserSummaryAfterMigration(fromMarketData, toMarketData); 115 116 const loading = 117 userMigrationReservesLoading || 118 fromUserSummaryAndIncentivesLoading || 119 toUserReservesDataLoading || 120 toUserSummaryForMigrationLoading || 121 userSummaryAfterMigrationLoading; 122 123 useEffect(() => { 124 if (resetMigrationSelectedAssets) { 125 resetMigrationSelectedAssets(); 126 } 127 }, [resetMigrationSelectedAssets]); 128 129 const enabledAsCollateral = (canBeEnforced: boolean, underlyingAsset: string) => { 130 if (canBeEnforced) { 131 enforceAsCollateral(underlyingAsset); 132 } 133 }; 134 135 const handleToggleAllSupply = () => { 136 selectAllSupply(supplyReserves); 137 }; 138 139 const handleToggleAllBorrow = () => { 140 selectAllBorrow(borrowReserves); 141 }; 142 143 const userControlledCollateral = 144 selectedSupplyAssets.length > 1 && 145 toUserSummaryForMigration && 146 toUserSummaryForMigration.totalCollateralMarketReferenceCurrency == '0'; 147 148 const changeFromMarketData = (marketData: MarketDataType) => { 149 resetMigrationSelectedAssets(); 150 setFromMarketData(marketData); 151 }; 152 153 const bottomPanelProps = fromUserSummaryAndIncentives && 154 toUserSummaryForMigration && { 155 fromUserSummaryBeforeMigration: fromUserSummaryAndIncentives, 156 toUserSummaryBeforeMigration: toUserSummaryForMigration, 157 }; 158 159 return ( 160 <> 161 <MigrationTopPanel /> 162 {currentAccount ? ( 163 <ContentContainer> 164 <Box 165 sx={{ 166 display: 'flex', 167 gap: 4, 168 alignItems: 'start', 169 flexDirection: { xs: 'column', lg: 'row' }, 170 }} 171 > 172 <MigrationBottomPanel 173 userSummaryAfterMigration={userSummaryAfterMigration} 174 userSummaryBeforeMigration={bottomPanelProps} 175 disableButton={selectedSupplyAssets.length === 0 && selectedBorrowAssets.length === 0} 176 enteringIsolationMode={isolatedReserveV3?.enteringIsolationMode || false} 177 loading={loading} 178 fromMarketData={fromMarketData} 179 toMarketData={toMarketData} 180 setFromMarketData={changeFromMarketData} 181 selectableMarkets={selectableMarkets} 182 /> 183 <MigrationLists 184 loading={loading} 185 isSupplyPositionsAvailable={supplyReserves.length > 0} 186 isBorrowPositionsAvailable={borrowReserves.length > 0} 187 onSelectAllSupplies={handleToggleAllSupply} 188 onSelectAllBorrows={handleToggleAllBorrow} 189 emodeCategoryId={toUserEModeCategoryId} 190 isolatedReserveV3={isolatedReserveV3} 191 supplyReserves={supplyReserves} 192 borrowReserves={borrowReserves} 193 suppliesPositions={ 194 <> 195 {loading ? ( 196 <> 197 <MigrationListItemLoader /> 198 <MigrationListItemLoader /> 199 </> 200 ) : supplyReserves.length > 0 ? ( 201 supplyReserves.map((reserve) => ( 202 <MigrationListItem 203 key={reserve.underlyingAsset} 204 checked={ 205 selectedSupplyAssets.findIndex( 206 (selectedAsset) => 207 selectedAsset.underlyingAsset == reserve.underlyingAsset 208 ) >= 0 209 } 210 enableAsCollateral={() => 211 enabledAsCollateral(reserve.canBeEnforced, reserve.underlyingAsset) 212 } 213 userControlledCollateral={userControlledCollateral} 214 canBeEnforced={ 215 toUserSummaryForMigration && 216 toUserSummaryForMigration.totalCollateralMarketReferenceCurrency == '0' && 217 reserve.canBeEnforced 218 } 219 userReserve={reserve} 220 amount={reserve.underlyingBalance} 221 amountInUSD={reserve.underlyingBalanceUSD} 222 onCheckboxClick={() => { 223 toggleSelectedSupplyPosition(reserve.underlyingAsset); 224 }} 225 enabledAsCollateral={reserve.usageAsCollateralEnabledOnUserV3} 226 isIsolated={reserve.isolatedOnV3} 227 enteringIsolation={isolatedReserveV3?.enteringIsolationMode || false} 228 v3Rates={reserve.v3Rates} 229 disabled={reserve.migrationDisabled} 230 isSupplyList 231 /> 232 )) 233 ) : ( 234 <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}> 235 <DashboardContentNoData text={<Trans>Nothing supplied yet</Trans>} /> 236 </Box> 237 )} 238 </> 239 } 240 borrowsPositions={ 241 <> 242 {loading ? ( 243 <> 244 <MigrationListItemLoader /> 245 <MigrationListItemLoader /> 246 </> 247 ) : borrowReserves.length > 0 ? ( 248 borrowReserves.map((reserve) => ( 249 <MigrationListBorrowItem 250 key={reserve.debtKey} 251 userReserve={reserve} 252 selectedBorrowAssets={selectedBorrowAssets} 253 toggleSelectedBorrowPosition={toggleSelectedBorrowPosition} 254 v3Rates={reserve.v3Rates} 255 enteringIsolation={isolatedReserveV3?.enteringIsolationMode || false} 256 /> 257 )) 258 ) : ( 259 <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}> 260 <DashboardContentNoData text={<Trans>Nothing borrowed yet</Trans>} /> 261 </Box> 262 )} 263 </> 264 } 265 /> 266 </Box> 267 </ContentContainer> 268 ) : ( 269 <ConnectWalletPaper 270 description={<Trans> Please connect your wallet to see migration tool.</Trans>} 271 /> 272 )} 273 </> 274 ); 275 } 276 277 V3Migration.getLayout = function getLayout(page: React.ReactElement) { 278 return ( 279 <MainLayout> 280 {page} 281 <MigrateV3Modal /> 282 </MainLayout> 283 ); 284 };