From a5d75bc93cffd9e0246d1b2fd3e954a58ed667c9 Mon Sep 17 00:00:00 2001 From: Sven Date: Mon, 27 Jan 2025 00:04:12 +0800 Subject: [PATCH] add swap & fix bug (#3225) * add swap & fix bug * add liquidity * fix bug * fix bug * fix --- infra/rooch-portal-v2/package.json | 1 + infra/rooch-portal-v2/src/app/apps/page.tsx | 5 +- .../src/app/trade/liquidity/page.tsx | 9 + .../app/trade/{ => market}/[tick]/page.tsx | 2 +- .../src/app/trade/{ => market}/page.tsx | 2 +- .../src/app/trade/swap/page.tsx | 15 + .../src/components/guard/WalletGuard.tsx | 8 +- .../components/market/inscription-card.tsx | 1 - .../market/inscription-shop-card.tsx | 1 - .../src/components/market/list-dialog.tsx | 4 +- .../swap/swap-transaction-header.tsx | 50 ++ .../src/components/swap/swap.tsx | 2 +- .../rooch-portal-v2/src/hooks/use-networks.ts | 6 + .../src/layouts/config-nav-dashboard.tsx | 34 +- infra/rooch-portal-v2/src/routes/paths.ts | 4 +- .../src/sections/apps/view.tsx | 4 +- .../invitations/components/lottery-list.tsx | 2 +- .../src/sections/inviter/index.tsx | 2 +- .../trade/components/select-token-pair.tsx | 349 ++++++++++++ .../sections/trade/components/token-pair.tsx | 87 +++ .../src/sections/trade/components/types.ts | 8 + .../trade/liquidity/components/icon.tsx | 18 + .../liquidity/list/add-liquidity-modal.tsx | 511 ++++++++++++++++++ .../liquidity/list/all-liquidity-row-item.tsx | 62 +++ .../liquidity/list/all_liquidity_list.tsx | 242 +++++++++ .../list/owner-liquidity-row-item.tsx | 74 +++ .../liquidity/list/owner_liquidity_list.tsx | 138 +++++ .../liquidity/list/remove-liquidity-modal.tsx | 206 +++++++ .../sections/trade/liquidity/list/view.tsx | 40 ++ .../src/sections/trade/liquidity/view.tsx | 9 + .../sections/trade/{ => market}/home-view.tsx | 4 +- .../src/sections/trade/{ => market}/view.tsx | 4 +- .../src/sections/trade/swap/confirm-modal.tsx | 165 ++++++ .../src/sections/trade/swap/view.tsx | 125 +++++ .../rooch-portal-v2/src/sections/tx/view.tsx | 2 +- infra/rooch-portal-v2/src/utils/number.ts | 2 +- pnpm-lock.yaml | 358 ++++++++++-- 37 files changed, 2486 insertions(+), 70 deletions(-) create mode 100644 infra/rooch-portal-v2/src/app/trade/liquidity/page.tsx rename infra/rooch-portal-v2/src/app/trade/{ => market}/[tick]/page.tsx (74%) rename infra/rooch-portal-v2/src/app/trade/{ => market}/page.tsx (76%) create mode 100644 infra/rooch-portal-v2/src/app/trade/swap/page.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/components/select-token-pair.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/components/token-pair.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/components/types.ts create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/components/icon.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/list/add-liquidity-modal.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/list/all-liquidity-row-item.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/list/all_liquidity_list.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/list/owner-liquidity-row-item.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/list/owner_liquidity_list.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/list/remove-liquidity-modal.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/list/view.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/liquidity/view.tsx rename infra/rooch-portal-v2/src/sections/trade/{ => market}/home-view.tsx (91%) rename infra/rooch-portal-v2/src/sections/trade/{ => market}/view.tsx (99%) create mode 100644 infra/rooch-portal-v2/src/sections/trade/swap/confirm-modal.tsx create mode 100644 infra/rooch-portal-v2/src/sections/trade/swap/view.tsx diff --git a/infra/rooch-portal-v2/package.json b/infra/rooch-portal-v2/package.json index c8fea5a985..6d2e3d940f 100644 --- a/infra/rooch-portal-v2/package.json +++ b/infra/rooch-portal-v2/package.json @@ -37,6 +37,7 @@ "@iconify/react": "^5.0.1", "@mui/lab": "^5.0.0-alpha.170", "@mui/material": "^5.15.20", + "@mui/icons-material": "6.4.0", "@mui/material-nextjs": "^5.15.11", "@mui/x-data-grid": "^7.7.0", "@mui/x-date-pickers": "^7.7.0", diff --git a/infra/rooch-portal-v2/src/app/apps/page.tsx b/infra/rooch-portal-v2/src/app/apps/page.tsx index aaddcee9e9..1f2827db22 100644 --- a/infra/rooch-portal-v2/src/app/apps/page.tsx +++ b/infra/rooch-portal-v2/src/app/apps/page.tsx @@ -1,4 +1,7 @@ -import AppsView, { Project } from 'src/sections/apps/view'; +import type { Project } from 'src/sections/apps/view'; + +import AppsView from 'src/sections/apps/view'; + import { getAvatar } from '../../utils/avatar'; export const metadata = { title: `Apps` }; diff --git a/infra/rooch-portal-v2/src/app/trade/liquidity/page.tsx b/infra/rooch-portal-v2/src/app/trade/liquidity/page.tsx new file mode 100644 index 0000000000..55ae3df377 --- /dev/null +++ b/infra/rooch-portal-v2/src/app/trade/liquidity/page.tsx @@ -0,0 +1,9 @@ +import LiquidityPage from 'src/sections/trade/liquidity/view'; + +export const metadata = { + title: 'liquidity', +}; + +export default function Page() { + return ; +} diff --git a/infra/rooch-portal-v2/src/app/trade/[tick]/page.tsx b/infra/rooch-portal-v2/src/app/trade/market/[tick]/page.tsx similarity index 74% rename from infra/rooch-portal-v2/src/app/trade/[tick]/page.tsx rename to infra/rooch-portal-v2/src/app/trade/market/[tick]/page.tsx index 9b5c8abab4..437f1f30f2 100644 --- a/infra/rooch-portal-v2/src/app/trade/[tick]/page.tsx +++ b/infra/rooch-portal-v2/src/app/trade/market/[tick]/page.tsx @@ -1,4 +1,4 @@ -import MarketplaceView from 'src/sections/trade/view'; +import MarketplaceView from 'src/sections/trade/market/view'; export const metadata = { title: 'Market | Orderbook', diff --git a/infra/rooch-portal-v2/src/app/trade/page.tsx b/infra/rooch-portal-v2/src/app/trade/market/page.tsx similarity index 76% rename from infra/rooch-portal-v2/src/app/trade/page.tsx rename to infra/rooch-portal-v2/src/app/trade/market/page.tsx index cdfca5d61a..8fcdcbef4a 100644 --- a/infra/rooch-portal-v2/src/app/trade/page.tsx +++ b/infra/rooch-portal-v2/src/app/trade/market/page.tsx @@ -1,4 +1,4 @@ -import HomeView from 'src/sections/trade/home-view'; +import HomeView from 'src/sections/trade/market/home-view'; // ---------------------------------------------------------------------- diff --git a/infra/rooch-portal-v2/src/app/trade/swap/page.tsx b/infra/rooch-portal-v2/src/app/trade/swap/page.tsx new file mode 100644 index 0000000000..11f5935817 --- /dev/null +++ b/infra/rooch-portal-v2/src/app/trade/swap/page.tsx @@ -0,0 +1,15 @@ +import WalletGuard from 'src/components/guard/WalletGuard'; + +import PoolView from 'src/sections/trade/swap/view'; + +export const metadata = { + title: 'swap', +}; + +export default function Page({ params }: { params: { tick: string } }) { + return ( + + + + ); +} diff --git a/infra/rooch-portal-v2/src/components/guard/WalletGuard.tsx b/infra/rooch-portal-v2/src/components/guard/WalletGuard.tsx index df2755982a..5a8aeef36e 100644 --- a/infra/rooch-portal-v2/src/components/guard/WalletGuard.tsx +++ b/infra/rooch-portal-v2/src/components/guard/WalletGuard.tsx @@ -11,7 +11,7 @@ import { DashboardContent } from 'src/layouts/dashboard'; import { Iconify } from '../iconify'; export default function CustomWalletGuard({ children }: { children: ReactNode }) { - const { status } = useCurrentWallet(); + const { status, wallet } = useCurrentWallet(); if (status === 'connected') { return children; @@ -47,7 +47,11 @@ export default function CustomWalletGuard({ children }: { children: ReactNode }) spacing={2} > - {}}> + { + console.log('hahah'); + }} + > diff --git a/infra/rooch-portal-v2/src/components/market/inscription-card.tsx b/infra/rooch-portal-v2/src/components/market/inscription-card.tsx index ed825af727..d5dab98f00 100644 --- a/infra/rooch-portal-v2/src/components/market/inscription-card.tsx +++ b/infra/rooch-portal-v2/src/components/market/inscription-card.tsx @@ -1,4 +1,3 @@ -import Link from 'next/link'; import { yellow } from '@mui/material/colors'; import { Chip, Stack, Typography } from '@mui/material'; diff --git a/infra/rooch-portal-v2/src/components/market/inscription-shop-card.tsx b/infra/rooch-portal-v2/src/components/market/inscription-shop-card.tsx index 2d685a2953..c2d64bfe1a 100644 --- a/infra/rooch-portal-v2/src/components/market/inscription-shop-card.tsx +++ b/infra/rooch-portal-v2/src/components/market/inscription-shop-card.tsx @@ -1,6 +1,5 @@ import type { BalanceInfoView } from '@roochnetwork/rooch-sdk'; -import Link from 'next/link'; import BigNumber from 'bignumber.js'; import { useCurrentAddress } from '@roochnetwork/rooch-sdk-kit'; diff --git a/infra/rooch-portal-v2/src/components/market/list-dialog.tsx b/infra/rooch-portal-v2/src/components/market/list-dialog.tsx index d4cbee4fbd..5ccad87953 100644 --- a/infra/rooch-portal-v2/src/components/market/list-dialog.tsx +++ b/infra/rooch-portal-v2/src/components/market/list-dialog.tsx @@ -21,7 +21,7 @@ import { InputAdornment, } from '@mui/material'; -import { toDust, fromDust, formatNumber, formatCoin } from 'src/utils/number'; +import { toDust, fromDust, formatNumber } from 'src/utils/number'; import { warning, secondary } from 'src/theme/core'; @@ -29,8 +29,6 @@ import { toast } from 'src/components/snackbar'; import InscriptionCard from './inscription-card'; import { useNetworkVariable } from '../../hooks/use-networks'; -import { formatUnitPrice } from '../../utils/marketplace'; -import { GAS_COIN_DECIMALS } from '../../config/constant'; export default function ListDialog({ listDialogOpen, diff --git a/infra/rooch-portal-v2/src/components/swap/swap-transaction-header.tsx b/infra/rooch-portal-v2/src/components/swap/swap-transaction-header.tsx index 4cf54b8ec0..e17d35eebe 100644 --- a/infra/rooch-portal-v2/src/components/swap/swap-transaction-header.tsx +++ b/infra/rooch-portal-v2/src/components/swap/swap-transaction-header.tsx @@ -18,6 +18,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + import { Box, Stack, Typography } from '@mui/material'; import { grey } from 'src/theme/core'; diff --git a/infra/rooch-portal-v2/src/components/swap/swap.tsx b/infra/rooch-portal-v2/src/components/swap/swap.tsx index 2673cf2070..8a0d143311 100644 --- a/infra/rooch-portal-v2/src/components/swap/swap.tsx +++ b/infra/rooch-portal-v2/src/components/swap/swap.tsx @@ -56,7 +56,7 @@ export default function Swap({ () => [fromCoin?.coinType || '', toCoin?.coinType || ''], [fromCoin?.coinType, toCoin?.coinType] ); - const memPool = useNetworkVariable('gasMarket').memPool + const {memPool} = useNetworkVariable('gasMarket') const showDetails = useMemo( () => diff --git a/infra/rooch-portal-v2/src/hooks/use-networks.ts b/infra/rooch-portal-v2/src/hooks/use-networks.ts index dd637b4779..23e48fe607 100644 --- a/infra/rooch-portal-v2/src/hooks/use-networks.ts +++ b/infra/rooch-portal-v2/src/hooks/use-networks.ts @@ -48,6 +48,9 @@ const market = (network: 'test' | 'main') => ({ }; }, }); +const dex = { + address: ROOCH_MULTI_SIG_ADDRESS, +}; const { networkConfig, useNetworkVariable, useNetworkVariables } = createNetworkConfig({ mainnet: { @@ -71,6 +74,7 @@ const { networkConfig, useNetworkVariable, useNetworkVariables } = createNetwork faucet: faucet('main'), inviter, market: market('main'), + dex, }, }, testnet: { @@ -94,6 +98,7 @@ const { networkConfig, useNetworkVariable, useNetworkVariables } = createNetwork faucet: faucet('test'), inviter, market: market('test'), + dex, }, }, localnet: { @@ -117,6 +122,7 @@ const { networkConfig, useNetworkVariable, useNetworkVariables } = createNetwork faucet: faucet('test'), inviter, market: market('test'), + dex, }, }, }); diff --git a/infra/rooch-portal-v2/src/layouts/config-nav-dashboard.tsx b/infra/rooch-portal-v2/src/layouts/config-nav-dashboard.tsx index aa3e641d5e..f7b832b245 100644 --- a/infra/rooch-portal-v2/src/layouts/config-nav-dashboard.tsx +++ b/infra/rooch-portal-v2/src/layouts/config-nav-dashboard.tsx @@ -14,28 +14,28 @@ export const navData = [ { title: 'Account', path: paths.dashboard.account, - icon: , + icon: , }, { title: 'Assets', path: paths.dashboard.assets, - icon: , + icon: , }, { title: 'Transactions', path: paths.dashboard.transactions, - icon: , + icon: , }, { title: 'Purchase Gas', path: paths.dashboard['gas-swap'], - icon: , + icon: , noAddressRequired: true, }, { title: 'Faucet', path: paths.dashboard.faucet, - icon: , + icon: , }, { title: 'Invitation', @@ -45,7 +45,7 @@ export const navData = [ { title: 'Settings', path: paths.dashboard.settings, - icon: , + icon: , noAddressRequired: true, connectWalletRequired: false, }, @@ -59,8 +59,20 @@ export const navData = [ items: [ { title: 'Marketplace', - path: paths.dashboard.trade, - icon: , + path: paths.dashboard.market, + icon: , + noAddressRequired: true, + }, + { + title: 'Liquidity', + path: paths.dashboard.liquidity, + icon: , + noAddressRequired: true, + }, + { + title: 'Swap', + path: paths.dashboard.swap, + icon: , noAddressRequired: true, }, ], @@ -88,13 +100,13 @@ export const navData = [ { title: 'Search', path: paths.dashboard.search, - icon: , + icon: , noAddressRequired: true, }, { title: 'Apps', path: paths.dashboard.apps, - icon: , + icon: , noAddressRequired: true, }, { @@ -105,7 +117,7 @@ export const navData = [ icon: , noAddressRequired: true, }, - ]// .filter((item) => !(isMainNetwork() && item.title === 'Apps')), + ], // .filter((item) => !(isMainNetwork() && item.title === 'Apps')), }, ].filter((item) => !(isMainNetwork() && item.subheader === 'Tokens')); // .filter((item) => !(isMainNetwork() && (item.subheader === 'Tokens' || item.subheader === 'Trade'))); diff --git a/infra/rooch-portal-v2/src/routes/paths.ts b/infra/rooch-portal-v2/src/routes/paths.ts index 41ab4718e7..d516926a78 100644 --- a/infra/rooch-portal-v2/src/routes/paths.ts +++ b/infra/rooch-portal-v2/src/routes/paths.ts @@ -9,7 +9,9 @@ export const paths = { assets: `${ROOTS.DASHBOARD}/assets`, mint: `${ROOTS.DASHBOARD}/mint`, transactions: `${ROOTS.DASHBOARD}/transactions`, - trade: `${ROOTS.DASHBOARD}/trade`, + market: `${ROOTS.DASHBOARD}/trade/market`, + liquidity: `${ROOTS.DASHBOARD}/trade/liquidity`, + swap: `${ROOTS.DASHBOARD}/trade/swap`, apps: `${ROOTS.DASHBOARD}/apps`, settings: `${ROOTS.DASHBOARD}/settings`, search: `${ROOTS.DASHBOARD}/search`, diff --git a/infra/rooch-portal-v2/src/sections/apps/view.tsx b/infra/rooch-portal-v2/src/sections/apps/view.tsx index 771f1a1a4f..432cc6ea1e 100644 --- a/infra/rooch-portal-v2/src/sections/apps/view.tsx +++ b/infra/rooch-portal-v2/src/sections/apps/view.tsx @@ -1,12 +1,12 @@ 'use client'; +import { useCurrentNetwork } from '@roochnetwork/rooch-sdk-kit'; + import Typography from '@mui/material/Typography'; import { Box, Card, Stack, Button, CardHeader, CardContent } from '@mui/material'; import { CONFIG } from 'src/config-global'; import { DashboardContent } from 'src/layouts/dashboard'; -import Badge from '@mui/material/Badge'; -import { useCurrentNetwork } from '@roochnetwork/rooch-sdk-kit'; export interface Project { id: string diff --git a/infra/rooch-portal-v2/src/sections/invitations/components/lottery-list.tsx b/infra/rooch-portal-v2/src/sections/invitations/components/lottery-list.tsx index 92e530fcac..774fbb9786 100644 --- a/infra/rooch-portal-v2/src/sections/invitations/components/lottery-list.tsx +++ b/infra/rooch-portal-v2/src/sections/invitations/components/lottery-list.tsx @@ -17,8 +17,8 @@ import { import { Scrollbar } from '../../../components/scrollbar'; import { getUTCOffset } from '../../../utils/format-time'; import { formatCoin } from '../../../utils/format-number'; -import { useNetworkVariable } from '../../../hooks/use-networks'; import { GAS_COIN_DECIMALS } from '../../../config/constant'; +import { useNetworkVariable } from '../../../hooks/use-networks'; import TableSkeleton from '../../../components/skeleton/table-skeleton'; import { TableNoData, TableHeadCustom } from '../../../components/table'; diff --git a/infra/rooch-portal-v2/src/sections/inviter/index.tsx b/infra/rooch-portal-v2/src/sections/inviter/index.tsx index 60f6ef206c..125d787969 100644 --- a/infra/rooch-portal-v2/src/sections/inviter/index.tsx +++ b/infra/rooch-portal-v2/src/sections/inviter/index.tsx @@ -1,8 +1,8 @@ 'use client'; import { useEffect } from 'react'; - import { isValidAddress } from '@roochnetwork/rooch-sdk'; + import { useRouter } from '../../routes/hooks'; import { INVITER_ADDRESS_KEY } from '../../utils/inviter'; diff --git a/infra/rooch-portal-v2/src/sections/trade/components/select-token-pair.tsx b/infra/rooch-portal-v2/src/sections/trade/components/select-token-pair.tsx new file mode 100644 index 0000000000..362d6c04d7 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/components/select-token-pair.tsx @@ -0,0 +1,349 @@ +import type { SelectChangeEvent } from '@mui/material'; +import type { BalanceInfoView, AnnotatedMoveStructView } from '@roochnetwork/rooch-sdk'; + +import { useDebounce } from 'react-use'; +import { Args } from '@roochnetwork/rooch-sdk'; +import { useMemo, useState, useEffect, useCallback } from 'react'; +import { + useRoochClient, + useCurrentAddress, + useRoochClientQuery, +} from '@roochnetwork/rooch-sdk-kit'; + +import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward'; +import { Box, Button, Select, MenuItem, TextField, InputLabel, FormControl } from '@mui/material'; + +import { useNetworkVariable } from 'src/hooks/use-networks'; + +import { toDust, fromDust } from 'src/utils/number'; + +import { toast } from 'src/components/snackbar'; + +import type { TradeCoinType } from './types'; + +type TokenType = { + id: string; + type: string; + name: string; +}; + +type TokenPairType = { + x2y: boolean; + x: TokenType; + y: TokenType; +}; + +interface SelectTokenPairProps { + onLoading: (status: boolean) => void; + onCallback: (x?: TradeCoinType, y?: TradeCoinType) => void; +} + +export default function SelectTokenPair({ onLoading, onCallback }: SelectTokenPairProps) { + const client = useRoochClient(); + const dex = useNetworkVariable('dex'); + const currentAddress = useCurrentAddress(); + + const [loading, setLoading] = useState(false); + const [x, setX] = useState(); + const [xValue, setXValue] = useState(''); + const [xCount, setXCount] = useState(''); + const [xRatio, setXRation] = useState(0); + const [y, setY] = useState(); + const [x2y, setX2y] = useState(true); + const [yValue, setYValue] = useState(''); + const [yCount, setYCount] = useState(''); + // map + const [tokenPair, setTokenPair] = useState>(); + + const { data } = useRoochClientQuery( + 'getBalances', + { + owner: currentAddress?.toStr() || '', + }, + { + refetchInterval: 5000, + } + ); + + // map + const assetsMap = useMemo(() => { + const assetsMap = new Map(); + data?.data.forEach((i) => { + assetsMap.set(i.coin_type, { + ...i, + }); + }); + return assetsMap; + }, [data]); + + useEffect(() => { + client + .queryObjectStates({ + filter: { + object_type: `${dex.address}::swap::TokenPair`, + }, + }) + .then((result) => { + const pair: TokenPairType[] = result.data.map((item) => { + const xView = item.decoded_value!.value.balance_x as AnnotatedMoveStructView; + let xType = xView.type.replace('0x2::object::Object<0x3::coin_store::CoinStore<', ''); + xType = xType.replace('>>', ''); + const xName = xType.split('::'); + const yView = item.decoded_value!.value.balance_y as AnnotatedMoveStructView; + let yType = yView.type.replace('0x2::object::Object<0x3::coin_store::CoinStore<', ''); + yType = yType.replace('>>', ''); + const yName = yType.split('::'); + return { + x2y: true, + x: { + id: xView.value.id as string, + type: xType, + name: xName[xName.length - 1].replace('>>', ''), + }, + y: { + id: yView.value.id as string, + type: yType, + name: yName[yName.length - 1].replace('>>', ''), + }, + }; + }); + + const pairMap = new Map(); + pair.forEach((p) => { + const key = p.x.name; + if (!pairMap.has(key)) { + pairMap.set(key, []); + } + pairMap.get(key)!.push(p); + + const key1 = p.y.name; + if (!pairMap.has(key1)) { + pairMap.set(key1, []); + } + pairMap.get(key1)!.push({ + x2y: false, + x: p.y, + y: p.x, + }); + }); + + // Update the state + setTokenPair(pairMap); + }); + }, [client, dex]); + + const fetchY = useCallback(async () => { + if (xCount === '' || xCount === '0' || !x || !y) { + return; + } + + try { + setLoading(true); + const fixdXCount = toDust(xCount, assetsMap?.get(x.type)?.decimals || 0); + const result = await client.executeViewFunction({ + target: `${dex.address}::router::get_amount_out`, + args: [Args.u64(fixdXCount)], + typeArgs: [x.type, y.type], + }); + + if (result.vm_status !== 'Executed') { + toast.error('unknow error'); + } + + const yCount = result.return_values![0].decoded_value as string; + const fixdYCount = fromDust(yCount, assetsMap?.get(y.type)?.decimals || 0); + setYCount(fixdYCount.toString()); + + const xCoin = assetsMap?.get(x.type)!; + const yCoin = assetsMap?.get(y.type)!; + onCallback( + { + balance: xCoin.fixedBalance, + type: xCoin.coin_type, + icon: xCoin.icon_url || undefined, + symbol: xCoin.symbol, + amount: xCount, + decimal: xCoin.decimals, + }, + { + balance: yCoin.fixedBalance, + type: yCoin.coin_type, + icon: yCoin.icon_url || undefined, + symbol: yCoin.symbol, + amount: yCount, + decimal: yCoin.decimals, + } + ); + } catch (e) { + console.log(e); + } finally { + setLoading(false); + } + }, [x, y, xCount, client, assetsMap, dex.address, onCallback]); + + useEffect(() => { + const interval = setInterval(() => { + fetchY(); + }, 2000); + return () => clearInterval(interval); + }, [fetchY]); + + const exchange = () => { + const oldX = x; + const oldXValue = xValue; + const oldY = y; + const oldYValue = yValue; + + if (!oldX || !oldY) { + return; + } + setX2y(!x2y); + setX(oldY); + setXValue(oldYValue); + setY(oldX); + setYValue(oldXValue); + + const xbalance = assetsMap?.get(oldY.type)!.fixedBalance || 0; + if (xRatio !== 0) { + setXCount((xbalance * xRatio).toString()); + } else if (Number(xCount) > xbalance) { + setXCount(xbalance.toString()); + setXRation(0); + } + }; + + useDebounce(fetchY, 500, [xCount, y]); + + return ( + <> + + + X + + + + + ) => { + const { value } = e.target; + // Use a regular expression to allow only whole numbers + if (/^\d*\.?\d*$/.test(value) === false) { + return; + } + const xBalance = assetsMap?.get(x!.type)!.fixedBalance || 0; + if (xRatio !== 0) { + if (value !== (xBalance * xRatio).toString()) { + setXRation(0); + } + } + + if (Number(value) > xBalance) { + setXCount(xBalance.toString()); + } else { + setXCount(value); + } + }} + /> + + + + + + + + {[0.25, 0.5, 0.75, 1].map((item, index) => ( + + ))} + + + + + Y + + + + + ) => { + setYCount(e.target.value); + }} + /> + + + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/components/token-pair.tsx b/infra/rooch-portal-v2/src/sections/trade/components/token-pair.tsx new file mode 100644 index 0000000000..519c095ed3 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/components/token-pair.tsx @@ -0,0 +1,87 @@ +import type { SelectChangeEvent} from '@mui/material'; + +import { useState } from 'react'; +import { useDebounce } from 'react-use'; + +import { Box, Button, Select , MenuItem , TextField , InputLabel, FormControl } from '@mui/material'; + +import type { TradeCoinType } from './types'; + +interface TokenPairProps { + coins: TradeCoinType[]; + onChange: (coin: TradeCoinType, amount: number) => void; +} + +export default function TokenPair({ coins, onChange }: TokenPairProps) { + const [x, setX] = useState(''); + const [xValue, setXValue] = useState(''); + + useDebounce( + () => { + const coin = coins.find((item) => item.symbol === x); + onChange(coin!, Number(xValue)); + }, + 500, + [] + ); + + return ( + <> + Choose the tokens you want to provide liquidity for. + + + X + + + + + ) => { + setXValue(e.target.value); + }} + /> + + + + + {[0.25, 0.5, 0.75, 1].map((item, index) => ( + + ))} + + + + The amount earned providing liquidity. All pools have fixed 0.3% fees. + + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/components/types.ts b/infra/rooch-portal-v2/src/sections/trade/components/types.ts new file mode 100644 index 0000000000..6fac8596ed --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/components/types.ts @@ -0,0 +1,8 @@ +export type TradeCoinType = { + symbol: string; + type: string; + balance: number; + amount: string; + icon?: string; + decimal: number; +}; diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/components/icon.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/components/icon.tsx new file mode 100644 index 0000000000..3582f520ec --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/components/icon.tsx @@ -0,0 +1,18 @@ +import DOMPurify from 'dompurify'; + +import { Box } from '@mui/material'; + +import { Iconify } from 'src/components/iconify'; + +export default function Icon({ url }: { url?: string }) { + return url ? ( + + ) : ( + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/list/add-liquidity-modal.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/add-liquidity-modal.tsx new file mode 100644 index 0000000000..8bb1ec811f --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/add-liquidity-modal.tsx @@ -0,0 +1,511 @@ +import type { BalanceInfoView, AnnotatedMoveStructView } from '@roochnetwork/rooch-sdk'; + +import { toast } from 'sonner'; +import BigNumber from 'bignumber.js'; +import { useDebounce } from 'react-use'; +import { Args, Transaction } from '@roochnetwork/rooch-sdk'; +import { useMemo, useState, useEffect, useCallback } from 'react'; +import { + SessionKeyGuard, + useCurrentAddress, + useRoochClientQuery, + useSignAndExecuteTransaction, +} from '@roochnetwork/rooch-sdk-kit'; + +import { LoadingButton } from '@mui/lab'; +import { + Box, + Stack, + Button, + Dialog, + Divider, + TextField, + Typography, + DialogTitle, + FormControl, + DialogActions, + DialogContent, + InputAdornment, +} from '@mui/material'; + +import { useRouter } from 'src/routes/hooks'; + +import { useNetworkVariable } from 'src/hooks/use-networks'; + +import { formatCoin } from 'src/utils/format-number'; +import { toDust, bigNumberToBigInt } from 'src/utils/number'; + +import Icon from '../components/icon'; + +import type { AllLiquidityItemType } from './all-liquidity-row-item'; + +const STEPS = ['Deposit amounts', 'You will receive']; + +export default function AddLiquidityModal({ + open, + onClose, + row, +}: { + open: boolean; + onClose: () => void; + row: AllLiquidityItemType; +}) { + const dex = useNetworkVariable('dex'); + const currentAddress = useCurrentAddress(); + const [xAmount, setXAmount] = useState(''); + const [yAmount, setYAmount] = useState(''); + const { mutateAsync, isPending } = useSignAndExecuteTransaction(); + const [slippage, setSlippage] = useState(0.005); + const [customSlippage, setCustomSlippage] = useState(''); + const [activeStep, setActiveStep] = useState(0); + const [yLabelError, setYLabelError] = useState(); + const router = useRouter(); + + const { data } = useRoochClientQuery( + 'getBalances', + { + owner: currentAddress?.toStr() || '', + }, + { + refetchInterval: 5000, + } + ); + + const { data: lpTotalSupply } = useRoochClientQuery('queryObjectStates', { + filter: { + object_id: row.lpTokenId, + }, + }); + + const { data: reserveX } = useRoochClientQuery('queryObjectStates', { + filter: { + object_id: row.x.id, + }, + }); + + const { data: reserveY } = useRoochClientQuery('queryObjectStates', { + filter: { + object_id: row.y.id, + }, + }); + + // map + const assetsMap = useMemo(() => { + const assetsMap = new Map(); + data?.data.forEach((i) => { + assetsMap.set(i.coin_type, { + ...i, + }); + }); + return assetsMap; + }, [data]); + + const receive = useMemo(() => { + if ( + activeStep === 0 || + !lpTotalSupply || + !reserveX || + !reserveY || + xAmount === '' || + yAmount === '' || + !assetsMap + ) { + return { + liquidity: '-', + share: '-', + }; + } + + const lpView = lpTotalSupply.data[0].decoded_value!.value; + const totalSupply = lpView.supply as string; + const lpDecimals = lpView.decimals as number; + const xBalance = (reserveX.data[0].decoded_value!.value.balance as AnnotatedMoveStructView) + .value.value as string; + const yBalance = (reserveY.data[0].decoded_value!.value.balance as AnnotatedMoveStructView) + .value.value as string; + + const fixdXAmout = toDust(xAmount, assetsMap.get(row.x.type)?.decimals || 0); + const fixdYAmout = toDust(yAmount, assetsMap.get(row.y.type)?.decimals || 0); + + const liquidity = Math.min( + BigNumber(fixdXAmout.toString()).multipliedBy(totalSupply).div(xBalance).toNumber(), + BigNumber(fixdYAmout.toString()).multipliedBy(totalSupply).div(yBalance).toNumber() + ); + const share = BigNumber(liquidity.toString()).div(totalSupply).toFixed(4, 1); + return { liquidity: formatCoin(liquidity, lpDecimals, 2), share }; + }, [ + lpTotalSupply, + reserveX, + reserveY, + xAmount, + yAmount, + assetsMap, + row.x.type, + row.y.type, + activeStep, + ]); + + const handleNext = () => { + setActiveStep(activeStep + 1); + }; + + const handleBack = () => { + setActiveStep(activeStep - 1); + }; + + useEffect(() => { + if (!currentAddress) { + onClose(); + } + }, [currentAddress, onClose]); + + const handleAddLiquidity = () => { + const fixdX = toDust(xAmount, assetsMap.get(row.x.type)?.decimals || 0); + const fixdY = toDust(yAmount, assetsMap.get(row.y.type)?.decimals || 0); + const finalSlippage = + slippage === 0 + ? customSlippage === '' || customSlippage === '0' + ? 0 + : Number(customSlippage) + : slippage; + const minX = finalSlippage + ? bigNumberToBigInt( + BigNumber(fixdX.toString()).minus( + BigNumber(fixdX.toString()).multipliedBy(BigNumber(slippage)) + ) + ) + : fixdX; + const minY = finalSlippage + ? bigNumberToBigInt( + BigNumber(fixdY.toString()).minus( + BigNumber(fixdY.toString()).multipliedBy(BigNumber(slippage)) + ) + ) + : fixdY; + const tx = new Transaction(); + tx.callFunction({ + target: `${dex.address}::router::add_liquidity`, + args: [Args.u64(fixdX), Args.u64(fixdY), Args.u64(minX), Args.u64(minY)], + typeArgs: [row.x.type, row.y.type], + }); + mutateAsync({ + transaction: tx, + }) + .then((result) => { + if (result.execution_info.status.type === 'executed') { + toast.success('add success'); + } else { + toast.error('add failed'); + } + }) + .catch((e: any) => { + console.log(e); + toast.error('add failed'); + }) + .finally(() => { + onClose(); + }); + }; + + const fetchY = useCallback(() => { + if (xAmount === '' || xAmount === '0' || !reserveX || !reserveY || !assetsMap) { + return; + } + + const xBalance = (reserveX.data[0].decoded_value!.value.balance as AnnotatedMoveStructView) + .value.value as string; + const yBalance = (reserveY.data[0].decoded_value!.value.balance as AnnotatedMoveStructView) + .value.value as string; + const fixdX = toDust(xAmount, assetsMap.get(row.x.type)?.decimals || 0); + const xRate = BigNumber(fixdX.toString()).div(xBalance); + const y = BigNumber(yBalance).multipliedBy(xRate); + + if (y.toNumber() > Number(assetsMap.get(row.y.type)?.balance || 0)) { + setYLabelError(`Insufficient`); + } + setYAmount(y.toFixed(0, 1)); + }, [xAmount, reserveX, reserveY, row.x.type, row.y.type, assetsMap]); + + useDebounce(fetchY, 500, [fetchY]); + + const getStepContent = (step: number) => { + switch (step) { + case 0: + return ( + <> + + + {row.x.symbol} + + + + Balance: {assetsMap.get(row.x.type)?.fixedBalance} + + + + + + { + const value = e.target.value; + if (/^\d*\.?\d*$/.test(value) === false) { + return; + } + const xBalance = assetsMap?.get(row.x.type)!.fixedBalance || 0; + if (Number(value) > xBalance) { + setXAmount(xBalance.toString()); + } else { + setXAmount(value); + } + }} + InputProps={{ + endAdornment: ( + + + + + + + ), + }} + /> + + + + {row.y.symbol} + + + + Balance: {assetsMap.get(row.y.type)?.fixedBalance} + + + + + { + setYAmount(e.target.value); + }} + InputProps={{ + endAdornment: yLabelError && ( + + + + + + ), + }} + /> + + + + + Slippage + {[0.005, 0.01, 0.03].map((item, index) => ( + + ))} + + ) => { + setSlippage(0); + setCustomSlippage(e.target.value); + }} + /> + + % + + + ); + case 1: + return ( + <> + + + + + + {`${row.x.symbol}-${row.y.symbol} LP : `} + + + + {receive.liquidity} + + + + Your share in the pair : + {receive.share}% + + + + Info + + + + + + {row.x.symbol}: + + - {xAmount} + + + + + {row.y.symbol}: + + - {yAmount} + + + {/* + Rates : + 1 Grow = 0.001 RGas + */} + + Slippage : + {slippage * 100}% + + + + ); + default: + return <>; + } + }; + + return ( + + {STEPS[activeStep]} + + + {getStepContent(activeStep)} + + + + + + {activeStep === STEPS.length - 1 ? ( + + + Confirm + + + ) : ( + + Next + + )} + + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/list/all-liquidity-row-item.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/all-liquidity-row-item.tsx new file mode 100644 index 0000000000..d5ef2fd864 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/all-liquidity-row-item.tsx @@ -0,0 +1,62 @@ +import dayjs from 'dayjs'; +import { WalletGuard } from '@roochnetwork/rooch-sdk-kit'; + +import { Box, Button, TableRow, TableCell, ListItemText } from '@mui/material'; + +export type AllLiquidityItemType = { + id: string; + createAt: number; + x: { + id: string; + symbol: string; + type: string; + }; + y: { + id: string; + symbol: string; + type: string; + }; + lpTokenId: string; + creator: string; +}; + +type RowItemProps = { + row: AllLiquidityItemType; + balance?: { + x: string; + y: string; + }; + onOpenViewModal: (row: AllLiquidityItemType) => void; +}; + +export default function AllLiquidityRowItem({ row, balance, onOpenViewModal }: RowItemProps) { + return ( + + + + + + + + + + + + + + + + + { + onOpenViewModal(row); + }} + > + + + + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/list/all_liquidity_list.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/all_liquidity_list.tsx new file mode 100644 index 0000000000..95a19abd52 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/all_liquidity_list.tsx @@ -0,0 +1,242 @@ +import type { IndexerStateIDView, AnnotatedMoveStructView } from '@roochnetwork/rooch-sdk'; + +import { useRef, useMemo, useState, useEffect } from 'react'; +import { + useRoochClient, + useCurrentAddress, + useRoochClientQuery, +} from '@roochnetwork/rooch-sdk-kit'; + +import { Card, Table, Stack, TableBody, Pagination } from '@mui/material'; + +import { useNetworkVariable } from 'src/hooks/use-networks'; + +import { formatCoin } from 'src/utils/format-number'; + +import { Scrollbar } from 'src/components/scrollbar'; +import TableSkeleton from 'src/components/skeleton/table-skeleton'; +import { TableNoData, TableHeadCustom } from 'src/components/table'; + +import AllLiquidityRowItem from './add-liquidity-modal'; +import LiquidityRowItem from './all-liquidity-row-item'; + +import type { AllLiquidityItemType } from './all-liquidity-row-item'; + +const headerLabel = [ + { id: 'create_at', label: 'Create At' }, + { id: 'x', label: 'X' }, + { id: 'y', label: 'Y' }, + { id: 'action', label: 'Action', align: 'right' }, +]; + +export default function AllLiquidityList() { + const dex = useNetworkVariable('dex'); + const client = useRoochClient(); + const currentAddress = useCurrentAddress(); + const [paginationModel, setPaginationModel] = useState({ index: 1, limit: 10 }); + const mapPageToNextCursor = useRef<{ [page: number]: IndexerStateIDView | null }>({}); + const [tokenPairInfos, setTokenPairInfos] = useState>( + new Map() + ); + + const queryOptions = useMemo( + () => ({ + cursor: mapPageToNextCursor.current[paginationModel.index - 1]?.toString(), + limit: paginationModel.limit.toString(), + }), + [paginationModel] + ); + + const { data: tokenPairs, isPending } = useRoochClientQuery('queryObjectStates', { + filter: { + object_type: `${dex.address}::swap::TokenPair`, + }, + queryOption: { + showDisplay: true, + }, + }); + + const resolvedTokenPairs = useMemo(() => { + if (!tokenPairs) { + return []; + } + + const rowItme: AllLiquidityItemType[] = tokenPairs!.data.map((item) => { + const xView = item.decoded_value!.value.balance_x as AnnotatedMoveStructView; + let xType = xView.type.replace('0x2::object::Object<0x3::coin_store::CoinStore<', ''); + xType = xType.replace('>>', ''); + const xName = xType.split('::'); + const yView = item.decoded_value!.value.balance_y as AnnotatedMoveStructView; + let yType = yView.type.replace('0x2::object::Object<0x3::coin_store::CoinStore<', ''); + yType = yType.replace('>>', ''); + const yName = yType.split('::'); + const lpView = item.decoded_value!.value.coin_info as AnnotatedMoveStructView; + return { + id: item.id, + creator: item.decoded_value!.value.creator as string, + createAt: Number(item.created_at), + lpTokenId: lpView.value.id as string, + x: { + id: xView.value.id as string, + symbol: xName[xName.length - 1].replace('>>', ''), + type: xType, + }, + y: { + id: yView.value.id as string, + symbol: yName[xName.length - 1].replace('>>', ''), + type: yType, + }, + }; + }); + + return rowItme; + }, [tokenPairs]); + + useEffect(() => { + if (!client || !currentAddress) { + return; + } + const fetch = async () => { + const infos = new Map(); + const promises = resolvedTokenPairs.map(async (item) => { + const [xResult, xCoinResult, yResult, yCoinResult] = await Promise.all([ + client.queryObjectStates({ + filter: { + object_id: item.x.id, + }, + }), + client.getBalance({ + owner: currentAddress!.toStr(), + coinType: item.x.type, + }), + client.queryObjectStates({ + filter: { + object_id: item.y.id, + }, + }), + client.getBalance({ + owner: currentAddress!.toStr(), + coinType: item.y.type, + }), + ]); + + const xBalance = (xResult.data[0].decoded_value!.value.balance as AnnotatedMoveStructView) + .value.value as string; + const yBalance = (yResult.data[0].decoded_value!.value.balance as AnnotatedMoveStructView) + .value.value as string; + + infos.set(item.id, { + x: formatCoin(Number(xBalance), xCoinResult.decimals, 2), + y: formatCoin(Number(yBalance), yCoinResult.decimals, 2), + }); + }); + + await Promise.all(promises); + + setTokenPairInfos(infos); + }; + + fetch(); + }, [resolvedTokenPairs, currentAddress, client]); + + const paginate = (index: number): void => { + if (index < 0) { + return; + } + setPaginationModel({ + ...paginationModel, + index, + }); + }; + + useEffect(() => { + if (!tokenPairs) { + return; + } + if (tokenPairs.has_next_page) { + mapPageToNextCursor.current[paginationModel.index] = tokenPairs.next_cursor ?? null; + } + }, [paginationModel, tokenPairs]); + + // const { + // data: assetsList, + // isPending, + // refetch: refetchAssetsList, + // } = useRoochClientQuery( + // 'getBalances', + // { + // owner: currentAddress?.toStr() || '', + // }, + // { refetchInterval: 5000 } + // ); + + const [openAddLiquidityModal, setOpenAddLiquidityModal] = useState(false); + const [selectedRow, setSelectedRow] = useState(); + + const handleRemoveModal = (row: AllLiquidityItemType) => { + setSelectedRow(row); + setOpenAddLiquidityModal(true); + }; + + const handleCloseRemoveModal = () => { + setOpenAddLiquidityModal(false); + setSelectedRow(undefined); + }; + + return ( + + {/* + + + + + + */} + + + + + + {isPending ? ( + + ) : ( + <> + {resolvedTokenPairs.map((row) => ( + + ))} + + + )} + +
+
+ + , value: number) => { + paginate(value); + }} + /> + + {selectedRow && ( + + )} +
+ ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/list/owner-liquidity-row-item.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/owner-liquidity-row-item.tsx new file mode 100644 index 0000000000..bb7240cb21 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/owner-liquidity-row-item.tsx @@ -0,0 +1,74 @@ +import type { BalanceInfoView } from '@roochnetwork/rooch-sdk'; + +import { WalletGuard } from '@roochnetwork/rooch-sdk-kit'; + +import { Box, Button, TableRow, TableCell, ListItemText } from '@mui/material'; + +export type OwnerLiquidityItemType = { + x: { + name: string; + type: string; + }; + y: { + name: string; + type: string; + }; +} & BalanceInfoView; + +type RowItemProps = { + row: OwnerLiquidityItemType; + onOpenViewModal: (row: OwnerLiquidityItemType) => void; +}; + +export default function OwnerLiquidityRowItem({ row, onOpenViewModal }: RowItemProps) { + // const coin = useMemo(() => { + // const t = row.coin_type.split(','); + // const x = t[0]; + // const y = t[1]; + // const xName = x.split('::'); + // const yName = y.split('::'); + // return { + // x: { + // type: x, + // name: xName[xName.length - 1].replaceAll('>', ''), + // }, + // y: { + // type: y, + // name: yName[yName.length - 1].replaceAll('>', ''), + // }, + // }; + // }, [row]); + return ( + + + + + + + + + + + + + + + + + + + + + { + onOpenViewModal(row); + }} + > + + + + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/list/owner_liquidity_list.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/owner_liquidity_list.tsx new file mode 100644 index 0000000000..31e40368c1 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/owner_liquidity_list.tsx @@ -0,0 +1,138 @@ +import { useMemo, useState } from 'react'; +import { useCurrentAddress, useRoochClientQuery } from '@roochnetwork/rooch-sdk-kit'; + +import { Card, Table, TableBody } from '@mui/material'; + +import { useNetworkVariable } from 'src/hooks/use-networks'; + +import { Scrollbar } from 'src/components/scrollbar'; +import WalletGuard from 'src/components/guard/WalletGuard'; +import TableSkeleton from 'src/components/skeleton/table-skeleton'; +import { TableNoData, TableHeadCustom } from 'src/components/table'; + +import RemoveLiquidityModal from './remove-liquidity-modal'; +import OwnerLiquidityRowItem from './owner-liquidity-row-item'; + +import type { OwnerLiquidityItemType } from './owner-liquidity-row-item'; + +const headerLabel = [ + { id: 'lp', label: 'LP' }, + { id: 'x', label: 'X' }, + { id: 'y', label: 'Y' }, + { id: 'balance', label: 'Balance' }, + { id: 'supply', label: 'Supply' }, + { id: 'action', label: 'Action', align: 'right' }, +]; + +export default function OwnerLiquidityList() { + const currentAddress = useCurrentAddress(); + const dex = useNetworkVariable('dex'); + + const { + data: assetsList, + isPending, + refetch: refetchAssetsList, + } = useRoochClientQuery( + 'getBalances', + { + owner: currentAddress?.toStr() || '', + }, + { refetchInterval: 5000 } + ); + + const lpTokens = useMemo(() => { + if (!assetsList) { + return []; + } + const tokens: OwnerLiquidityItemType[] = assetsList!.data + .filter((item) => item.symbol.startsWith('RDexLP')) + .map((item) => { + const t = item.coin_type + .replaceAll(' ', '') + .replace(`${dex.address}::swap::LPToken<`, '') + .split(','); + const x = t[0]; + const y = t[1].substring(0, t[1].length - 1); + const xName = x.split('::'); + const yName = y.split('::'); + return { + ...item, + x: { + type: x, + name: xName[xName.length - 1], + }, + y: { + type: y, + name: yName[yName.length - 1], + }, + }; + }) + .sort((a, b) => b.fixedBalance - a.fixedBalance); + return tokens; + }, [assetsList, dex.address]); + + console.log('my token', lpTokens); + + const [openRemoveLiquidityModal, setOpenRemoveLiquidityModal] = useState(false); + const [selectedRow, setSelectedRow] = useState(); + + const handleRemoveModal = (row: OwnerLiquidityItemType) => { + setSelectedRow(row); + setOpenRemoveLiquidityModal(true); + }; + + const handleCloseRemoveModal = () => { + setOpenRemoveLiquidityModal(false); + setSelectedRow(undefined); + }; + + return ( + + + {/* + + + + + + */} + + + + + + {isPending ? ( + + ) : ( + <> + {lpTokens?.map((row) => ( + + ))} + + + )} + +
+
+ + {selectedRow && ( + + )} +
+
+ ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/list/remove-liquidity-modal.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/remove-liquidity-modal.tsx new file mode 100644 index 0000000000..af92530aab --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/remove-liquidity-modal.tsx @@ -0,0 +1,206 @@ + +import { useState } from 'react'; +import BigNumber from 'bignumber.js'; +import { + Args, + Transaction, +} from '@roochnetwork/rooch-sdk'; +import { + SessionKeyGuard, + useSignAndExecuteTransaction, +} from '@roochnetwork/rooch-sdk-kit'; + +import { LoadingButton } from '@mui/lab'; +import { Box , + Stack, + Button, + Dialog, + TextField, + Typography, + DialogTitle, + FormControl, + DialogActions, + DialogContent, + InputAdornment, +} from '@mui/material'; + +import { useNetworkVariable } from 'src/hooks/use-networks'; + +import { toDust } from 'src/utils/number'; +import { formatCoin } from 'src/utils/format-number'; + +import { toast } from 'src/components/snackbar'; + +import type { OwnerLiquidityItemType } from './owner-liquidity-row-item'; + +// TODO: 计算收入 +export default function RemoveLiquidityModal({ + open, + onClose, + row, +}: { + open: boolean; + onClose: () => void; + row: OwnerLiquidityItemType; +}) { + const dex = useNetworkVariable('dex'); + + const { mutateAsync, isPending } = useSignAndExecuteTransaction(); + + const [liquidity, setLiquidity] = useState(''); + const [slippage, setSlippage] = useState(0.005); + const [customSlippage, setCustomSlippage] = useState(''); + + const handleRemoveLiquidity = () => { + const fixdLiquidity = toDust(liquidity, row.decimals); + const tx = new Transaction(); + tx.callFunction({ + target: `${dex.address}::router::remove_liquidity`, + args: [Args.u64(fixdLiquidity), Args.u64(BigInt(0)), Args.u64(BigInt(0))], + typeArgs: [row.x.type, row.y.type], + }); + mutateAsync({ + transaction: tx, + }) + .then((result) => { + if (result.execution_info.status.type === 'executed') { + toast.success('remove success'); + } else { + console.log(result); + toast.error('remove failed'); + } + }) + .catch((e: any) => { + console.log(e); + toast.error('remove failed'); + }) + .finally(() => { + onClose(); + }); + }; + + return ( + + Remove Liquidity + + + + + {row.symbol} + {row.name} + + + + Balance: {row.fixedBalance}{' '} + + + + + + { + setLiquidity(e.target.value); + }} + InputProps={{ + endAdornment: ( + + + + + + + ), + }} + /> + + + + Slippage + {[0.005, 0.01, 0.03].map((item, index) => ( + + ))} + + + + + + + + + Confirm + + + + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/list/view.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/view.tsx new file mode 100644 index 0000000000..31552631cc --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/list/view.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { Tab, Tabs, Stack, Typography } from '@mui/material'; + +import { useTabs } from 'src/hooks/use-tabs'; + +import { DashboardContent } from 'src/layouts/dashboard'; + +import AllLiquidityList from './all_liquidity_list'; +import OwnerLiquidityList from './owner_liquidity_list'; + +const TABS = [ + { label: 'All Liquidity', value: 'all_liquidity' }, + { label: 'Your Liquidity', value: 'you_liquidity' }, + // { label: 'Farm', value: 'farm' }, +]; + +export default function LiquidityListView() { + const tabs = useTabs('all_liquidity'); + + const renderTabs = ( + + {TABS.map((tab) => ( + + ))} + + ); + + return ( + + + Pool + + {renderTabs} + + {tabs.value === 'all_liquidity' && } + {tabs.value === 'you_liquidity' && } + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/liquidity/view.tsx b/infra/rooch-portal-v2/src/sections/trade/liquidity/view.tsx new file mode 100644 index 0000000000..9fa406cc69 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/liquidity/view.tsx @@ -0,0 +1,9 @@ +import LiquidityListView from './list/view'; + +export const metadata = { + title: 'Pool', +}; + +export default function LiquidityPage() { + return ; +} diff --git a/infra/rooch-portal-v2/src/sections/trade/home-view.tsx b/infra/rooch-portal-v2/src/sections/trade/market/home-view.tsx similarity index 91% rename from infra/rooch-portal-v2/src/sections/trade/home-view.tsx rename to infra/rooch-portal-v2/src/sections/trade/market/home-view.tsx index 5c158c4fba..72fe3ff99c 100644 --- a/infra/rooch-portal-v2/src/sections/trade/home-view.tsx +++ b/infra/rooch-portal-v2/src/sections/trade/market/home-view.tsx @@ -31,13 +31,13 @@ export default function MarketplaceHomeView() { { - router.push(`/trade/grow`); + router.push(`/trade/market/grow`); }} /> { - router.push(`/trade/gold`); + router.push(`/trade/market/gold`); }} />
diff --git a/infra/rooch-portal-v2/src/sections/trade/view.tsx b/infra/rooch-portal-v2/src/sections/trade/market/view.tsx similarity index 99% rename from infra/rooch-portal-v2/src/sections/trade/view.tsx rename to infra/rooch-portal-v2/src/sections/trade/market/view.tsx index c78cb612e1..d95d71fe76 100644 --- a/infra/rooch-portal-v2/src/sections/trade/view.tsx +++ b/infra/rooch-portal-v2/src/sections/trade/market/view.tsx @@ -35,8 +35,8 @@ import { renderSkeleton } from 'src/components/skeleton/product-item-skeleton-li import { ProductItemSkeleton } from 'src/components/skeleton/product-item-skeleton'; import InscriptionItemBidCard from 'src/components/market/inscription-item-bid-card'; -import { formatUnitPrice } from '../../utils/marketplace'; -import { useNetworkVariable } from '../../hooks/use-networks'; +import { formatUnitPrice } from '../../../utils/marketplace'; +import { useNetworkVariable } from '../../../hooks/use-networks'; export default function MarketplaceView({ params }: { params: { tick: string } }) { const { tick: marketplaceTick }: { tick: string } = params; diff --git a/infra/rooch-portal-v2/src/sections/trade/swap/confirm-modal.tsx b/infra/rooch-portal-v2/src/sections/trade/swap/confirm-modal.tsx new file mode 100644 index 0000000000..518c3be056 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/swap/confirm-modal.tsx @@ -0,0 +1,165 @@ +import DOMPurify from 'dompurify'; +import BigNumber from 'bignumber.js'; +import { Args, Transaction } from '@roochnetwork/rooch-sdk'; +import { SessionKeyGuard, useSignAndExecuteTransaction } from '@roochnetwork/rooch-sdk-kit'; + +import { LoadingButton } from '@mui/lab'; +import { + Box, + Card, + Stack, + Button, + Dialog, + CardHeader, + DialogTitle, + CardContent, + DialogActions, + DialogContent, +} from '@mui/material'; + +import { useNetworkVariable } from 'src/hooks/use-networks'; + +import { toDust, bigNumberToBigInt } from 'src/utils/number'; + +import { toast } from 'src/components/snackbar'; + +import type { TradeCoinType } from '../components/types'; + +export default function SwapConfirmModal({ + open, + onClose, + x, + y, + slippage, +}: { + open: boolean; + onClose: () => void; + x: TradeCoinType; + y: TradeCoinType; + slippage: number; +}) { + const dex = useNetworkVariable('dex'); + const { mutateAsync, isPending } = useSignAndExecuteTransaction(); + + const handleSwap = () => { + if (!x || !y) { + return; + } + const tx = new Transaction(); + const fixdX = toDust(x.amount, x.decimal); + const fixdY = toDust(y.amount, y.decimal); + + const finalY = + slippage !== 0 + ? bigNumberToBigInt( + BigNumber(fixdY.toString()).minus( + BigNumber(fixdY.toString()).multipliedBy(BigNumber(slippage)) + ) + ) + : fixdX; + tx.callFunction({ + target: `${dex.address}::router::swap_with_exact_input`, + args: [Args.u64(fixdX), Args.u64(finalY)], + typeArgs: [x!.type, y!.type], + }); + + mutateAsync({ transaction: tx }) + .then((result) => { + if (result.execution_info.status.type === 'executed') { + toast.success('swap success'); + } else { + toast.error('swap failed'); + } + }) + .catch((e: any) => { + console.log(e); + toast.error('swap failed'); + }) + .finally(() => { + onClose(); + }); + }; + + return ( + + Swap Confirm + + + + + + + + + + + {' '} + - {x.amount} + {` ${x.symbol} `} + + + + + + {' '} + + {y.amount} + {` ${y.symbol} `} + + + + + Price : + + 1 {x.symbol} ≈ {`${BigNumber(y.amount).div(x.amount).toFixed(0)}`} {y.symbol} + + + + Slippage : + {slippage * 100}% + + + + + + + + + + + + Confirm + + + + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/trade/swap/view.tsx b/infra/rooch-portal-v2/src/sections/trade/swap/view.tsx new file mode 100644 index 0000000000..1b68765370 --- /dev/null +++ b/infra/rooch-portal-v2/src/sections/trade/swap/view.tsx @@ -0,0 +1,125 @@ +'use client'; + +import { useState } from 'react'; +import BigNumber from 'bignumber.js'; +import { useSignAndExecuteTransaction } from '@roochnetwork/rooch-sdk-kit'; + +import { Box, Stack, Button, TextField, Typography, FormControl } from '@mui/material'; + +import { useNetworkVariable } from 'src/hooks/use-networks'; + +import { DashboardContent } from 'src/layouts/dashboard'; + +import SwapConfirmModal from './confirm-modal'; +import SelectTokenPair from '../components/select-token-pair'; + +import type { TradeCoinType } from '../components/types'; + +export default function SwapView() { + const dex = useNetworkVariable('dex'); + const { mutate, isPending } = useSignAndExecuteTransaction(); + + const [loading, setLoading] = useState(false); + const [x, setX] = useState(); + const [y, setY] = useState(); + const [slippage, setSlippage] = useState(0.005); + const [customSlippage, setCustomSlippage] = useState(''); + const [openSwapModal, setOpenSwapModal] = useState(false); + + return ( + <> + + + Swap + + Trade tokens in an instant. + { + setLoading(loading); + }} + onCallback={(x, y) => { + setX(x); + setY(y); + }} + /> + + Price{' '} + {x && y + ? `1 ${x?.symbol} ≈ ${BigNumber(y?.amount).div(x?.amount).toFixed(0, 1)} ${y?.symbol}` + : '-'} + + Slippage + + + {[0.005, 0.01, 0.03].map((item, index) => ( + + ))} + + ) => { + setSlippage(0); + setCustomSlippage(e.target.value); + }} + /> + + % + + + + + + {x && y && ( + setOpenSwapModal(false)} + x={x!} + y={y!} + /> + )} + + ); +} diff --git a/infra/rooch-portal-v2/src/sections/tx/view.tsx b/infra/rooch-portal-v2/src/sections/tx/view.tsx index 09818edc54..3681cbc1b6 100644 --- a/infra/rooch-portal-v2/src/sections/tx/view.tsx +++ b/infra/rooch-portal-v2/src/sections/tx/view.tsx @@ -31,8 +31,8 @@ import { useTabs } from 'src/hooks/use-tabs'; import { formatCoin } from 'src/utils/format-number'; import { varAlpha } from 'src/theme/styles'; -import { DashboardContent } from 'src/layouts/dashboard'; import { GAS_COIN_DECIMALS } from 'src/config/constant'; +import { DashboardContent } from 'src/layouts/dashboard'; import { Iconify } from 'src/components/iconify'; diff --git a/infra/rooch-portal-v2/src/utils/number.ts b/infra/rooch-portal-v2/src/utils/number.ts index 742f605fa2..d72d0e444a 100644 --- a/infra/rooch-portal-v2/src/utils/number.ts +++ b/infra/rooch-portal-v2/src/utils/number.ts @@ -78,7 +78,7 @@ function toDustBigNumber(val: BigNumber | number | string, decimal: number | big } export function bigNumberToBigInt(val: BigNumber | number | string): bigint { - const str = toBigNumber(val).toString(); + const str = toBigNumber(val).toFixed(0, 1); if (str === 'NaN') { return BigInt(0); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1398c9cc77..539d6bc99a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -196,24 +196,27 @@ importers: '@iconify/react': specifier: ^5.0.1 version: 5.0.2(react@18.3.1) + '@mui/icons-material': + specifier: 6.4.0 + version: 6.4.0(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/lab': specifier: ^5.0.0-alpha.170 - version: 5.0.0-alpha.173(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.0.0-alpha.173(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material': specifier: ^5.15.20 - version: 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/material-nextjs': specifier: ^5.15.11 - version: 5.16.6(@emotion/cache@11.13.1)(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 5.16.6(@emotion/cache@11.13.1)(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@mui/x-data-grid': specifier: ^7.7.0 - version: 7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-date-pickers': specifier: ^7.7.0 - version: 7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-tree-view': specifier: ^7.7.0 - version: 7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/colors': specifier: ^3.0.0 version: 3.0.0 @@ -1684,6 +1687,10 @@ packages: resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} engines: {node: '>=6.9.0'} + '@babel/runtime@7.26.0': + resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.25.0': resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} engines: {node: '>=6.9.0'} @@ -1769,6 +1776,9 @@ packages: '@emotion/cache@11.13.1': resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} @@ -1790,6 +1800,9 @@ packages: '@emotion/serialize@1.3.1': resolution: {integrity: sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==} + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + '@emotion/sheet@1.4.0': resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} @@ -1814,6 +1827,9 @@ packages: '@emotion/utils@1.4.0': resolution: {integrity: sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==} + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + '@emotion/weak-memoize@0.4.0': resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} @@ -2647,8 +2663,19 @@ packages: '@types/react': optional: true - '@mui/core-downloads-tracker@5.16.7': - resolution: {integrity: sha512-RtsCt4Geed2/v74sbihWzzRs+HsIQCfclHeORh5Ynu2fS4icIKozcSubwuG7vtzq2uW3fOR1zITSP84TNt2GoQ==} + '@mui/core-downloads-tracker@5.16.14': + resolution: {integrity: sha512-sbjXW+BBSvmzn61XyTMun899E7nGPTXwqD9drm1jBUAvWEhJpPFIRxwQQiATWZnd9rvdxtnhhdsDxEGWI0jxqA==} + + '@mui/icons-material@6.4.0': + resolution: {integrity: sha512-zF0Vqt8a+Zp2Oz8P+WvJflba6lLe3PhxIz1NNqn+n4A+wKLPbkeqY8ShmKjPyiCTg0RMbPrp993oUDl9xGsDlQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^6.4.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true '@mui/lab@5.0.0-alpha.173': resolution: {integrity: sha512-Gt5zopIWwxDgGy/MXcp6GueD84xFFugFai4hYiXY0zowJpTVnIrTQCQXV004Q7rejJ7aaCntX9hpPJqCrioshA==} @@ -2686,15 +2713,15 @@ packages: '@types/react': optional: true - '@mui/material@5.16.7': - resolution: {integrity: sha512-cwwVQxBhK60OIOqZOVLFt55t01zmarKJiJUWbk0+8s/Ix5IaUzAShqlJchxsIQ4mSrWqgcKCCXKtIlG5H+/Jmg==} + '@mui/material@5.16.14': + resolution: {integrity: sha512-eSXQVCMKU2xc7EcTxe/X/rC9QsV2jUe8eLM3MUCPYbo6V52eCE436akRIvELq/AqZpxx2bwkq7HC0cRhLB+yaw==} engines: {node: '>=12.0.0'} peerDependencies: '@emotion/react': ^11.5.0 '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@emotion/react': optional: true @@ -2703,6 +2730,16 @@ packages: '@types/react': optional: true + '@mui/private-theming@5.16.14': + resolution: {integrity: sha512-12t7NKzvYi819IO5IapW2BcR33wP/KAVrU8d7gLhGHoAmhDxyXlRoKiRij3TOD8+uzk0B6R9wHUNKi4baJcRNg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/private-theming@5.16.6': resolution: {integrity: sha512-rAk+Rh8Clg7Cd7shZhyt2HGTTE5wYKNSJ5sspf28Fqm/PZ69Er9o6KX25g03/FG2dfpg5GCwZh/xOojiTfm3hw==} engines: {node: '>=12.0.0'} @@ -2713,6 +2750,29 @@ packages: '@types/react': optional: true + '@mui/private-theming@6.4.1': + resolution: {integrity: sha512-DcT7mwK89owwgcEuiE7w458te4CIjHbYWW6Kn6PiR6eLtxBsoBYphA968uqsQAOBQDpbYxvkuFLwhgk4bxoN/Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@5.16.14': + resolution: {integrity: sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/styled-engine@5.16.6': resolution: {integrity: sha512-zaThmS67ZmtHSWToTiHslbI8jwrmITcN93LQaR2lKArbvS7Z3iLkwRoiikNWutx9MBs8Q6okKvbZq1RQYB3v7g==} engines: {node: '>=12.0.0'} @@ -2726,6 +2786,35 @@ packages: '@emotion/styled': optional: true + '@mui/styled-engine@6.4.0': + resolution: {integrity: sha512-ek/ZrDujrger12P6o4luQIfRd2IziH7jQod2WMbLqGE03Iy0zUwYmckRTVhRQTLPNccpD8KXGcALJF+uaUQlbg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@5.16.14': + resolution: {integrity: sha512-KBxMwCb8mSIABnKvoGbvM33XHyT+sN0BzEBG+rsSc0lLQGzs7127KWkCA6/H8h6LZ00XpBEME5MAj8mZLiQ1tw==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + '@mui/system@5.16.7': resolution: {integrity: sha512-Jncvs/r/d/itkxh7O7opOunTqbbSSzMTHzZkNLM+FjAOg+cYAZHrPDlYe1ZGKUYORwwb2XexlWnpZp0kZ4AHuA==} engines: {node: '>=12.0.0'} @@ -2742,6 +2831,22 @@ packages: '@types/react': optional: true + '@mui/system@6.4.1': + resolution: {integrity: sha512-rgQzgcsHCTtzF9MZ+sL0tOhf2ZBLazpjrujClcb4Siju5lTrK0xX4PsiropActzCemNfM+mOu+0jezAVnfRK8g==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + '@mui/types@7.2.16': resolution: {integrity: sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==} peerDependencies: @@ -2750,6 +2855,24 @@ packages: '@types/react': optional: true + '@mui/types@7.2.21': + resolution: {integrity: sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@5.16.14': + resolution: {integrity: sha512-wn1QZkRzSmeXD1IguBVvJJHV3s6rxJrfb6YuC9Kk6Noh9f8Fb54nUs5JRkKm+BOerRhj5fLg05Dhx/H3Ofb8Mg==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/utils@5.16.6': resolution: {integrity: sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==} engines: {node: '>=12.0.0'} @@ -2760,6 +2883,16 @@ packages: '@types/react': optional: true + '@mui/utils@6.4.1': + resolution: {integrity: sha512-iQUDUeYh87SvR4lVojaRaYnQix8BbRV51MxaV6MBmqthecQoxwSbS5e2wnbDJUeFxY2ppV505CiqPLtd0OWkqw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/x-data-grid@7.16.0': resolution: {integrity: sha512-71ZyffTeF8RPa399UkMlUbQ8T70kOrUK3fBXfinnal4mwgISlKwBN8EHNZZhyxSQ4vpWs3wHrHZ6MGQeXNUhJQ==} engines: {node: '>=14.0.0'} @@ -4707,6 +4840,9 @@ packages: '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + '@types/prop-types@15.7.14': + resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} + '@types/randomstring@1.3.0': resolution: {integrity: sha512-kCP61wludjY7oNUeFiMxfswHB3Wn/aC03Cu82oQsNTO6OCuhVN/rCbBs68Cq6Nkgjmp2Sh3Js6HearJPkk7KQA==} @@ -4728,6 +4864,11 @@ packages: '@types/react-transition-group@4.4.11': resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + '@types/react-transition-group@4.4.12': + resolution: {integrity: sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==} + peerDependencies: + '@types/react': '*' + '@types/react@18.3.5': resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==} @@ -9514,6 +9655,9 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-is@19.0.0: + resolution: {integrity: sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==} + react-konva@18.2.10: resolution: {integrity: sha512-ohcX1BJINL43m4ynjZ24MxFI1syjBdrXhqVxYVDw2rKgr3yuS0x/6m1Y2Z4sl4T/gKhfreBx8KHisd0XC6OT1g==} peerDependencies: @@ -12293,6 +12437,10 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 + '@babel/runtime@7.26.0': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.25.0': dependencies: '@babel/code-frame': 7.24.7 @@ -12496,6 +12644,14 @@ snapshots: '@emotion/weak-memoize': 0.4.0 stylis: 4.2.0 + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + '@emotion/hash@0.9.2': {} '@emotion/is-prop-valid@1.3.0': @@ -12528,6 +12684,14 @@ snapshots: '@emotion/utils': 1.4.0 csstype: 3.1.3 + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.1.3 + '@emotion/sheet@1.4.0': {} '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': @@ -12553,6 +12717,8 @@ snapshots: '@emotion/utils@1.4.0': {} + '@emotion/utils@1.4.2': {} + '@emotion/weak-memoize@0.4.0': {} '@esbuild/aix-ppc64@0.21.5': @@ -13265,13 +13431,21 @@ snapshots: optionalDependencies: '@types/react': 18.3.5 - '@mui/core-downloads-tracker@5.16.7': {} + '@mui/core-downloads-tracker@5.16.14': {} + + '@mui/icons-material@6.4.0(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/material': 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 - '@mui/lab@5.0.0-alpha.173(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/lab@5.0.0-alpha.173(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/base': 5.0.0-beta.40(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/material': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/system': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/types': 7.2.16(@types/react@18.3.5) '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) @@ -13284,37 +13458,46 @@ snapshots: '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@types/react': 18.3.5 - '@mui/material-nextjs@5.16.6(@emotion/cache@11.13.1)(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@mui/material-nextjs@5.16.6(@emotion/cache@11.13.1)(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(next@14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@mui/material': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/material': 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next: 14.2.11(@babel/core@7.25.2)(@playwright/test@1.47.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 optionalDependencies: '@emotion/cache': 11.13.1 '@types/react': 18.3.5 - '@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 - '@mui/core-downloads-tracker': 5.16.7 - '@mui/system': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) - '@mui/types': 7.2.16(@types/react@18.3.5) - '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) + '@babel/runtime': 7.26.0 + '@mui/core-downloads-tracker': 5.16.14 + '@mui/system': 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/types': 7.2.21(@types/react@18.3.5) + '@mui/utils': 5.16.14(@types/react@18.3.5)(react@18.3.1) '@popperjs/core': 2.11.8 - '@types/react-transition-group': 4.4.11 + '@types/react-transition-group': 4.4.12(@types/react@18.3.5) clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-is: 18.3.1 + react-is: 19.0.0 react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) optionalDependencies: '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@types/react': 18.3.5 + '@mui/private-theming@5.16.14(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/utils': 5.16.14(@types/react@18.3.5)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + '@mui/private-theming@5.16.6(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -13324,6 +13507,26 @@ snapshots: optionalDependencies: '@types/react': 18.3.5 + '@mui/private-theming@6.4.1(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/utils': 6.4.1(@types/react@18.3.5)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@mui/styled-engine@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/cache': 11.14.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/styled-engine@5.16.6(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -13335,6 +13538,35 @@ snapshots: '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/styled-engine@6.4.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + + '@mui/system@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/private-theming': 5.16.14(@types/react@18.3.5)(react@18.3.1) + '@mui/styled-engine': 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.21(@types/react@18.3.5) + '@mui/utils': 5.16.14(@types/react@18.3.5)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@types/react': 18.3.5 + '@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -13351,10 +13583,42 @@ snapshots: '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@types/react': 18.3.5 + '@mui/system@6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/private-theming': 6.4.1(@types/react@18.3.5)(react@18.3.1) + '@mui/styled-engine': 6.4.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.21(@types/react@18.3.5) + '@mui/utils': 6.4.1(@types/react@18.3.5)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@types/react': 18.3.5 + '@mui/types@7.2.16(@types/react@18.3.5)': optionalDependencies: '@types/react': 18.3.5 + '@mui/types@7.2.21(@types/react@18.3.5)': + optionalDependencies: + '@types/react': 18.3.5 + + '@mui/utils@5.16.14(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/types': 7.2.21(@types/react@18.3.5) + '@types/prop-types': 15.7.14 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.0.0 + optionalDependencies: + '@types/react': 18.3.5 + '@mui/utils@5.16.6(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 @@ -13367,11 +13631,23 @@ snapshots: optionalDependencies: '@types/react': 18.3.5 - '@mui/x-data-grid@7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/utils@6.4.1(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.0 + '@mui/types': 7.2.21(@types/react@18.3.5) + '@types/prop-types': 15.7.14 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.0.0 + optionalDependencies: + '@types/react': 18.3.5 + + '@mui/x-data-grid@7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@mui/material': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/system': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/material': 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) '@mui/x-internals': 7.16.0(@types/react@18.3.5)(react@18.3.1) clsx: 2.1.1 @@ -13385,11 +13661,11 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@mui/x-date-pickers@7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/x-date-pickers@7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@mui/material': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/system': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/material': 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) '@mui/x-internals': 7.16.0(@types/react@18.3.5)(react@18.3.1) '@types/react-transition-group': 4.4.11 @@ -13413,11 +13689,11 @@ snapshots: transitivePeerDependencies: - '@types/react' - '@mui/x-tree-view@7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@mui/x-tree-view@7.16.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@mui/material': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@mui/system': 5.16.7(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/material': 5.16.14(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.4.1(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) '@mui/x-internals': 7.16.0(@types/react@18.3.5)(react@18.3.1) '@types/react-transition-group': 4.4.11 @@ -14899,7 +15175,7 @@ snapshots: '@react-three/fiber@8.17.7(react-dom@18.3.1(react@18.3.1))(react-native@0.75.3(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.3.5)(react@18.3.1)(typescript@4.8.4))(react@18.3.1)(three@0.168.0)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@types/debounce': 1.2.4 '@types/react-reconciler': 0.26.7 '@types/webxr': 0.5.20 @@ -15603,6 +15879,8 @@ snapshots: '@types/prop-types@15.7.12': {} + '@types/prop-types@15.7.14': {} + '@types/randomstring@1.3.0': {} '@types/react-copy-to-clipboard@5.0.7': @@ -15629,6 +15907,10 @@ snapshots: dependencies: '@types/react': 18.3.5 + '@types/react-transition-group@4.4.12(@types/react@18.3.5)': + dependencies: + '@types/react': 18.3.5 + '@types/react@18.3.5': dependencies: '@types/prop-types': 15.7.12 @@ -20701,7 +20983,7 @@ snapshots: metro-runtime@0.80.12: dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 flow-enums-runtime: 0.0.6 metro-source-map@0.80.12: @@ -22027,6 +22309,8 @@ snapshots: react-is@18.3.1: {} + react-is@19.0.0: {} + react-konva@18.2.10(konva@9.3.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@types/react-reconciler': 0.28.8