diff --git a/package.json b/package.json index b78ca549..ba6012a0 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,8 @@ "build:base-sepolia-testnet": "bun run build:packages && NETWORK_NAME=base-sepolia-testnet bash scripts/build.sh", "build:base": "bun run build:packages && NETWORK_NAME=base bash scripts/build.sh", "build:qa": "bun run build:packages && NETWORK_NAME=legacy bash scripts/build.sh", - "dev": "bun run build:packages && vite", + "dev": "bunx concurrently -k -n CORE,METAPORT,PORTAL -c cyan,magenta,green 'bun run --cwd packages/core dev' 'bun run --cwd packages/metaport dev' 'bun run dev:portal'", "dev:portal": "vite", - "dev:watch": "bunx concurrently -k -n CORE,METAPORT,PORTAL -c cyan,magenta,green 'bun run --cwd packages/core dev' 'bun run --cwd packages/metaport dev' 'bun run dev:portal'", "build": "bun run build:mainnet", "lint": "eslint ./src --ext ts,tsx", "preview": "vite preview", diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 85342bf2..56c05aca 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -66,5 +66,10 @@ export const DEFAULT_DELEGATION_INFO = 'portal' export const POPULAR_TOKENS = ['SKL', 'ETH', 'USDC', 'WBTC', 'USDT'] +export const CREDIT_STATION_START_BLOCKS: Record = { + 'base-sepolia-testnet': 33559298, + base: 38111750, +} + const _DEFAULT_UPDATE_INTERVAL_SECONDS = 10 export const DEFAULT_UPDATE_INTERVAL_MS = _DEFAULT_UPDATE_INTERVAL_SECONDS * MS_MULTIPLIER diff --git a/packages/core/src/networks.ts b/packages/core/src/networks.ts index d810c1a2..d8eac6f1 100644 --- a/packages/core/src/networks.ts +++ b/packages/core/src/networks.ts @@ -73,6 +73,24 @@ export const MAINNET_BACKGROUNDS: { [key in types.SkaleNetwork]: string } = { 'base-sepolia-testnet': 'linear-gradient(#008a47, #006936)', } +export const NATIVE_TOKEN_SYMBOLS: { [key in types.SkaleNetwork]: string } = { + mainnet: 'sFUEL', + legacy: 'sFUEL', + regression: 'sFUEL', + testnet: 'sFUEL', + base: 'CREDIT', + 'base-sepolia-testnet': 'CREDIT', +} + +export const KEY_FEATURES: { [key in types.SkaleNetwork]: types.NetworkFeature } = { + mainnet: 'bridge', + legacy: 'bridge', + regression: 'bridge', + testnet: 'bridge', + base: 'credits', + 'base-sepolia-testnet': 'credits', +} + export function parse(networksEnv: string | undefined): types.SkaleNetwork[] { if (!networksEnv) throw new Error('VITE_NETWORKS environment variable is not defined'); return networksEnv.split(',').map((network) => network.trim() as types.SkaleNetwork); diff --git a/packages/metaport/src/components/ChainIcon.tsx b/packages/metaport/src/components/ChainIcon.tsx index b23c1831..6ebbc086 100644 --- a/packages/metaport/src/components/ChainIcon.tsx +++ b/packages/metaport/src/components/ChainIcon.tsx @@ -12,7 +12,7 @@ export default function ChainIcon(props: { chainsMeta?: types.ChainsMetadataMap, className?: string app?: string - size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' + size?: 'xxs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' }) { const { mode } = useThemeMode() const iconPath = chainIconPath(props.skaleNetwork, props.chainName, props.app) diff --git a/packages/metaport/src/components/Tile.tsx b/packages/metaport/src/components/Tile.tsx index d9ff7f6c..c157c0d4 100644 --- a/packages/metaport/src/components/Tile.tsx +++ b/packages/metaport/src/components/Tile.tsx @@ -43,7 +43,7 @@ export default function Tile(props: { progress?: number children?: ReactElement | ReactElement[] | false childrenRi?: ReactElement | ReactElement[] | null | '' - size?: 'lg' | 'md' + size?: 'lg' | 'md' | 'xl' textColor?: string disabled?: boolean | null ri?: boolean @@ -77,6 +77,7 @@ export default function Tile(props: { const value = (

{props.textRi ? (

{props.textRi} diff --git a/packages/metaport/src/components/TokenIcon.tsx b/packages/metaport/src/components/TokenIcon.tsx index dc5ca101..2648e2f1 100644 --- a/packages/metaport/src/components/TokenIcon.tsx +++ b/packages/metaport/src/components/TokenIcon.tsx @@ -28,10 +28,11 @@ import { styles, cls } from '../core/css' export default function TokenIcon(props: { tokenSymbol: string | undefined | null iconUrl?: string | undefined | null - size?: 'xs' | 'sm' | 'md' | 'lg' + size?: 'xs' | 'sm' | 'md' | 'lg', + className?: string }) { const size = props.size ?? 'sm' - const className = cls(styles[`chainIcon${size}`], 'rounded-full') + const className = cls(styles[`chainIcon${size}`], 'rounded-full', props.className) if (props.tokenSymbol === undefined || props.tokenSymbol === null) { return } diff --git a/packages/metaport/src/core/wagmi_network.ts b/packages/metaport/src/core/wagmi_network.ts index a83ae108..a68b98de 100644 --- a/packages/metaport/src/core/wagmi_network.ts +++ b/packages/metaport/src/core/wagmi_network.ts @@ -22,7 +22,7 @@ */ import { Chain } from 'wagmi/chains' -import { types, metadata, endpoints } from '@/core' +import { types, metadata, endpoints, networks } from '@/core' import { getExplorerUrl } from './explorer' import { getChainId } from './chain_id' @@ -40,8 +40,8 @@ export function constructWagmiChain(network: types.SkaleNetwork, chainName: stri name: name, nativeCurrency: { decimals: 18, - name: 'sFUEL', - symbol: 'sFUEL' + name: networks.NATIVE_TOKEN_SYMBOLS[network], + symbol: networks.NATIVE_TOKEN_SYMBOLS[network] }, rpcUrls: { public: { http: [endpointHttp], webSocket: [endpointWs] }, diff --git a/packages/metaport/src/styles/styles.module.scss b/packages/metaport/src/styles/styles.module.scss index bf140938..40a051db 100644 --- a/packages/metaport/src/styles/styles.module.scss +++ b/packages/metaport/src/styles/styles.module.scss @@ -235,6 +235,12 @@ button { flex-shrink: 0; } +.chainIconBgxxs { + width: 17px; + height: 17px; + border-radius: 6px; +} + .chainIconBgxs { width: 23px; height: 23px; @@ -275,6 +281,16 @@ button { display: block; } +.chainIconxxs { + width: 12px; + height: 12px; + + svg { + width: 12px; + height: 12px; + } +} + .chainIconxs { width: 17px; height: 17px; diff --git a/src/App.scss b/src/App.scss index 97f6a7cb..c3296599 100644 --- a/src/App.scss +++ b/src/App.scss @@ -21,7 +21,7 @@ body { } .skLogo { - width: 75pt; + width: 65pt; } .skMessage { @@ -29,9 +29,15 @@ body { } .sk-header { + .MuiToolbar-root { + padding-left: 0px !important; + padding-right: 0px !important; + } + background-color: rgba(0, 0, 0, 0) !important; - -webkit-backdrop-filter: blur(80px) !important; - backdrop-filter: blur(80px) !important; + margin: 10px; + width: calc(100vw - 20px); + border-radius: 140px; } .bridge-light .logo { @@ -40,7 +46,7 @@ body { .MuiAppBar-positionFixed { background-image: none !important; - padding: 10pt 0; + padding: 0; } diff --git a/src/Header.tsx b/src/Header.tsx index 69006aca..4d0ceae6 100644 --- a/src/Header.tsx +++ b/src/Header.tsx @@ -57,34 +57,29 @@ export default function Header(props: { sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }} > -

- - logo - -
-
- {constants.MAINNET_CHAIN_NAME !== 'mainnet' ? ( - + logo + +
+
+ + + + - ) : null} + className="ml-1.5! h-8 w-8 rounded-full bg-card! text-foreground! hover:bg-muted" + onClick={toggleMode} + > + {mode === 'dark' ? ( + + ) : ( + + )} + + + +
- - - - - {mode === 'dark' ? : } - - - - ) diff --git a/src/Router.tsx b/src/Router.tsx index 77cf80a7..365465bc 100644 --- a/src/Router.tsx +++ b/src/Router.tsx @@ -189,7 +189,9 @@ export default function Router(props: {
-

Loading SKALE Chains

+

+ Loading SKALE Chains +

diff --git a/src/components/CopySurface.tsx b/src/components/CopySurface.tsx index c519491f..2fb01392 100644 --- a/src/components/CopySurface.tsx +++ b/src/components/CopySurface.tsx @@ -30,12 +30,14 @@ import Tooltip from '@mui/material/Tooltip' import ButtonBase from '@mui/material/ButtonBase' import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded' import ContentCopyIcon from '@mui/icons-material/ContentCopy' +import { CircleCheck, Copy } from 'lucide-react' export default function CopySurface(props: { title: string value: string | undefined className?: string tokenMetadata?: types.mp.TokenMetadata + icon?: React.ReactNode }) { const [copied, setCopied] = useState(false) @@ -60,11 +62,11 @@ export default function CopySurface(props: {
-
+
{props.tokenMetadata ? (
) : null} -

+ {props.icon &&

{props.icon}
} +

{props.title} {props.tokenMetadata ? ` (${props.tokenMetadata.decimals ?? constants.DEFAULT_ERC20_DECIMALS})` @@ -84,9 +87,13 @@ export default function CopySurface(props: {

{props.value}

{copied ? ( - +
+ +
) : ( - +
+ +
)} diff --git a/src/components/ErrorTile.tsx b/src/components/ErrorTile.tsx index ccbc45cb..5f38f903 100644 --- a/src/components/ErrorTile.tsx +++ b/src/components/ErrorTile.tsx @@ -25,7 +25,7 @@ import { Tile } from '@skalenetwork/metaport' import Button from '@mui/material/Button' import Collapse from '@mui/material/Collapse' -import ErrorRoundedIcon from '@mui/icons-material/ErrorRounded' +import { FileExclamationPoint } from 'lucide-react' export default function ErrorTile(props: { errorMsg: string | undefined @@ -35,11 +35,10 @@ export default function ErrorTile(props: { return ( } - color="error" + icon={} grow childrenRi={ props.setErrorMsg && ( diff --git a/src/components/HomeBanner.tsx b/src/components/HomeBanner.tsx index ab210dfd..f1e1930b 100644 --- a/src/components/HomeBanner.tsx +++ b/src/components/HomeBanner.tsx @@ -23,9 +23,15 @@ import { Button } from '@mui/material' import { Link } from 'react-router-dom' -import { Sparkles } from 'lucide-react' +import { Badge, Sparkles } from 'lucide-react' +import Avatar from 'boring-avatars' +import { HOME_CARD_COLORS } from '../core/constants' -export default function HomeBanner() { +import MoneyIcon from '../icons/arrows.svg' +import { networks, types } from '@/core' +import { EXPLORE_CARDS } from './HomeComponents' + +function HomeBanner1() { return (
@@ -48,3 +54,52 @@ export default function HomeBanner() {
) } + +export default function HomeBanner(props: { skaleNetwork: types.SkaleNetwork }) { + const keyFeature = networks.KEY_FEATURES[props.skaleNetwork] + const exploreCard = EXPLORE_CARDS.find((card) => card.feature === keyFeature) + + return ( +
+
+
+
+ +
+
+
+
+ +

+ {exploreCard?.name} +

+

+ {exploreCard?.description} +

+ + + +
+
+
+
+ ) +} diff --git a/src/components/HomeComponents.tsx b/src/components/HomeComponents.tsx index 1ec4aea6..e64bb250 100644 --- a/src/components/HomeComponents.tsx +++ b/src/components/HomeComponents.tsx @@ -32,7 +32,9 @@ import { WalletCards, Link2, Star, - Sparkle + Sparkle, + Coins, + Send } from 'lucide-react' import CoinIcon from '../icons/coin.svg' @@ -41,6 +43,7 @@ import SwapIcon from '../icons/swap.svg' import ChainsIcon from '../icons/chains.svg' import MoneyIcon from '../icons/money.svg' import QuestionIcon from '../icons/question.svg' +import ArrowsIcon from '../icons/arrows.svg' import { types } from '@/core' @@ -63,9 +66,21 @@ interface ExploreCard { feature: types.NetworkFeature bgKey: string url?: string + buttonText?: string + buttonIcon?: JSX.Element } export const EXPLORE_CARDS: ExploreCard[] = [ + { + name: 'Bridge to SKALE', + description: 'Blazingly fast transfers, free between SKALE Chains.', + url: '/bridge', + icon: ArrowsIcon, + feature: 'bridge', + bgKey: 'bridge_1234567891', + buttonText: 'Bridge Now', + buttonIcon: + }, { name: 'Stake your SKL', description: @@ -105,7 +120,9 @@ export const EXPLORE_CARDS: ExploreCard[] = [ url: '/credits', icon: MoneyIcon, feature: 'credits', - bgKey: 'credits_12345678' + bgKey: 'creditssdidfdfs', + buttonText: 'Buy Credits', + buttonIcon: }, { name: 'Learn about Chain Credits', diff --git a/src/components/PageCard.tsx b/src/components/PageCard.tsx index 5876c7a7..b9930ed7 100644 --- a/src/components/PageCard.tsx +++ b/src/components/PageCard.tsx @@ -59,7 +59,6 @@ export default function PageCard(props: {
diff --git a/src/components/SchainDetails.tsx b/src/components/SchainDetails.tsx index c89e7953..bafa2dff 100644 --- a/src/components/SchainDetails.tsx +++ b/src/components/SchainDetails.tsx @@ -29,34 +29,34 @@ import { type types, metadata, constants, endpoints, networks } from '@/core' import Button from '@mui/material/Button' -import AddCircleRoundedIcon from '@mui/icons-material/AddCircleRounded' -import ArrowOutwardRoundedIcon from '@mui/icons-material/ArrowOutwardRounded' import ArrowBackIosNewRoundedIcon from '@mui/icons-material/ArrowBackIosNewRounded' import LinkRoundedIcon from '@mui/icons-material/LinkRounded' -import TrendingUpRoundedIcon from '@mui/icons-material/TrendingUpRounded' -import PersonRoundedIcon from '@mui/icons-material/PersonRounded' -import GridViewRoundedIcon from '@mui/icons-material/GridViewRounded' -import DataSaverOffRoundedIcon from '@mui/icons-material/DataSaverOffRounded' -import SavingsRoundedIcon from '@mui/icons-material/SavingsRounded' -import ViewInArRoundedIcon from '@mui/icons-material/ViewInArRounded' -import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded' import SkStack from './SkStack' import Breadcrumbs from './Breadcrumbs' import CollapsibleDescription from './CollapsibleDescription' -import SkBtn from './SkBtn' import Logo from './Logo' import { getRpcUrl, getChainId } from '../core/chain' import { formatNumber } from '../core/timeHelper' import ChainTabsSection from './chains/tabs/ChainTabsSection' import CategoriesChips from './ecosystem/CategoriesChips' +import { + BadgeCheck, + Blocks, + ChartPie, + CirclePlus, + ExternalLink, + Grid2x2, + HandCoins, + TrendingUp, + Users +} from 'lucide-react' export default function SchainDetails(props: { - schainName: string chainsMeta: types.ChainsMetadataMap schainStats: types.IStatsData | null schainMetrics: types.IChainMetrics | null - chain: any + chain: types.ISChain mpc: MetaportCore isXs: boolean }) { @@ -66,24 +66,21 @@ export default function SchainDetails(props: { const network = props.mpc.config.skaleNetwork const proxyBase = endpoints.getProxyEndpoint(network) - const rpcUrl = getRpcUrl(proxyBase, props.schainName, constants.HTTPS_PREFIX) + const rpcUrl = getRpcUrl(proxyBase, props.chain.name, constants.HTTPS_PREFIX) const explorerUrl = explorer.getExplorerUrl( - props.chainsMeta[props.schainName], + props.chainsMeta[props.chain.name], network, - props.schainName + props.chain.name ) - const chainId = getChainId(props.schainName) + const chainId = getChainId(props.chain.name) const networkParams = { chainId, - chainName: - 'SKALE' + - (network === 'testnet' ? ' Testnet ' : ' ') + - metadata.getAlias(network, props.chainsMeta, props.schainName), + chainName: metadata.getAlias(network, props.chainsMeta, props.chain.name), rpcUrls: [rpcUrl], nativeCurrency: { - name: 'sFUEL', - symbol: 'sFUEL', + name: networks.NATIVE_TOKEN_SYMBOLS[network], + symbol: networks.NATIVE_TOKEN_SYMBOLS[network], decimals: 18 }, blockExplorerUrls: [explorerUrl] @@ -109,9 +106,9 @@ export default function SchainDetails(props: { return loading ? 'Connecting Chain' : 'Connect to Chain' } - const chainMeta = props.chainsMeta[props.schainName] + const chainMeta = props.chainsMeta[props.chain.name] - const chainAlias = metadata.getAlias(network, props.chainsMeta, props.schainName) + const chainAlias = metadata.getAlias(network, props.chainsMeta, props.chain.name) const chainDescription = metadata.getChainDescription(chainMeta) const isMainnet = props.mpc.config.skaleNetwork === constants.MAINNET_CHAIN_NAME @@ -159,89 +156,84 @@ export default function SchainDetails(props: { - -
-
- -
-
-
- -
+ +
+ +
+
+
+
- -

{chainAlias}

- + {props.isXs || !props.schainMetrics ? null : ( +
+ +

+ {props.schainMetrics + ? formatNumber(props.schainMetrics.chain_stats?.transactions_today) + : '...'} + + Daily Tx +

+
+ )}
+

{chainAlias}

+
- - - - - - - : } - size="md" - className={`mr-2.5 text-accent-foreground! p-4! py-3! ${loading ? 'btnPaddLoading' : ''} ${props.isXs ? 'w-full' : ''}`} - onClick={addNetwork} - disabled={loading} - text={connectBtnText()} - loading={loading} - /> - {chainMeta?.url && ( - - - - )} -
+ +
+ + + + - {networks.hasFeature(network, 'metrics') ? ( - + {chainMeta?.url && ( + + + + )} +
+
+ {networks.hasFeature(network, 'metrics') && ( + + } + icon={} /> {isMainnet && ( } + icon={} /> )} } + icon={} /> } + icon={} /> - ) : ( -
- )} -
+ + )}
diff --git a/src/components/TermsModal.tsx b/src/components/TermsModal.tsx index fb691dd6..336dd3d0 100644 --- a/src/components/TermsModal.tsx +++ b/src/components/TermsModal.tsx @@ -62,7 +62,10 @@ export default function TermsModal(props: { if (props.termsAccepted) return null return (
- +

{title}

@@ -108,10 +111,7 @@ export default function TermsModal(props: { Before you use the SKALE {title}, you must review the terms of service carefully and confirm below:

-
+
= ({ return (
{chainMeta && chainMeta.url && ( - - - - - - )} - - } + className="font-sans! bg-secondary-foreground/10! py-2! px-3! capitalize! text-foreground! font-semibold! text-xs! mr-2! ease-in-out transition-transform duration-150 active:scale-[0.97]" > - - - + Website + + )} +
) } diff --git a/src/components/chains/HubTile.tsx b/src/components/chains/HubTile.tsx index f0241e82..f26050df 100644 --- a/src/components/chains/HubTile.tsx +++ b/src/components/chains/HubTile.tsx @@ -35,6 +35,7 @@ import ChainLogo from '../ChainLogo' import { formatNumber } from '../../core/timeHelper' import { MAINNET_CHAIN_LOGOS } from '../../core/constants' +import { TrendingUp } from 'lucide-react' export default function HubTile(props: { network: types.SkaleNetwork @@ -94,7 +95,7 @@ export default function HubTile(props: {
{props.isXs || !props.showStats ? null : (
- +

{schainMetrics ? formatNumber(schainMetrics.chain_stats?.transactions_today) diff --git a/src/components/chains/tabs/ChainTabsSection.tsx b/src/components/chains/tabs/ChainTabsSection.tsx index b8d43f8f..cc22c717 100644 --- a/src/components/chains/tabs/ChainTabsSection.tsx +++ b/src/components/chains/tabs/ChainTabsSection.tsx @@ -22,31 +22,27 @@ */ import { useEffect, useState } from 'react' +import { Bolt, Coins, FileCheck, Heart } from 'lucide-react' import { MetaportCore, SkPaper, explorer } from '@skalenetwork/metaport' import { type types } from '@/core' -import WidgetsRoundedIcon from '@mui/icons-material/WidgetsRounded' -import ConstructionRoundedIcon from '@mui/icons-material/ConstructionRounded' -import PlaylistAddCheckCircleRoundedIcon from '@mui/icons-material/PlaylistAddCheckCircleRounded' -import AccountBalanceWalletRoundedIcon from '@mui/icons-material/AccountBalanceWalletRounded' - import ChainTabs from './Tabs' import DeveloperInfo from './DeveloperInfo' import Tokens from './Tokens' -import VerifiedContracts from './VerifiedContracts' +// import VerifiedContracts from './VerifiedContracts' import FeaturedApps from '../../ecosystem/tabs/FeaturedApps' import { useApps } from '../../../useApps' const BASE_TABS = [ { label: 'Developer info', - icon: - }, - { - label: 'Contracts', - icon: + icon: } + // { + // label: 'Contracts', + // icon: + // } ] interface TabPanelProps { @@ -75,25 +71,21 @@ function CustomTabPanel(props: TabPanelProps) { export default function ChainTabsSection(props: { mpc: MetaportCore chainsMeta: types.ChainsMetadataMap - schainName: string + chain: types.ISChain isXs: boolean }) { const network = props.mpc.config.skaleNetwork - const chainMeta = props.chainsMeta[props.schainName] + const chainMeta = props.chainsMeta[props.chain.name] const { featuredApps, newApps, trendingApps } = useApps(props.chainsMeta, null) - const chainFeaturedApps = featuredApps.filter((app) => app.chain === props.schainName) + const chainFeaturedApps = featuredApps.filter((app) => app.chain === props.chain.name) - const explorerUrl = explorer.getExplorerUrl(chainMeta, network, props.schainName) + // const explorerUrl = explorer.getExplorerUrl(chainMeta, network, props.schainName) const BASE_TABS_CONTENT = [ - , - + + // ] const [tab, setTab] = useState(0) @@ -101,38 +93,37 @@ export default function ChainTabsSection(props: { const [tabsContent, setTabsContent] = useState(BASE_TABS_CONTENT) useEffect(() => { - const tokenConnections = props.mpc.config.connections[props.schainName] ?? {} + const tokenConnections = props.mpc.config.connections[props.chain.name] ?? {} const chainTokens = tokenConnections.erc20 ?? {} const hasTokens = Object.keys(chainTokens).length !== 0 const currentTabs = [...BASE_TABS] const currentTabsContent = [...BASE_TABS_CONTENT] + + // if ( + // props.chainsMeta[props.schainName] && + // props.chainsMeta[props.schainName].apps && + // chainFeaturedApps.length > 0 + // ) { + // currentTabs.unshift({ label: 'Featured Apps', icon: }) + // currentTabsContent.unshift( + //

+ // + //
+ // ) + // } if (hasTokens) { - currentTabs.unshift({ label: 'Tokens', icon: }) - currentTabsContent.unshift() - } - if ( - props.chainsMeta[props.schainName] && - props.chainsMeta[props.schainName].apps && - chainFeaturedApps.length > 0 - ) { - currentTabs.unshift({ label: 'Featured Apps', icon: }) - currentTabsContent.unshift( - -
- -
-
- ) + currentTabs.push({ label: 'Tokens', icon: }) + currentTabsContent.push() } setTabs(currentTabs) setTabsContent(currentTabsContent) @@ -143,20 +134,24 @@ export default function ChainTabsSection(props: { } return ( -
- - {tabsContent.map((content, index) => ( - - {content} - - ))} -
+ +
+ +
+
+ {tabsContent.map((content, index) => ( + + {content} + + ))} +
+
) } diff --git a/src/components/chains/tabs/DeveloperInfo.tsx b/src/components/chains/tabs/DeveloperInfo.tsx index 64a0bad8..34589205 100644 --- a/src/components/chains/tabs/DeveloperInfo.tsx +++ b/src/components/chains/tabs/DeveloperInfo.tsx @@ -21,49 +21,136 @@ * @copyright SKALE Labs 2024-Present */ -import { SkPaper } from '@skalenetwork/metaport' import { type types, endpoints, constants } from '@/core' import CopySurface from '../../CopySurface' -import { getRpcUrl, getRpcWsUrl, getFsUrl, getChainId } from '../../../core/chain' +import { + getRpcUrl, + getRpcWsUrl, + getFsUrl, + getChainId, + getChainIdInt, + getAllocationTypeName, + hasFilestorage +} from '../../../core/chain' +import { + BadgeInfo, + FastForward, + FileBox, + FileCodeCorner, + FileDigit, + GlobeLock, + HardDrive, + Rabbit, + RadioTower, + ScanQrCode, + UserRoundCheck, + UserStar +} from 'lucide-react' export default function DeveloperInfo(props: { - schainName: string + chain: types.ISChain skaleNetwork: types.SkaleNetwork className?: string shortAlias?: string | undefined }) { const proxyBase = endpoints.getProxyEndpoint(props.skaleNetwork) - const rpcUrl = getRpcUrl(proxyBase, props.shortAlias || props.schainName, constants.HTTPS_PREFIX) + const rpcUrl = getRpcUrl(proxyBase, props.shortAlias || props.chain.name, constants.HTTPS_PREFIX) const rpcWssUrl = getRpcWsUrl( proxyBase, - props.shortAlias || props.schainName, + props.shortAlias || props.chain.name, constants.WSS_PREFIX ) - const fsUrl = getFsUrl(proxyBase, props.shortAlias || props.schainName, constants.HTTPS_PREFIX) + const fsUrl = getFsUrl(proxyBase, props.shortAlias || props.chain.name, constants.HTTPS_PREFIX) - const chainId = getChainId(props.schainName) + const chainId = getChainId(props.chain.name) + const chainIdInt = getChainIdInt(props.chain.name) + const allocationType = getAllocationTypeName(props.chain.allocationType) + const hasFs = hasFilestorage(props.chain.allocationType) return ( - -
+
+

Endpoint links

+
- + } + /> +
+
+ } + /> +
+ {hasFs && ( +
+ } + /> +
+ )} +
+

Chain details

+
+
+ } + /> +
+
+ } + />
- + } + />
- + } + />
- + } + />
- + } + />
- +
) } diff --git a/src/components/chains/tabs/Tabs.tsx b/src/components/chains/tabs/Tabs.tsx index 2b70939e..595cfb94 100644 --- a/src/components/chains/tabs/Tabs.tsx +++ b/src/components/chains/tabs/Tabs.tsx @@ -26,11 +26,12 @@ import { Link } from 'react-router-dom' import Tabs from '@mui/material/Tabs' import Tab from '@mui/material/Tab' +import { Button, IconButton, Tooltip } from '@mui/material' + +import { Settings2 } from 'lucide-react' import { networks, type types } from '@/core' -import AdminPanelSettingsRoundedIcon from '@mui/icons-material/AdminPanelSettingsRounded' -import { Button } from '@mui/material' import { NETWORKS } from '../../../core/constants' export default function ChainTabs(props: { @@ -42,7 +43,7 @@ export default function ChainTabs(props: { isXs: boolean }) { return ( -
+
) : null )} +
{networks.hasFeatureInAny(NETWORKS, 'paymaster') && ( - - - + + + + + + + )}
diff --git a/src/components/chains/tabs/Tokens.tsx b/src/components/chains/tabs/Tokens.tsx index df9a3928..2ef8c2cf 100644 --- a/src/components/chains/tabs/Tokens.tsx +++ b/src/components/chains/tabs/Tokens.tsx @@ -22,7 +22,7 @@ */ import { type types } from '@/core' -import { type MetaportCore, SkPaper } from '@skalenetwork/metaport' +import { type MetaportCore } from '@skalenetwork/metaport' import CopySurface from '../../CopySurface' import { getAddress } from 'ethers' @@ -70,12 +70,26 @@ export default function Tokens(props: { }) } + const totalTokensCount = Object.entries({ ...ethToken, ...chainTokens }).reduce( + (acc, [, tokenData]: [string, any]) => { + const wrapperAddress = findWrapperAddress(tokenData) + return acc + 1 + (wrapperAddress ? 1 : 0) + }, + 0 + ) + return ( - -
+
+
+

Tokens

+
+

{totalTokensCount}

+
+
+
{renderTokens(ethToken)} {renderTokens(chainTokens)}
- +
) } diff --git a/src/components/chains/tabs/VerifiedContracts.tsx b/src/components/chains/tabs/VerifiedContracts.tsx index 1c99c42f..96645146 100644 --- a/src/components/chains/tabs/VerifiedContracts.tsx +++ b/src/components/chains/tabs/VerifiedContracts.tsx @@ -69,8 +69,8 @@ export default function VerifiedContracts(props: { } return ( - -
+
+
{contracts.map((contract: any, index: number) => (
)} - +
) } diff --git a/src/components/credits/ChainCreditsTile.tsx b/src/components/credits/ChainCreditsTile.tsx index 065dd622..b6c2c96a 100644 --- a/src/components/credits/ChainCreditsTile.tsx +++ b/src/components/credits/ChainCreditsTile.tsx @@ -25,34 +25,43 @@ import { type MetaportCore, Tile, SkPaper, - styles, TokenIcon, useWagmiAccount, sendTransaction, walletClientToSigner, useWagmiWalletClient, useWagmiSwitchNetwork, - enforceNetwork + enforceNetwork, + ChainIcon } from '@skalenetwork/metaport' -import { types, metadata, units, constants, helper, ERC_ABIS } from '@/core' + +import { + Coins, + Wallet, + ArrowRightLeft, + Shuffle, + Fuel, + HandCoins, + ArrowRight, + CoinsIcon +} from 'lucide-react' + +import { types, metadata, units, constants, ERC_ABIS } from '@/core' +import { MAINNET_ALIASES } from '@/core/networks' import { useState, useEffect } from 'react' import { Grid, Button, Dialog } from '@mui/material' import AddCircleRoundedIcon from '@mui/icons-material/AddCircleRounded' -import MonetizationOnRoundedIcon from '@mui/icons-material/MonetizationOnRounded' import Logo from '../Logo' import SkStack from '../SkStack' -import Headline from '../Headline' import { Contract } from 'ethers' import { Link } from 'react-router-dom' import { - AVATAR_COLORS, CREDITS_CONFIRMATION_BLOCKS, - DEFAULT_CREDITS_AMOUNT + DEFAULT_CREDITS_AMOUNT, + CREDITS_USAGE_EXAMPLE_PER_CREDIT } from '../../core/constants' -import Avatar from 'boring-avatars' -import { BadgeDollarSign, ChevronRight, Coins, Wallet } from 'lucide-react' interface ChainCreditsTileProps { mpc: MetaportCore @@ -118,9 +127,14 @@ const ChainCreditsTile: React.FC = ({ if (tokenBalances?.[token] === undefined) return 'Loading...' if (loading) return 'Processing...' if (tokenBalances[token] < getAmountToPayWei()) { - return 'Insufficient Token Balance' + const displayBalance = units.displayBalance( + tokenBalances?.[token] || 0n, + token, + tokensMeta[token].decimals + ) + return 'Insufficient Token Balance: ' + displayBalance } - return 'Buy Credits' + return `Buy ${DEFAULT_CREDITS_AMOUNT} Credits` } async function buyCredits() { @@ -223,7 +237,7 @@ const ChainCreditsTile: React.FC = ({ 18 ) } - text="Available" + text="Balance" grow ri={!isXs} icon={} @@ -234,7 +248,7 @@ const ChainCreditsTile: React.FC = ({ size="small" variant="contained" startIcon={} - className="btnMd ml-5 bg-accent-foreground! text-accent!" + className="btnMd ml-5 bg-accent-foreground! disabled:bg-accent-foreground/50! text-accent! ease-in-out transition-transform duration-150 active:scale-[0.97]" onClick={() => setOpenModal(true)} disabled={creditStation === undefined} > @@ -250,6 +264,11 @@ const ChainCreditsTile: React.FC = ({ onClose={() => setOpenModal(false)} maxWidth="sm" fullWidth + BackdropProps={{ + sx: { + backdropFilter: 'blur(4px)' + } + }} PaperProps={{ sx: { background: 'transparent', @@ -257,21 +276,16 @@ const ChainCreditsTile: React.FC = ({ } }} > - - } - size="small" - /> -

- All purchases are converted to SKL on the backend for distribution per governance - agreements. -

+ +
+

Buy Credits

+
} children={ @@ -285,7 +299,8 @@ const ChainCreditsTile: React.FC = ({ color="primary" size="small" className={cls( - 'items-center mr-2.5! uppercase btnLg bg-accent-foreground! text-accent!', + 'items-center mr-2.5! p-4! py-3! pr-5! rounded-full! uppercase btnLg bg-accent-foreground/30! text-foreground!', + 'ease-in-out transition-transform duration-150 active:scale-[0.97]', ['bg-card!', symbol !== token], ['text-foreground!', symbol !== token] )} @@ -296,6 +311,7 @@ const ChainCreditsTile: React.FC = ({ {symbol}
@@ -307,61 +323,71 @@ const ChainCreditsTile: React.FC = ({ } /> - - - ) - } - /> - - - - +
+ + } + className="py-5! px-6!" + children={ +
+

+ Pay on{' '} + {' '} + {MAINNET_ALIASES[network]} +

+

+ {units.displayBalance( + getAmountToPayWei(), + token, + tokensMeta[token].decimals + )} +

+
+ } /> - -
- -
-
- - + + } + className="py-5! px-6!" + children={ +
+

+ + Receive on{' '} + + + + {chainAlias} + +

+

+ {DEFAULT_CREDITS_AMOUNT} CREDITS +

+
+ } /> - +
- +
+
+ +
+
+
+

+ {DEFAULT_CREDITS_AMOUNT} CREDITS is enough for thousands of transactions depending on + type: +

+
+

+ + {Number( + DEFAULT_CREDITS_AMOUNT * CREDITS_USAGE_EXAMPLE_PER_CREDIT.transfers + ).toLocaleString()}{' '} + Credit transfers +

+

+ + {Number( + DEFAULT_CREDITS_AMOUNT * CREDITS_USAGE_EXAMPLE_PER_CREDIT.x402 + ).toLocaleString()}{' '} + x402 transfers +

+

+ + {Number( + DEFAULT_CREDITS_AMOUNT * CREDITS_USAGE_EXAMPLE_PER_CREDIT.ammSwaps + ).toLocaleString()}{' '} + AMM swaps +

+

+ + {Number( + DEFAULT_CREDITS_AMOUNT * CREDITS_USAGE_EXAMPLE_PER_CREDIT.gasUnits + ).toLocaleString()}{' '} + Gas units +

+
+
+

+ These are estimated transaction amounts which can change at any time based on chain + consumption. +

+

+ All purchases are converted to SKL on the backend for distribution per governance + agreements. +

+
diff --git a/src/components/credits/CreditStationStatusTile.tsx b/src/components/credits/CreditStationStatusTile.tsx index fc709459..a02d3452 100644 --- a/src/components/credits/CreditStationStatusTile.tsx +++ b/src/components/credits/CreditStationStatusTile.tsx @@ -111,7 +111,7 @@ const CreditStationStatusTile: React.FC = ({ return (
= ({ ? timeUtils.timestampToDate(txTimestamp, true) : helper.shortAddress(payment.from)} -

{chainAlias}

+

+ {isAdmin ? timeUtils.timestampToDate(txTimestamp, true) : chainAlias} +

diff --git a/src/components/credits/TokensAdmin.tsx b/src/components/credits/TokensAdmin.tsx index 34345e5c..1e2c3de9 100644 --- a/src/components/credits/TokensAdmin.tsx +++ b/src/components/credits/TokensAdmin.tsx @@ -24,12 +24,10 @@ import { Contract, ethers } from 'ethers' import { useEffect, useState } from 'react' import { type MetaportCore, SkPaper } from '@skalenetwork/metaport' -import { types } from '@/core' +import { constants, types } from '@/core' import * as cs from '../../core/credit-station' -import GeneratingTokensRoundedIcon from '@mui/icons-material/GeneratingTokensRounded' import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded' -import EvStationRoundedIcon from '@mui/icons-material/EvStationRounded' import TokenAdminTile from './TokenAdminTile' import AccordionSection from '../AccordionSection' @@ -95,7 +93,10 @@ const CreditTokensAdmin: React.FC = ({ async function loadAllPayments() { setIsLoading(true) - const events = await cs.getAllPaymentEvents(creditStation) + const events = await cs.getAllPaymentEvents( + creditStation, + constants.CREDIT_STATION_START_BLOCKS[mpc.config.skaleNetwork] + ) setAllPayments(events) setIsLoading(false) } @@ -159,7 +160,9 @@ const CreditTokensAdmin: React.FC = ({
{isLoading && (
-

Loading purchases history...

+

+ Loading purchases history... +

)} {!isLoading && creditsHistory.length === 0 && ( diff --git a/src/core/chain.ts b/src/core/chain.ts index 7ebbaa85..1a38714f 100644 --- a/src/core/chain.ts +++ b/src/core/chain.ts @@ -83,3 +83,28 @@ export function getFsUrl(proxyUrl: string, schainName: string, prefix: string): export function getChainId(schainName: string): string { return toQuantity(toBeHex(id(schainName).substring(0, 15))) } + +export function getChainIdInt(schainName: string): number { + return parseInt(getChainId(schainName), 16) +} + +export function getAllocationTypeName(allocationType: number): string { + switch (allocationType) { + case 0: + return 'Default' + case 1: + return 'No Filestorage' + case 2: + return 'Max Contract Storage' + case 3: + return 'Max Consensus DB' + case 4: + return 'Max Filestorage' + default: + return 'Unknown' + } +} + +export function hasFilestorage(allocationType: number): boolean { + return allocationType === 0 || allocationType === 4 +} diff --git a/src/core/constants.ts b/src/core/constants.ts index 11f9c001..0bfcf122 100644 --- a/src/core/constants.ts +++ b/src/core/constants.ts @@ -101,6 +101,12 @@ export const NETWORKS = networks.parse(import.meta.env.VITE_NETWORKS) export const CREDITS_CONFIRMATION_BLOCKS = 2 export const DEFAULT_CREDITS_AMOUNT = 40n +export const CREDITS_USAGE_EXAMPLE_PER_CREDIT = { + transfers: 1000n, + x402: 250n, + ammSwaps: 125n, + gasUnits: 21000000n +} as const export const AVATAR_COLORS = [ '#efeecc', diff --git a/src/core/credit-station.ts b/src/core/credit-station.ts index c51fd5ab..a0a8f4f6 100644 --- a/src/core/credit-station.ts +++ b/src/core/credit-station.ts @@ -102,13 +102,16 @@ export async function getPaymentEvents( if (!provider) return [] const currentBlock = await provider.getBlockNumber() - const startBlock = fromBlock ?? currentBlock - MAX_BLOCKS_TO_SCAN - const endBlock = fromBlock ?? currentBlock + const endBlock = currentBlock + const startBlock = fromBlock ?? Math.max(currentBlock - MAX_BLOCKS_TO_SCAN, 0) const filter = creditStation.filters.PaymentReceived(null, null, address ?? null) const allEvents: EventLog[] = [] for (let block = startBlock; block <= endBlock; block += CHUNK_SIZE) { + console.log( + `Fetching events from block ${block} to ${Math.min(block + CHUNK_SIZE - 1, endBlock)}` + ) const toBlock = Math.min(block + CHUNK_SIZE - 1, endBlock) const events = await creditStation.queryFilter(filter, block, toBlock) const eventLogs = events.filter((event): event is EventLog => 'args' in event) diff --git a/src/icons/arrows.svg b/src/icons/arrows.svg new file mode 100644 index 00000000..5c9c7eb3 --- /dev/null +++ b/src/icons/arrows.svg @@ -0,0 +1,33 @@ + + roadmap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/pages/Chain.tsx b/src/pages/Chain.tsx index e9098f8c..ed70df35 100644 --- a/src/pages/Chain.tsx +++ b/src/pages/Chain.tsx @@ -22,15 +22,17 @@ */ import { useState, useEffect } from 'react' - import { useParams } from 'react-router-dom' -import Container from '@mui/material/Container' -import SchainDetails from '../components/SchainDetails' + import CircularProgress from '@mui/material/CircularProgress' +import Container from '@mui/material/Container' import { type MetaportCore } from '@skalenetwork/metaport' import { type types, metadata } from '@/core' +import SchainDetails from '../components/SchainDetails' +import ErrorTile from '../components/ErrorTile' + export default function Chain(props: { loadData: () => Promise schains: types.ISChain[] @@ -71,7 +73,9 @@ export default function Chain(props: {
-

Loading SKALE Chain

+

+ Loading SKALE Chain +

@@ -79,13 +83,16 @@ export default function Chain(props: { } if (chain === undefined || chain === null) { - return

No such chain: {chainName}

+ return ( + + + + ) } return (
-

Loading SKALE Chains

+

+ Loading SKALE Chains +

diff --git a/src/pages/Credits.tsx b/src/pages/Credits.tsx index f817aa11..cccc28a1 100644 --- a/src/pages/Credits.tsx +++ b/src/pages/Credits.tsx @@ -27,7 +27,7 @@ import { useState, useEffect } from 'react' import { Contract } from 'ethers' -import { cls, type MetaportCore, SkPaper, styles } from '@skalenetwork/metaport' +import { cls, type MetaportCore, SkPaper } from '@skalenetwork/metaport' import { constants, dc, type types } from '@/core' import * as cs from '../core/credit-station' @@ -36,9 +36,6 @@ import Stack from '@mui/material/Stack' import CircularProgress from '@mui/material/CircularProgress' import { Collapse } from '@mui/material' -import LinkRoundedIcon from '@mui/icons-material/LinkRounded' -import HistoryRoundedIcon from '@mui/icons-material/HistoryRounded' - import { META_TAGS } from '../core/meta' import SkPageInfoIcon from '../components/SkPageInfoIcon' import AccordionSection from '../components/AccordionSection' @@ -86,7 +83,9 @@ const Credits: React.FC = ({ mpc, address, isXs, loadData, schains for (const schain of schains) { const startBlock = - isUpdate && lastBlocks[schain.name] ? lastBlocks[schain.name] + 1 : undefined + isUpdate && lastBlocks[schain.name] + ? lastBlocks[schain.name] + 1 + : constants.CREDIT_STATION_START_BLOCKS[mpc.config.skaleNetwork] const events = await cs.getPaymentEvents(creditStation, address, startBlock) if (isUpdate) { @@ -181,7 +180,9 @@ const Credits: React.FC = ({ mpc, address, isXs, loadData, schains
-

Loading credits info

+

+ Loading credits info +

diff --git a/src/pages/CreditsAdmin.tsx b/src/pages/CreditsAdmin.tsx index f98207c3..79834995 100644 --- a/src/pages/CreditsAdmin.tsx +++ b/src/pages/CreditsAdmin.tsx @@ -72,7 +72,9 @@ const Credits: React.FC = ({ mpc, isXs, loadData, schains, chainsM
-

Loading credits info

+

+ Loading credits info +

diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 4b532a06..9b57b159 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -46,6 +46,11 @@ interface HomeProps { metrics: types.IMetrics | null loadData: () => Promise } + +interface ExploreSectionProps { + skaleNetwork: types.SkaleNetwork +} + export default function Home({ skaleNetwork, chainsMeta, metrics, loadData }: HomeProps) { const { newApps, trendingApps, featuredApps } = useApps(chainsMeta, metrics) @@ -56,8 +61,8 @@ export default function Home({ skaleNetwork, chainsMeta, metrics, loadData }: Ho
- - + + {networks.hasFeatureInAny(NETWORKS, 'ecosystem') && (
- {EXPLORE_CARDS.map( + {EXPLORE_CARDS.filter((card) => card.feature !== keyFeature).map( (card, index) => networks.hasFeatureInAny(NETWORKS, card.feature) && (
diff --git a/src/pages/Terms.tsx b/src/pages/Terms.tsx index bb3bde3e..b0aa40c7 100644 --- a/src/pages/Terms.tsx +++ b/src/pages/Terms.tsx @@ -15,7 +15,6 @@ export default function Terms() { - ) diff --git a/src/styles/components.scss b/src/styles/components.scss index 1342f9e8..6e1f552e 100644 --- a/src/styles/components.scss +++ b/src/styles/components.scss @@ -57,8 +57,8 @@ } .sk-logo-md { - width: 170px; - height: 170px; + width: 150px; + height: 150px; .responsive-logo { padding: 20px !important;