Skip to content

Commit

Permalink
Merge pull request #391 from skalenetwork/fix-sfuel-status
Browse files Browse the repository at this point in the history
Fix sFUEL status checks, add sFUEL completion percentage
  • Loading branch information
dmytrotkk authored Oct 17, 2024
2 parents 0dc1b70 + 8ae1600 commit dc4f288
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 66 deletions.
7 changes: 1 addition & 6 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,7 @@ import MetricsWarning from './components/MetricsWarning'
import ScrollToTop from './components/ScrollToTop'

import { getHistoryFromStorage, setHistoryToStorage } from './core/transferHistory'
import {
BRIDGE_PAGES,
MAINNET_CHAIN_NAME,
STAKING_PAGES,
STATS_API
} from './core/constants'
import { BRIDGE_PAGES, MAINNET_CHAIN_NAME, STAKING_PAGES, STATS_API } from './core/constants'
import { type IValidator, type ISkaleContractsMap, type StakingInfoMap } from './core/interfaces'
import { getValidators } from './core/delegation/validators'
import { initContracts } from './core/contracts'
Expand Down
26 changes: 19 additions & 7 deletions src/components/GetSFuel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,21 @@

import { Box, Button, Tooltip } from '@mui/material'
import BoltRoundedIcon from '@mui/icons-material/BoltRounded'
import AutoModeRoundedIcon from '@mui/icons-material/AutoModeRounded'
import { cls, styles, cmn, type MetaportCore, useWagmiAccount } from '@skalenetwork/metaport'
import { usesFuel } from '../useSFuel'

export default function GetSFuel({ mpc }: { mpc: MetaportCore }) {
const { sFuelOk, isMining, mineSFuel } = usesFuel(mpc)
const { sFuelOk, isMining, mineSFuel, sFuelCompletionPercentage, loading } = usesFuel(mpc)
const { address } = useWagmiAccount()
if (!address) return null

function btnText() {
if (isMining) return `Getting sFUEL - ${sFuelCompletionPercentage}%`
if (loading) return 'Checking sFUEL'
return sFuelOk ? 'sFUEL OK' : 'Get sFUEL'
}

return (
<Box
sx={{ alignItems: 'center', textAlign: 'center', display: { xs: 'none', sm: 'flex' } }}
Expand All @@ -38,15 +46,19 @@ export default function GetSFuel({ mpc }: { mpc: MetaportCore }) {
<Tooltip arrow title={sFuelOk ? 'sFUEL balance is OK' : 'Click to get sFUEL for all chains'}>
<Button
onClick={sFuelOk ? undefined : mineSFuel}
disabled={isMining}
disabled={isMining || loading || sFuelOk}
className={cls('mp__btnConnect', styles.paperGrey, [cmn.pPrim, !isMining], cmn.flex)}
color="success"
>
<BoltRoundedIcon
className={cls(cmn.mri5, styles.chainIconxs)}
color={sFuelOk ? 'success' : 'primary'}
/>
{isMining ? 'Getting sFUEL...' : sFuelOk ? 'sFUEL OK' : 'Get sFUEL'}
{loading ? (
<AutoModeRoundedIcon className={cls(cmn.mri5, styles.chainIconxs)} />
) : (
<BoltRoundedIcon
className={cls(cmn.mri5, styles.chainIconxs)}
color={sFuelOk ? 'success' : 'primary'}
/>
)}
{btnText()}
</Button>
</Tooltip>
</Box>
Expand Down
8 changes: 1 addition & 7 deletions src/pages/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,7 @@ export default function App(props: {
chainsMeta: types.ChainsMetadataMap
}) {
let { chain, app } = useParams()
const {
likedApps,
appLikes,
toggleLikedApp,
getAppId,
refreshLikedApps
} = useLikedApps()
const { likedApps, appLikes, toggleLikedApp, getAppId, refreshLikedApps } = useLikedApps()
const { isSignedIn, handleSignIn } = useAuth()

const { address } = useWagmiAccount()
Expand Down
8 changes: 4 additions & 4 deletions src/pages/Ecosystem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,11 @@ export default function Ecosystem(props: {
3,
isSignedIn
? favoriteApps.filter((app) =>
filteredApps.some(
(filteredApp) =>
filteredApp.chain === app.chain && filteredApp.appName === app.appName
filteredApps.some(
(filteredApp) =>
filteredApp.chain === app.chain && filteredApp.appName === app.appName
)
)
)
: []
] // Favorite Apps
])
Expand Down
155 changes: 113 additions & 42 deletions src/useSFuel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,56 +21,83 @@
* @copyright SKALE Labs 2024-Present
*/

import { useState, useEffect } from 'react'
import { useState, useEffect, useCallback, useMemo } from 'react'
import { Logger, type ILogObj } from 'tslog'
import { useWagmiAccount, type MetaportCore, Station } from '@skalenetwork/metaport'
import { DEFAULT_MIN_SFUEL_WEI, SFUEL_CHECK_INTERVAL } from './core/constants'
import { types } from '@/core'

const log = new Logger<ILogObj>({ name: 'useSFuel' })

const CHAINS_TO_SKIP = ['turbulent-unique-scheat'] // todo: tmp fix, remove later

interface SFuelState {
sFuelOk: boolean
isMining: boolean
chainsWithFaucet: string[]
totalChainsWithStation: number
chainsWithEnoughSFuel: number
currentAddress: types.AddressType | null
loading: boolean
intervalId: number | null
}

export function usesFuel(mpc: MetaportCore) {
const { address } = useWagmiAccount()
const [state, setState] = useState({
const [state, setState] = useState<SFuelState>({
sFuelOk: true,
isMining: false,
chainsWithFaucet: [] as string[]
chainsWithFaucet: [] as string[],
totalChainsWithStation: 0,
chainsWithEnoughSFuel: 0,
currentAddress: null,
loading: true,
intervalId: null
})

useEffect(() => {
async function checkFaucetAvailability() {
const chainsWithFaucet = await Promise.all(
mpc.config.chains
.filter((chain) => !CHAINS_TO_SKIP.includes(chain))
.map(async (chain) => {
const station = new Station(chain, mpc)
return (await station.isFaucetAvailable()) ? chain : null
})
).then((chains) => chains.filter((chain): chain is string => chain !== null))
setState((prev) => ({ ...prev, chainsWithFaucet }))
}
checkFaucetAvailability()
const checkFaucetAvailability = useCallback(async () => {
const chainsWithFaucet = await Promise.all(
mpc.config.chains
.filter((chain) => !CHAINS_TO_SKIP.includes(chain))
.map(async (chain) => {
const station = new Station(chain, mpc)
return (await station.isFaucetAvailable()) ? chain : null
})
).then((chains) => chains.filter((chain): chain is string => chain !== null))
setState((prev) => ({
...prev,
chainsWithFaucet,
totalChainsWithStation: chainsWithFaucet.length
}))
}, [mpc.config.chains, mpc.config.skaleNetwork])

async function checkSFuelBalance(): Promise<boolean> {
if (!address) return true
for (const chain of state.chainsWithFaucet) {
if (CHAINS_TO_SKIP.includes(chain)) continue
const { balance } = await new Station(chain, mpc).getData(address)
if (balance < DEFAULT_MIN_SFUEL_WEI) {
setState((prev) => ({ ...prev, sFuelOk: false }))
return false
const checkSFuelBalance = useCallback(
async (currentAddress: types.AddressType): Promise<void> => {
if (!currentAddress || state.chainsWithFaucet.length === 0) return
if (state.currentAddress !== currentAddress) {
setState((prev) => ({ ...prev, currentAddress, loading: true }))
}
}
setState((prev) => ({ ...prev, sFuelOk: true }))
return true
}
let chainsWithEnoughSFuel = 0
let sFuelOk = true
for (const chain of state.chainsWithFaucet) {
if (CHAINS_TO_SKIP.includes(chain)) continue
const { balance } = await new Station(chain, mpc).getData(currentAddress)
if (balance >= DEFAULT_MIN_SFUEL_WEI) {
chainsWithEnoughSFuel++
} else {
sFuelOk = false
}
}
setState((prev) => ({ ...prev, sFuelOk, chainsWithEnoughSFuel, loading: false }))
},
[state.chainsWithFaucet, mpc]
)

const mineSFuel = async () => {
const mineSFuel = useCallback(async () => {
if (!address) return
setState((prev) => ({ ...prev, isMining: true }))
let errorOccurred = false
let chainsWithEnoughSFuel = 0

for (const chain of state.chainsWithFaucet) {
if (CHAINS_TO_SKIP.includes(chain)) continue
Expand All @@ -83,12 +110,20 @@ export function usesFuel(mpc: MetaportCore) {
if (!powResult.ok) {
log.error(`Failed to mine sFuel on chain ${chain}: ${powResult.message}`)
errorOccurred = true
} else {
chainsWithEnoughSFuel++
}
} else {
chainsWithEnoughSFuel++
}
} catch (error) {
log.error(`Error processing chain ${chain}:`, error)
errorOccurred = true
}
setState((prev) => ({
...prev,
chainsWithEnoughSFuel
}))
}

if (errorOccurred) {
Expand All @@ -97,24 +132,60 @@ export function usesFuel(mpc: MetaportCore) {
log.info('sFuel mining completed successfully on all required chains')
}

setState((prev) => ({ ...prev, sFuelOk: !errorOccurred, isMining: false }))
}
setState((prev) => ({
...prev,
sFuelOk: !errorOccurred,
isMining: false,
chainsWithEnoughSFuel
}))
}, [address, state.chainsWithFaucet, mpc])

useEffect(() => {
if (!address) return
let intervalId: NodeJS.Timeout
checkFaucetAvailability()
}, [checkFaucetAvailability])

async function checkAndSetInterval() {
await checkSFuelBalance()
intervalId = setInterval(checkSFuelBalance, SFUEL_CHECK_INTERVAL)
useEffect(() => {
const checkAndSetInterval = async () => {
if (address !== state.currentAddress && state.intervalId) {
clearInterval(state.intervalId)
setState((prev) => ({
...prev,
intervalId: null
}))
}
if (address) {
await checkSFuelBalance(address)
setState((prev) => ({ ...prev, intervalId: newIntervalId }))
const newIntervalId = setInterval(checkSFuelBalance, SFUEL_CHECK_INTERVAL)
} else {
setState((prev) => ({
...prev,
sFuelOk: true,
chainsWithEnoughSFuel: 0,
isChecking: false,
currentAddress: null,
intervalId: null
}))
}
}

checkAndSetInterval()

return () => {
if (intervalId) clearInterval(intervalId)
if (state.intervalId) {
clearInterval(state.intervalId)
}
}
}, [address, state.chainsWithFaucet])

return { ...state, mineSFuel }
}, [address, checkSFuelBalance])

const sFuelCompletionPercentage = useMemo(() => {
if (state.totalChainsWithStation === 0) return 100
return Math.round((state.chainsWithEnoughSFuel / state.totalChainsWithStation) * 100)
}, [state.chainsWithEnoughSFuel, state.totalChainsWithStation])

return {
...state,
mineSFuel,
totalChainsWithStation: state.chainsWithFaucet.length,
chainsWithEnoughSFuel: state.chainsWithEnoughSFuel,
sFuelCompletionPercentage
}
}

0 comments on commit dc4f288

Please sign in to comment.