Compare commits

...

8 Commits

Author SHA1 Message Date
woggioni 8d8fbff51a added new page with node redistribution statistics 2025-12-09 01:05:43 +08:00
bee-worker 369bd275c4 chore(master): release 0.33.3 (#711) 2025-11-17 13:34:32 +01:00
Ferenc Sárai 6d28653bb0 fix: add formbricks environment variables to workflow files (#709) 2025-11-17 13:25:32 +01:00
bee-worker aacb109e3f chore(master): release 0.33.2 (#708) 2025-11-14 15:34:50 +01:00
Ferenc Sárai 82e7c78a81 fix: clarify installation instructions in README (#707) 2025-11-14 15:27:12 +01:00
Bálint Ujvári c8fbf504e7 chore: set formbricks env in pull_request workflow (#103) (#706) 2025-11-14 14:26:14 +01:00
bee-worker 1e52adddf6 chore(master): release 0.33.1 (#705) 2025-11-13 19:09:37 +01:00
Cafe137 096522aa8a fix: put back external wallet balance context (#704)
* fix: put back external wallet balance context

* fix: include top up index page

* fix: include gift code index page

* fix: add BalanceProvider
2025-11-13 19:00:35 +01:00
23 changed files with 355 additions and 69 deletions
+2
View File
@@ -1 +1,3 @@
PORT=3002 PORT=3002
REACT_APP_FORMBRICKS_ENV_ID=
REACT_APP_FORMBRICKS_APP_URL=
+2
View File
@@ -18,6 +18,8 @@ jobs:
env: env:
REACT_APP_BEE_HOST: https://api.test-node.staging.ethswarm.org/ REACT_APP_BEE_HOST: https://api.test-node.staging.ethswarm.org/
REACT_APP_FORMBRICKS_ENV_ID: ${{ secrets.REACT_APP_FORMBRICKS_ENV_ID }}
REACT_APP_FORMBRICKS_APP_URL: ${{ secrets.REACT_APP_FORMBRICKS_APP_URL }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
+5
View File
@@ -8,6 +8,11 @@ on:
jobs: jobs:
publish: publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
REACT_APP_FORMBRICKS_ENV_ID: ${{ secrets.REACT_APP_FORMBRICKS_ENV_ID }}
REACT_APP_FORMBRICKS_APP_URL: ${{ secrets.REACT_APP_FORMBRICKS_APP_URL }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
+5
View File
@@ -9,6 +9,11 @@ on:
jobs: jobs:
publish: publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
REACT_APP_FORMBRICKS_ENV_ID: ${{ secrets.REACT_APP_FORMBRICKS_ENV_ID }}
REACT_APP_FORMBRICKS_APP_URL: ${{ secrets.REACT_APP_FORMBRICKS_APP_URL }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v1 - uses: actions/setup-node@v1
+4
View File
@@ -12,6 +12,10 @@ jobs:
tests: tests:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
REACT_APP_FORMBRICKS_ENV_ID: ${{ secrets.REACT_APP_FORMBRICKS_ENV_ID }}
REACT_APP_FORMBRICKS_APP_URL: ${{ secrets.REACT_APP_FORMBRICKS_APP_URL }}
strategy: strategy:
matrix: matrix:
node-version: [18.x] node-version: [18.x]
+21
View File
@@ -1,5 +1,26 @@
# Changelog # Changelog
## [0.33.3](https://github.com/ethersphere/bee-dashboard/compare/v0.33.2...v0.33.3) (2025-11-17)
### Bug Fixes
* add formbricks environment variables to workflow files ([#709](https://github.com/ethersphere/bee-dashboard/issues/709)) ([6d28653](https://github.com/ethersphere/bee-dashboard/commit/6d28653bb0b731a16d8adabbb9a8c9074387f769))
## [0.33.2](https://github.com/ethersphere/bee-dashboard/compare/v0.33.1...v0.33.2) (2025-11-14)
### Bug Fixes
* clarify installation instructions in README ([#707](https://github.com/ethersphere/bee-dashboard/issues/707)) ([82e7c78](https://github.com/ethersphere/bee-dashboard/commit/82e7c78a818b46031b040f92e9a9491003c9bd92))
## [0.33.1](https://github.com/ethersphere/bee-dashboard/compare/v0.33.0...v0.33.1) (2025-11-13)
### Bug Fixes
* put back external wallet balance context ([#704](https://github.com/ethersphere/bee-dashboard/issues/704)) ([096522a](https://github.com/ethersphere/bee-dashboard/commit/096522aa8a2f11afb0061a6fedbae241967408ef))
## [0.33.0](https://github.com/ethersphere/bee-dashboard/compare/v0.32.0...v0.33.0) (2025-11-12) ## [0.33.0](https://github.com/ethersphere/bee-dashboard/compare/v0.32.0...v0.33.0) (2025-11-12)
+2
View File
@@ -41,6 +41,8 @@ Install globally with npm. We require Node.js's version of at least 12.x and npm
npm install -g @ethersphere/bee-dashboard npm install -g @ethersphere/bee-dashboard
``` ```
For the latest stable version, always use the official npm registry.
## Usage ## Usage
:warning: To successfully connect to the Bee node, you will need to enable CORS. You can do so by setting :warning: To successfully connect to the Bee node, you will need to enable CORS. You can do so by setting
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "@ethersphere/bee-dashboard", "name": "@ethersphere/bee-dashboard",
"version": "0.33.0", "version": "0.33.3",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@ethersphere/bee-dashboard", "name": "@ethersphere/bee-dashboard",
"version": "0.33.0", "version": "0.33.3",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"@ethersphere/bee-js": "^10.1.1", "@ethersphere/bee-js": "^10.1.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@ethersphere/bee-dashboard", "name": "@ethersphere/bee-dashboard",
"version": "0.33.0", "version": "0.33.3",
"description": "An app which helps users to setup their Bee node and do actions like cash out cheques", "description": "An app which helps users to setup their Bee node and do actions like cash out cheques",
"keywords": [ "keywords": [
"bee", "bee",
+3
View File
@@ -13,6 +13,7 @@ import { Provider as PlatformProvider } from './providers/Platform'
import { Provider as SettingsProvider } from './providers/Settings' import { Provider as SettingsProvider } from './providers/Settings'
import { Provider as StampsProvider } from './providers/Stamps' import { Provider as StampsProvider } from './providers/Stamps'
import { Provider as TopUpProvider } from './providers/TopUp' import { Provider as TopUpProvider } from './providers/TopUp'
import { Provider as BalanceProvider } from './providers/WalletBalance'
import BaseRouter from './routes' import BaseRouter from './routes'
import { theme } from './theme' import { theme } from './theme'
@@ -45,6 +46,7 @@ const App = ({
> >
<TopUpProvider> <TopUpProvider>
<BeeProvider> <BeeProvider>
<BalanceProvider>
<StampsProvider> <StampsProvider>
<FileProvider> <FileProvider>
<FileManagerProvider> <FileManagerProvider>
@@ -65,6 +67,7 @@ const App = ({
</FileManagerProvider> </FileManagerProvider>
</FileProvider> </FileProvider>
</StampsProvider> </StampsProvider>
</BalanceProvider>
</BeeProvider> </BeeProvider>
</TopUpProvider> </TopUpProvider>
</SettingsProvider> </SettingsProvider>
+71
View File
@@ -0,0 +1,71 @@
import { RedistributionState, BZZ, DAI } from '@ethersphere/bee-js'
import { useContext, useEffect, useState } from 'react'
import { Context } from '../providers/Settings'
import ExpandableListItem from './ExpandableListItem'
export function Redistribution() {
const { beeApi } = useContext(Context)
const [redistributionState, setRedistributionState] = useState<RedistributionState | null>(null)
useEffect(() => {
const interval = setInterval(() => {
if (!beeApi) {
return
}
beeApi.getRedistributionState().then(setRedistributionState).catch(console.error) // eslint-disable-line
}, 3_000)
return () => clearInterval(interval)
})
const formatDurationSeconds = (s?: number) => {
if (s === null || s === undefined) {
return '-'
} else {
return `${s} s`
}
}
const formatBzzAmount = (amount?: BZZ) => {
if (amount === null || amount === undefined) {
return '-'
} else {
return `${amount.toSignificantDigits(4)} xBZZ`
}
}
const formatDaiAmount = (amount?: DAI) => {
if (amount === null || amount === undefined) {
return '-'
} else {
return `${amount.toSignificantDigits(4)} xDAI`
}
}
return (
<>
<ExpandableListItem
label="Has sufficient funds"
value={redistributionState?.hasSufficientFunds?.toString() ?? '-'}
/>
<ExpandableListItem label="Fully synced" value={redistributionState?.isFullySynced?.toString() ?? '-'} />
<ExpandableListItem label="Frozen" value={redistributionState?.isFrozen?.toString() ?? '-'} />
<ExpandableListItem label="Phase" value={redistributionState?.phase ?? '-'} />
<ExpandableListItem label="Round" value={redistributionState?.round?.toString() ?? '-'} />
<ExpandableListItem
label="Last selected round"
value={redistributionState?.lastSelectedRound.toString() ?? '-'}
/>
<ExpandableListItem label="Last played round" value={redistributionState?.lastPlayedRound.toString() ?? '-'} />
<ExpandableListItem label="Last round won" value={redistributionState?.lastWonRound.toString() ?? '-'} />
<ExpandableListItem label="Last frozen round" value={redistributionState?.lastFrozenRound.toString() ?? '-'} />
<ExpandableListItem
label="Last sample duration"
value={formatDurationSeconds(redistributionState?.lastSampleDurationSeconds)}
/>
<ExpandableListItem label="Reward" value={formatBzzAmount(redistributionState?.reward)} />
<ExpandableListItem label="Fees" value={formatDaiAmount(redistributionState?.fees)} />
</>
)
}
+6
View File
@@ -8,6 +8,7 @@ import FileManagerIcon from 'remixicon-react/FolderOpenLineIcon'
import DocsIcon from 'remixicon-react/BookOpenLineIcon' import DocsIcon from 'remixicon-react/BookOpenLineIcon'
import ExternalLinkIcon from 'remixicon-react/ExternalLinkLineIcon' import ExternalLinkIcon from 'remixicon-react/ExternalLinkLineIcon'
import GithubIcon from 'remixicon-react/GithubFillIcon' import GithubIcon from 'remixicon-react/GithubFillIcon'
import ExchangeDollarLineIcon from 'remixicon-react/ExchangeDollarLineIcon'
import HomeIcon from 'remixicon-react/Home3LineIcon' import HomeIcon from 'remixicon-react/Home3LineIcon'
import SettingsIcon from 'remixicon-react/Settings2LineIcon' import SettingsIcon from 'remixicon-react/Settings2LineIcon'
import AccountIcon from 'remixicon-react/Wallet3LineIcon' import AccountIcon from 'remixicon-react/Wallet3LineIcon'
@@ -95,6 +96,11 @@ export default function SideBar(): ReactElement {
icon: AccountIcon, icon: AccountIcon,
pathMatcherSubstring: '/account/', pathMatcherSubstring: '/account/',
}, },
{
label: 'Redistribution',
path: ROUTES.REDISTRIBUTION,
icon: ExchangeDollarLineIcon,
},
{ {
label: 'Settings', label: 'Settings',
path: ROUTES.SETTINGS, path: ROUTES.SETTINGS,
+7 -11
View File
@@ -1,5 +1,5 @@
import { Box, Tooltip, Typography } from '@material-ui/core'
import { BZZ, DAI } from '@ethersphere/bee-js' import { BZZ, DAI } from '@ethersphere/bee-js'
import { Box, Tooltip, Typography } from '@material-ui/core'
import { Wallet } from 'ethers' import { Wallet } from 'ethers'
import { useSnackbar } from 'notistack' import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect, useState } from 'react' import { ReactElement, useContext, useEffect, useState } from 'react'
@@ -12,9 +12,9 @@ import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import { HistoryHeader } from '../../components/HistoryHeader' import { HistoryHeader } from '../../components/HistoryHeader'
import { Loading } from '../../components/Loading' import { Loading } from '../../components/Loading'
import { SwarmButton } from '../../components/SwarmButton' import { SwarmButton } from '../../components/SwarmButton'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings' import { Context as SettingsContext } from '../../providers/Settings'
import { Context as TopUpContext } from '../../providers/TopUp' import { Context as TopUpContext } from '../../providers/TopUp'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { createGiftWallet } from '../../utils/desktop' import { createGiftWallet } from '../../utils/desktop'
import { ResolvedWallet } from '../../utils/wallet' import { ResolvedWallet } from '../../utils/wallet'
@@ -24,7 +24,7 @@ const GIFT_WALLET_FUND_BZZ_AMOUNT = BZZ.fromDecimalString('0.5')
export default function Index(): ReactElement { export default function Index(): ReactElement {
const { giftWallets, addGiftWallet } = useContext(TopUpContext) const { giftWallets, addGiftWallet } = useContext(TopUpContext)
const { rpcProvider, desktopUrl } = useContext(SettingsContext) const { rpcProvider, desktopUrl } = useContext(SettingsContext)
const { walletBalance } = useContext(BeeContext) const { balance } = useContext(BalanceProvider)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [balances, setBalances] = useState<ResolvedWallet[]>([]) const [balances, setBalances] = useState<ResolvedWallet[]>([])
@@ -67,13 +67,12 @@ export default function Index(): ReactElement {
navigate(-1) navigate(-1)
} }
if (!walletBalance) { if (!balance) {
return <Loading /> return <Loading />
} }
const notEnoughFundsCheck = const notEnoughFundsCheck =
walletBalance.nativeTokenBalance.lte(GIFT_WALLET_FUND_DAI_AMOUNT) || balance.dai.lte(GIFT_WALLET_FUND_DAI_AMOUNT) || balance.bzz.lt(GIFT_WALLET_FUND_BZZ_AMOUNT)
walletBalance.bzzBalance.lt(GIFT_WALLET_FUND_BZZ_AMOUNT)
return ( return (
<> <>
@@ -86,13 +85,10 @@ export default function Index(): ReactElement {
</Typography> </Typography>
</Box> </Box>
<Box mb={0.25}> <Box mb={0.25}>
<ExpandableListItem <ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
label="xDAI balance"
value={`${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`}
/>
</Box> </Box>
<Box mb={2}> <Box mb={2}>
<ExpandableListItem label="xBZZ balance" value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`} /> <ExpandableListItem label="xBZZ balance" value={`${balance.bzz.toSignificantDigits(4)} xBZZ`} />
</Box> </Box>
<Box mb={4}> <Box mb={4}>
{balances.map((x, i) => ( {balances.map((x, i) => (
+4 -1
View File
@@ -13,7 +13,7 @@ import NodeInfoCard from './NodeInfoCard'
import { WalletInfoCard } from './WalletInfoCard' import { WalletInfoCard } from './WalletInfoCard'
export default function Status(): ReactElement { export default function Status(): ReactElement {
const { beeVersion, status, topology, nodeInfo, walletBalance } = useContext(BeeContext) const { beeVersion, status, topology, nodeInfo, nodeStatus, walletBalance } = useContext(BeeContext)
const { isDesktop, desktopUrl } = useContext(SettingsContext) const { isDesktop, desktopUrl } = useContext(SettingsContext)
const { beeDesktopVersion } = useBeeDesktop(isDesktop, desktopUrl) const { beeDesktopVersion } = useBeeDesktop(isDesktop, desktopUrl)
const { newBeeDesktopVersion } = useNewBeeDesktopVersion(isDesktop, desktopUrl, false) const { newBeeDesktopVersion } = useNewBeeDesktopVersion(isDesktop, desktopUrl, false)
@@ -38,7 +38,10 @@ export default function Status(): ReactElement {
<div style={{ height: '2px' }} /> <div style={{ height: '2px' }} />
<ExpandableListItem label="Connected peers" value={topology?.connected ?? '-'} /> <ExpandableListItem label="Connected peers" value={topology?.connected ?? '-'} />
<ExpandableListItem label="Population" value={topology?.population ?? '-'} /> <ExpandableListItem label="Population" value={topology?.population ?? '-'} />
<ExpandableListItem label="Pullsync rate" value={nodeStatus?.pullsyncRate} />
<ExpandableListItem label="Depth" value={topology?.depth ?? '-'} /> <ExpandableListItem label="Depth" value={topology?.depth ?? '-'} />
<ExpandableListItem label="Neighborhood size" value={nodeStatus?.neighborhoodSize} />
<ExpandableListItem label="Node is reachable" value={nodeStatus?.isReachable?.toString()} />
<ChainSync /> <ChainSync />
<div style={{ height: '16px' }} /> <div style={{ height: '16px' }} />
+25
View File
@@ -0,0 +1,25 @@
import CircularProgress from '@material-ui/core/CircularProgress'
import { ReactElement, useContext } from 'react'
import ExpandableList from '../../components/ExpandableList'
import { Redistribution } from '../../components/Redistribution'
import { Context as SettingsContext } from '../../providers/Settings'
export default function RedistributionPage(): ReactElement {
const { isLoading } = useContext(SettingsContext)
if (isLoading) {
return (
<div style={{ textAlign: 'center', width: '100%' }}>
<CircularProgress />
</div>
)
}
return (
<>
<ExpandableList label="Redistribution" defaultOpen>
<Redistribution />
</ExpandableList>
</>
)
}
+8 -6
View File
@@ -1,5 +1,5 @@
import { Box, Grid, Typography } from '@material-ui/core'
import { DAI } from '@ethersphere/bee-js' import { DAI } from '@ethersphere/bee-js'
import { Box, Grid, Typography } from '@material-ui/core'
import { ReactElement, useContext } from 'react' import { ReactElement, useContext } from 'react'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
import Check from 'remixicon-react/CheckLineIcon' import Check from 'remixicon-react/CheckLineIcon'
@@ -10,6 +10,7 @@ import { Loading } from '../../components/Loading'
import { SwarmButton } from '../../components/SwarmButton' import { SwarmButton } from '../../components/SwarmButton'
import { SwarmDivider } from '../../components/SwarmDivider' import { SwarmDivider } from '../../components/SwarmDivider'
import { Context } from '../../providers/Bee' import { Context } from '../../providers/Bee'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { TopUpProgressIndicator } from './TopUpProgressIndicator' import { TopUpProgressIndicator } from './TopUpProgressIndicator'
const MINIMUM_XDAI = DAI.fromDecimalString('0.5') const MINIMUM_XDAI = DAI.fromDecimalString('0.5')
@@ -22,14 +23,15 @@ interface Props {
} }
export default function Index({ header, title, p, next }: Props): ReactElement { export default function Index({ header, title, p, next }: Props): ReactElement {
const { nodeAddresses, walletBalance } = useContext(Context) const { nodeAddresses } = useContext(Context)
const { balance } = useContext(BalanceProvider)
const navigate = useNavigate() const navigate = useNavigate()
if (!walletBalance || !nodeAddresses) { if (!balance || !nodeAddresses) {
return <Loading /> return <Loading />
} }
const disabled = walletBalance.nativeTokenBalance.lt(MINIMUM_XDAI) const disabled = balance.dai.lt(MINIMUM_XDAI)
return ( return (
<> <>
@@ -43,10 +45,10 @@ export default function Index({ header, title, p, next }: Props): ReactElement {
<Box mb={4}>{p}</Box> <Box mb={4}>{p}</Box>
<SwarmDivider mb={4} /> <SwarmDivider mb={4} />
<Box mb={0.25}> <Box mb={0.25}>
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded /> <ExpandableListItemKey label="Funding wallet address" value={balance.address} expanded />
</Box> </Box>
<Box mb={4}> <Box mb={4}>
<ExpandableListItem label="xDAI balance" value={walletBalance.nativeTokenBalance.toSignificantDigits(4)} /> <ExpandableListItem label="xDAI balance" value={balance.dai.toSignificantDigits(4)} />
</Box> </Box>
<Grid container direction="row" justifyContent="space-between"> <Grid container direction="row" justifyContent="space-between">
<SwarmButton iconType={Check} onClick={() => navigate(next)} disabled={disabled}> <SwarmButton iconType={Check} onClick={() => navigate(next)} disabled={disabled}>
+8 -9
View File
@@ -1,5 +1,5 @@
import { Box, Typography } from '@material-ui/core'
import { BeeModes } from '@ethersphere/bee-js' import { BeeModes } from '@ethersphere/bee-js'
import { Box, Typography } from '@material-ui/core'
import { useSnackbar } from 'notistack' import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect, useState } from 'react' import { ReactElement, useContext, useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router' import { useNavigate, useParams } from 'react-router'
@@ -14,14 +14,16 @@ import { SwarmButton } from '../../components/SwarmButton'
import { SwarmDivider } from '../../components/SwarmDivider' import { SwarmDivider } from '../../components/SwarmDivider'
import { Context as BeeContext } from '../../providers/Bee' import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings' import { Context as SettingsContext } from '../../providers/Settings'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { ROUTES } from '../../routes' import { ROUTES } from '../../routes'
import { sleepMs } from '../../utils' import { sleepMs } from '../../utils'
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop' import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
import { ResolvedWallet } from '../../utils/wallet' import { ResolvedWallet } from '../../utils/wallet'
export function GiftCardFund(): ReactElement { export function GiftCardFund(): ReactElement {
const { nodeAddresses, nodeInfo, walletBalance } = useContext(BeeContext) const { nodeAddresses, nodeInfo } = useContext(BeeContext)
const { isDesktop, desktopUrl, rpcProvider, rpcProviderUrl } = useContext(SettingsContext) const { isDesktop, desktopUrl, rpcProvider, rpcProviderUrl } = useContext(SettingsContext)
const { balance } = useContext(BalanceProvider)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [wallet, setWallet] = useState<ResolvedWallet | null>(null) const [wallet, setWallet] = useState<ResolvedWallet | null>(null)
@@ -39,7 +41,7 @@ export function GiftCardFund(): ReactElement {
ResolvedWallet.make(privateKeyString, rpcProvider).then(setWallet) ResolvedWallet.make(privateKeyString, rpcProvider).then(setWallet)
}, [privateKeyString, rpcProvider]) }, [privateKeyString, rpcProvider])
if (!wallet || !walletBalance) { if (!wallet || !balance) {
return <Loading /> return <Loading />
} }
@@ -94,7 +96,7 @@ export function GiftCardFund(): ReactElement {
</Box> </Box>
<SwarmDivider mb={4} /> <SwarmDivider mb={4} />
<Box mb={0.25}> <Box mb={0.25}>
<ExpandableListItemKey label="Gift wallet address" value={wallet.address || 'N/A'} /> <ExpandableListItemKey label="Gift wallet address" value={balance.address || 'N/A'} />
</Box> </Box>
<Box mb={0.25}> <Box mb={0.25}>
<ExpandableListItem label="xDAI balance" value={`${wallet.dai.toSignificantDigits(4)} xDAI`} /> <ExpandableListItem label="xDAI balance" value={`${wallet.dai.toSignificantDigits(4)} xDAI`} />
@@ -113,13 +115,10 @@ export function GiftCardFund(): ReactElement {
/> />
</Box> </Box>
<Box mb={0.25}> <Box mb={0.25}>
<ExpandableListItem <ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
label="xDAI balance"
value={`${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`}
/>
</Box> </Box>
<Box mb={2}> <Box mb={2}>
<ExpandableListItem label="xBZZ balance" value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`} /> <ExpandableListItem label="xBZZ balance" value={`${balance.bzz.toSignificantDigits(4)} xBZZ`} />
</Box> </Box>
<SwarmButton iconType={Check} onClick={onFund} disabled={loading} loading={loading}> <SwarmButton iconType={Check} onClick={onFund} disabled={loading} loading={loading}>
{canUpgradeToLightNode ? 'Send all funds to your node and Upgrade' : 'Send all funds to your node'} {canUpgradeToLightNode ? 'Send all funds to your node and Upgrade' : 'Send all funds to your node'}
+16 -14
View File
@@ -1,5 +1,5 @@
import { Box, Typography } from '@material-ui/core'
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js' import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
import { Box, Typography } from '@material-ui/core'
import { useSnackbar } from 'notistack' import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect, useState } from 'react' import { ReactElement, useContext, useEffect, useState } from 'react'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
@@ -15,6 +15,7 @@ import { SwarmDivider } from '../../components/SwarmDivider'
import { SwarmTextInput } from '../../components/SwarmTextInput' import { SwarmTextInput } from '../../components/SwarmTextInput'
import { Context as BeeContext } from '../../providers/Bee' import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings' import { Context as SettingsContext } from '../../providers/Settings'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { ROUTES } from '../../routes' import { ROUTES } from '../../routes'
import { sleepMs } from '../../utils' import { sleepMs } from '../../utils'
import { isSwapError, SwapError, wrapWithSwapError } from '../../utils/SwapError' import { isSwapError, SwapError, wrapWithSwapError } from '../../utils/SwapError'
@@ -48,7 +49,8 @@ export function Swap({ header }: Props): ReactElement {
const [daiAfterSwap, setDaiAfterSwap] = useState<DAI | null>(null) const [daiAfterSwap, setDaiAfterSwap] = useState<DAI | null>(null)
const { rpcProviderUrl, isDesktop, desktopUrl } = useContext(SettingsContext) const { rpcProviderUrl, isDesktop, desktopUrl } = useContext(SettingsContext)
const { nodeAddresses, nodeInfo, walletBalance } = useContext(BeeContext) const { nodeAddresses, nodeInfo } = useContext(BeeContext)
const { balance } = useContext(BalanceProvider)
const navigate = useNavigate() const navigate = useNavigate()
const { enqueueSnackbar } = useSnackbar() const { enqueueSnackbar } = useSnackbar()
@@ -61,20 +63,20 @@ export function Swap({ header }: Props): ReactElement {
// Set the initial xDAI to swap // Set the initial xDAI to swap
useEffect(() => { useEffect(() => {
if (!walletBalance || userInputSwap) { if (!balance || userInputSwap) {
return return
} }
const minimumOptimalValue = DAI.fromDecimalString('1').plus(MINIMUM_XDAI) const minimumOptimalValue = DAI.fromDecimalString('1').plus(MINIMUM_XDAI)
if (walletBalance.nativeTokenBalance.gte(minimumOptimalValue)) { if (balance.dai.gte(minimumOptimalValue)) {
// Balance has at least 1 + MINIMUM_XDAI xDai // Balance has at least 1 + MINIMUM_XDAI xDai
setDaiToSwap(walletBalance.nativeTokenBalance.minus(DAI.fromDecimalString('1'))) setDaiToSwap(balance.dai.minus(DAI.fromDecimalString('1')))
} else { } else {
// Balance is low, halve the amount // Balance is low, halve the amount
setDaiToSwap(walletBalance.nativeTokenBalance.divide(BigInt(2))) setDaiToSwap(balance.dai.divide(BigInt(2)))
} }
}, [walletBalance, userInputSwap]) }, [balance, userInputSwap])
// Set the xDAI to swap based on user input // Set the xDAI to swap based on user input
useEffect(() => { useEffect(() => {
@@ -95,13 +97,13 @@ export function Swap({ header }: Props): ReactElement {
// Calculate the amount of tokens after swap // Calculate the amount of tokens after swap
useEffect(() => { useEffect(() => {
if (!walletBalance || !daiToSwap || error) { if (!balance || !daiToSwap || error) {
return return
} }
const daiAfterSwap = walletBalance.nativeTokenBalance.minus(daiToSwap) const daiAfterSwap = balance.dai.minus(daiToSwap)
setDaiAfterSwap(daiAfterSwap) setDaiAfterSwap(daiAfterSwap)
const tokensConverted = daiToSwap.exchangeToBZZ(price) const tokensConverted = daiToSwap.exchangeToBZZ(price)
const bzzAfterSwap = tokensConverted.plus(walletBalance.bzzBalance) const bzzAfterSwap = tokensConverted.plus(balance.bzz)
setBzzAfterSwap(bzzAfterSwap) setBzzAfterSwap(bzzAfterSwap)
if (daiAfterSwap.lt(MINIMUM_XDAI)) { if (daiAfterSwap.lt(MINIMUM_XDAI)) {
@@ -109,9 +111,9 @@ export function Swap({ header }: Props): ReactElement {
} else if (bzzAfterSwap.lt(MINIMUM_XBZZ)) { } else if (bzzAfterSwap.lt(MINIMUM_XBZZ)) {
setError(`Must have at least ${MINIMUM_XBZZ.toSignificantDigits(4)} xBZZ after swap!`) setError(`Must have at least ${MINIMUM_XBZZ.toSignificantDigits(4)} xBZZ after swap!`)
} }
}, [error, walletBalance, daiToSwap, price]) }, [error, balance, daiToSwap, price])
if (!walletBalance || !nodeAddresses || !daiToSwap || !bzzAfterSwap || !daiAfterSwap) { if (!balance || !nodeAddresses || !daiToSwap || !bzzAfterSwap || !daiAfterSwap) {
return <Loading /> return <Loading />
} }
@@ -219,8 +221,8 @@ export function Swap({ header }: Props): ReactElement {
<SwarmDivider mb={4} /> <SwarmDivider mb={4} />
<Box mb={4}> <Box mb={4}>
<Typography> <Typography>
Your current balance is {walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI and{' '} Your current balance is {balance.dai.toSignificantDigits(4)} xDAI and {balance.bzz.toSignificantDigits(4)}{' '}
{walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ. xBZZ.
</Typography> </Typography>
</Box> </Box>
<Box mb={4}> <Box mb={4}>
+7 -5
View File
@@ -1,5 +1,5 @@
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js' import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
import { useSnackbar } from 'notistack' import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react' import { ReactElement, useContext, useState } from 'react'
import { useNavigate } from 'react-router' import { useNavigate } from 'react-router'
@@ -15,6 +15,7 @@ import { SwarmButton } from '../../components/SwarmButton'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard' import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext, CheckState } from '../../providers/Bee' import { Context as BeeContext, CheckState } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings' import { Context as SettingsContext } from '../../providers/Settings'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { ROUTES } from '../../routes' import { ROUTES } from '../../routes'
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop' import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
@@ -39,7 +40,8 @@ export default function TopUp(): ReactElement {
const navigate = useNavigate() const navigate = useNavigate()
const styles = useStyles() const styles = useStyles()
const { isDesktop, desktopUrl } = useContext(SettingsContext) const { isDesktop, desktopUrl } = useContext(SettingsContext)
const { nodeInfo, status, walletBalance } = useContext(BeeContext) const { nodeInfo, status } = useContext(BeeContext)
const { balance } = useContext(BalanceProvider)
const { rpcProviderUrl } = useContext(SettingsContext) const { rpcProviderUrl } = useContext(SettingsContext)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const { enqueueSnackbar } = useSnackbar() const { enqueueSnackbar } = useSnackbar()
@@ -47,8 +49,8 @@ export default function TopUp(): ReactElement {
const canUpgradeToLightNode = const canUpgradeToLightNode =
isDesktop && isDesktop &&
nodeInfo?.beeMode === BeeModes.ULTRA_LIGHT && nodeInfo?.beeMode === BeeModes.ULTRA_LIGHT &&
walletBalance?.nativeTokenBalance.gte(MINIMUM_XDAI) && balance?.dai.gte(MINIMUM_XDAI) &&
walletBalance?.bzzBalance.gte(MINIMUM_XBZZ) balance?.bzz.gte(MINIMUM_XBZZ)
async function restart() { async function restart() {
setLoading(true) setLoading(true)
@@ -65,7 +67,7 @@ export default function TopUp(): ReactElement {
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard /> if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
if (!walletBalance) { if (!balance) {
return <Loading /> return <Loading />
} }
+22
View File
@@ -5,11 +5,13 @@ import {
ChainState, ChainState,
ChequebookAddressResponse, ChequebookAddressResponse,
ChequebookBalanceResponse, ChequebookBalanceResponse,
DebugStatus,
LastChequesResponse, LastChequesResponse,
NodeAddresses, NodeAddresses,
NodeInfo, NodeInfo,
Peer, Peer,
PeerBalance, PeerBalance,
RedistributionState,
Topology, Topology,
WalletBalance, WalletBalance,
} from '@ethersphere/bee-js' } from '@ethersphere/bee-js'
@@ -49,6 +51,7 @@ interface ContextInterface {
apiHealth: boolean apiHealth: boolean
nodeAddresses: NodeAddresses | null nodeAddresses: NodeAddresses | null
nodeInfo: NodeInfo | null nodeInfo: NodeInfo | null
nodeStatus: DebugStatus | null
topology: Topology | null topology: Topology | null
chequebookAddress: ChequebookAddressResponse | null chequebookAddress: ChequebookAddressResponse | null
peers: Peer[] | null peers: Peer[] | null
@@ -59,6 +62,7 @@ interface ContextInterface {
settlements: AllSettlements | null settlements: AllSettlements | null
chainState: ChainState | null chainState: ChainState | null
walletBalance: WalletBalance | null walletBalance: WalletBalance | null
redistributionState: RedistributionState | null
latestBeeRelease: LatestBeeRelease | null latestBeeRelease: LatestBeeRelease | null
isLoading: boolean isLoading: boolean
lastUpdate: number | null lastUpdate: number | null
@@ -79,6 +83,7 @@ const initialValues: ContextInterface = {
apiHealth: false, apiHealth: false,
nodeAddresses: null, nodeAddresses: null,
nodeInfo: null, nodeInfo: null,
nodeStatus: null,
topology: null, topology: null,
chequebookAddress: null, chequebookAddress: null,
stake: null, stake: null,
@@ -89,6 +94,7 @@ const initialValues: ContextInterface = {
settlements: null, settlements: null,
chainState: null, chainState: null,
walletBalance: null, walletBalance: null,
redistributionState: null,
latestBeeRelease: null, latestBeeRelease: null,
isLoading: true, isLoading: true,
lastUpdate: null, lastUpdate: null,
@@ -172,6 +178,7 @@ export function Provider({ children }: Props): ReactElement {
const [apiHealth, setApiHealth] = useState<boolean>(false) const [apiHealth, setApiHealth] = useState<boolean>(false)
const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null) const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null)
const [nodeInfo, setNodeInfo] = useState<NodeInfo | null>(null) const [nodeInfo, setNodeInfo] = useState<NodeInfo | null>(null)
const [nodeStatus, setNodeStatus] = useState<DebugStatus | null>(null)
const [topology, setNodeTopology] = useState<Topology | null>(null) const [topology, setNodeTopology] = useState<Topology | null>(null)
const [chequebookAddress, setChequebookAddress] = useState<ChequebookAddressResponse | null>(null) const [chequebookAddress, setChequebookAddress] = useState<ChequebookAddressResponse | null>(null)
const [peers, setPeers] = useState<Peer[] | null>(null) const [peers, setPeers] = useState<Peer[] | null>(null)
@@ -182,6 +189,7 @@ export function Provider({ children }: Props): ReactElement {
const [settlements, setSettlements] = useState<AllSettlements | null>(null) const [settlements, setSettlements] = useState<AllSettlements | null>(null)
const [chainState, setChainState] = useState<ChainState | null>(null) const [chainState, setChainState] = useState<ChainState | null>(null)
const [walletBalance, setWalletBalance] = useState<WalletBalance | null>(null) const [walletBalance, setWalletBalance] = useState<WalletBalance | null>(null)
const [redistributionState, setRedistributionState] = useState<RedistributionState | null>(null)
const [startedAt] = useState(Date.now()) const [startedAt] = useState(Date.now())
const { latestBeeRelease } = useLatestBeeRelease() const { latestBeeRelease } = useLatestBeeRelease()
@@ -257,6 +265,12 @@ export function Provider({ children }: Props): ReactElement {
.then(setNodeInfo) .then(setNodeInfo)
.catch(() => setNodeInfo(null)), .catch(() => setNodeInfo(null)),
// NodeDebugInfo
beeApi
.getStatus({ timeout: TIMEOUT })
.then(setNodeStatus)
.catch(() => setNodeInfo(null)),
// Network Topology // Network Topology
beeApi beeApi
.getTopology({ timeout: TIMEOUT }) .getTopology({ timeout: TIMEOUT })
@@ -304,6 +318,12 @@ export function Provider({ children }: Props): ReactElement {
.then(stake => setStake(stake)) .then(stake => setStake(stake))
.catch(() => setStake(null)), .catch(() => setStake(null)),
// Redistribution stats
beeApi
.getRedistributionState({ timeout: TIMEOUT })
.then(setRedistributionState)
.catch(() => setRedistributionState(null)),
// Peer balances // Peer balances
beeApi beeApi
.getAllBalances({ timeout: TIMEOUT }) .getAllBalances({ timeout: TIMEOUT })
@@ -362,6 +382,7 @@ export function Provider({ children }: Props): ReactElement {
apiHealth, apiHealth,
nodeAddresses, nodeAddresses,
nodeInfo, nodeInfo,
nodeStatus,
topology, topology,
chequebookAddress, chequebookAddress,
peers, peers,
@@ -372,6 +393,7 @@ export function Provider({ children }: Props): ReactElement {
settlements, settlements,
chainState, chainState,
walletBalance, walletBalance,
redistributionState,
latestBeeRelease, latestBeeRelease,
isLoading, isLoading,
lastUpdate, lastUpdate,
+88
View File
@@ -0,0 +1,88 @@
import { createContext, ReactChild, ReactElement, useContext, useEffect, useState } from 'react'
import { WalletAddress } from '../utils/wallet'
import { Context as BeeContext } from './Bee'
import { Context as SettingsContext } from './Settings'
interface ContextInterface {
balance: WalletAddress | null
error: Error | null
isLoading: boolean
lastUpdate: number | null
start: (frequency?: number) => void
stop: () => void
refresh: () => Promise<void>
}
const initialValues: ContextInterface = {
balance: null,
error: null,
isLoading: false,
lastUpdate: null,
start: () => {}, // eslint-disable-line
stop: () => {}, // eslint-disable-line
refresh: () => Promise.reject(),
}
export const Context = createContext<ContextInterface>(initialValues)
export const Consumer = Context.Consumer
interface Props {
children: ReactChild
}
export function Provider({ children }: Props): ReactElement {
const { rpcProvider } = useContext(SettingsContext)
const { nodeAddresses } = useContext(BeeContext)
const [balance, setBalance] = useState<WalletAddress | null>(initialValues.balance)
const [error, setError] = useState<Error | null>(initialValues.error)
const [isLoading, setIsLoading] = useState<boolean>(initialValues.isLoading)
const [lastUpdate, setLastUpdate] = useState<number | null>(initialValues.lastUpdate)
const [frequency, setFrequency] = useState<number | null>(null)
useEffect(() => {
if (nodeAddresses?.ethereum && rpcProvider) {
WalletAddress.make(nodeAddresses.ethereum.toHex(), rpcProvider).then(setBalance)
} else {
setBalance(null)
}
}, [nodeAddresses, rpcProvider])
const refresh = async () => {
// Don't want to refresh when already refreshing
if (isLoading) return
if (!balance) return
try {
setIsLoading(true)
setBalance(await balance.refresh())
setLastUpdate(Date.now())
} catch (e) {
setError(e as Error)
} finally {
setIsLoading(false)
}
}
const start = (freq = 30000) => setFrequency(freq)
const stop = () => setFrequency(null)
// Start the update loop
useEffect(() => {
refresh()
// Start autorefresh only if the frequency is set
if (frequency) {
const interval = setInterval(refresh, frequency)
return () => clearInterval(interval)
}
}, [frequency]) // eslint-disable-line react-hooks/exhaustive-deps
return (
<Context.Provider value={{ balance, error, isLoading, lastUpdate, start, stop, refresh }}>
{children}
</Context.Provider>
)
}
+3
View File
@@ -25,6 +25,7 @@ import { BankCardTopUpIndex } from './pages/top-up/BankCardTopUpIndex'
import { CryptoTopUpIndex } from './pages/top-up/CryptoTopUpIndex' import { CryptoTopUpIndex } from './pages/top-up/CryptoTopUpIndex'
import { GiftCardFund } from './pages/top-up/GiftCardFund' import { GiftCardFund } from './pages/top-up/GiftCardFund'
import { GiftCardTopUpIndex } from './pages/top-up/GiftCardTopUpIndex' import { GiftCardTopUpIndex } from './pages/top-up/GiftCardTopUpIndex'
import RedistributionPage from './pages/redistribution'
import { Swap } from './pages/top-up/Swap' import { Swap } from './pages/top-up/Swap'
import { Context as SettingsContext } from './providers/Settings' import { Context as SettingsContext } from './providers/Settings'
import { FileManagerPage } from './pages/filemanager' import { FileManagerPage } from './pages/filemanager'
@@ -37,6 +38,7 @@ export enum ROUTES {
UPLOAD_IN_PROGRESS = '/files/upload/workflow', UPLOAD_IN_PROGRESS = '/files/upload/workflow',
DOWNLOAD = '/files/download', DOWNLOAD = '/files/download',
HASH = '/files/hash/:hash', HASH = '/files/hash/:hash',
REDISTRIBUTION = '/redistribution',
SETTINGS = '/settings', SETTINGS = '/settings',
STATUS = '/status', STATUS = '/status',
TOP_UP = '/account/wallet/top-up', TOP_UP = '/account/wallet/top-up',
@@ -82,6 +84,7 @@ const BaseRouter = (): ReactElement => {
<Route path={ROUTES.SETTINGS} element={<Settings />} /> <Route path={ROUTES.SETTINGS} element={<Settings />} />
<Route path={ROUTES.STATUS} element={<Status />} /> <Route path={ROUTES.STATUS} element={<Status />} />
<Route path={ROUTES.INFO} element={<Info />} /> <Route path={ROUTES.INFO} element={<Info />} />
<Route path={ROUTES.REDISTRIBUTION} element={<RedistributionPage />} />
<Route path={ROUTES.TOP_UP} element={<TopUp />} /> <Route path={ROUTES.TOP_UP} element={<TopUp />} />
<Route path={ROUTES.TOP_UP_CRYPTO} element={<CryptoTopUpIndex />} /> <Route path={ROUTES.TOP_UP_CRYPTO} element={<CryptoTopUpIndex />} />
<Route path={ROUTES.TOP_UP_CRYPTO_SWAP} element={<Swap header="Top-up with cryptocurrencies" />} /> <Route path={ROUTES.TOP_UP_CRYPTO_SWAP} element={<Swap header="Top-up with cryptocurrencies" />} />
+23
View File
@@ -2,6 +2,29 @@ import { BZZ, DAI, EthAddress } from '@ethersphere/bee-js'
import { providers, Wallet } from 'ethers' import { providers, Wallet } from 'ethers'
import { estimateNativeTransferTransactionCost, Rpc } from './rpc' import { estimateNativeTransferTransactionCost, Rpc } from './rpc'
export class WalletAddress {
private constructor(
public address: string,
public bzz: BZZ,
public dai: DAI,
public provider: providers.JsonRpcProvider,
) {}
static async make(address: string, provider: providers.JsonRpcProvider): Promise<WalletAddress> {
const bzz = await Rpc._eth_getBalanceERC20(address, provider)
const dai = await Rpc._eth_getBalance(address, provider)
return new WalletAddress(address, bzz, dai, provider)
}
public async refresh(): Promise<WalletAddress> {
this.bzz = await Rpc._eth_getBalanceERC20(this.address, this.provider)
this.dai = await Rpc._eth_getBalance(this.address, this.provider)
return this
}
}
export class ResolvedWallet { export class ResolvedWallet {
public address: string public address: string
public privateKey: string public privateKey: string