feat: bee-js revamp (#690)

* chore: initial commit

* refactor: remove unnecessary wrappers

* style: add missing newline

* chore: bump bee-js

* chore: ignore any cast in fdp

* fix: remove cid import

* fix: make TextEncoder and TextDecoder available in jest

* refactor: dedupe stamp ttl second conversion

* refactor: use convenience methods from bee-js

* feat: update to bee-js for restored ens support

* fix: bump bee-js to get download fix

* fix: resolve feed before downloading reference

* fix: fix token displays

* fix: fix token input modal error message

* refactor: remove wallet balance provider

* chore: remove dead code

* refactor: upcoming bee js 0.15.0 (#692)

* chore: initial commit

* fix: do not break page when duration seconds is 0

* ci: remove cache

* chore: upgrade bee-js

* feat: bee-js and bee v2.6 compatibility

* chore: switch upcoming/bee-js to ethersphere/bee-js
This commit is contained in:
Cafe137
2025-07-16 17:10:14 +02:00
committed by GitHub
parent 082a8f52ef
commit 1249c0df71
62 changed files with 675 additions and 16303 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
import { BeeModes } from '@ethersphere/bee-js'
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
import { BeeModes } from '@ethersphere/bee-js'
import { ReactElement, useContext } from 'react'
import { useNavigate } from 'react-router-dom'
import { Context } from '../../providers/Bee'
@@ -1,4 +1,3 @@
import { Utils } from '@ethersphere/bee-js'
import { Box } from '@material-ui/core'
import { ReactElement, useContext } from 'react'
import ExpandableList from '../../../components/ExpandableList'
@@ -35,20 +34,20 @@ export function AccountChequebook(): ReactElement {
<ExpandableList label="Chequebook" defaultOpen>
<ExpandableListItem
label="Total Balance"
value={`${chequebookBalance?.totalBalance.toFixedDecimal()} xBZZ`}
value={`${chequebookBalance?.totalBalance.toSignificantDigits(4)} xBZZ`}
/>
<ExpandableListItem
label="Available Uncommitted Balance"
value={`${chequebookBalance?.availableBalance.toFixedDecimal()} xBZZ`}
value={`${chequebookBalance?.availableBalance.toSignificantDigits(4)} xBZZ`}
/>
<ExpandableListItem
label="Total Cheques Amount Sent"
value={`${settlements?.totalSent.toFixedDecimal()} xBZZ`}
value={`${settlements?.totalSent.toSignificantDigits(4)} xBZZ`}
/>
<Box mb={2}>
<ExpandableListItem
label="Total Cheques Amount Received"
value={`${settlements?.totalReceived.toFixedDecimal()} xBZZ`}
value={`${settlements?.totalReceived.toSignificantDigits(4)} xBZZ`}
/>
</Box>
<ExpandableListItemActions>
@@ -60,7 +59,7 @@ export function AccountChequebook(): ReactElement {
<ExpandableList label="Blockchain" defaultOpen>
<ExpandableListItemKey
label="Ethereum address"
value={nodeAddresses?.ethereum ? Utils.capitalizeAddressERC55(nodeAddresses.ethereum) : ''}
value={nodeAddresses?.ethereum ? nodeAddresses.ethereum.toChecksum() : ''}
/>
<ExpandableListItemKey
label="Chequebook contract address"
+8 -7
View File
@@ -1,16 +1,19 @@
import { Box } from '@material-ui/core'
import { NULL_TOPIC } from '@ethersphere/bee-js'
import { ReactElement, useContext, useState } from 'react'
import Download from 'remixicon-react/Download2LineIcon'
import Info from 'remixicon-react/InformationLineIcon'
import { useNavigate } from 'react-router'
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
import Trash from 'remixicon-react/DeleteBin7LineIcon'
import { useNavigate } from 'react-router'
import Download from 'remixicon-react/Download2LineIcon'
import Info from 'remixicon-react/InformationLineIcon'
import ExpandableList from '../../../components/ExpandableList'
import ExpandableListItem from '../../../components/ExpandableListItem'
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
import { SwarmButton } from '../../../components/SwarmButton'
import { Context as IdentityContext, Identity } from '../../../providers/Feeds'
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
import { Identity, Context as IdentityContext } from '../../../providers/Feeds'
import { ROUTES } from '../../../routes'
import { formatEnum } from '../../../utils'
import { persistIdentitiesWithoutUpdate } from '../../../utils/identity'
@@ -19,8 +22,6 @@ import { ExportFeedDialog } from '../../feeds/ExportFeedDialog'
import { ImportFeedDialog } from '../../feeds/ImportFeedDialog'
import { AccountNavigation } from '../AccountNavigation'
import { Header } from '../Header'
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
export function AccountFeeds(): ReactElement {
const { identities, setIdentities } = useContext(IdentityContext)
@@ -98,7 +99,7 @@ export function AccountFeeds(): ReactElement {
<ExpandableListItem label="Identity type" value={formatEnum(x.type)} />
</ExpandableList>
</Box>
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
<Box mt={0.75}>
<ExpandableListItemActions>
+6 -8
View File
@@ -5,16 +5,14 @@ import ExpandableListItemActions from '../../../components/ExpandableListItemAct
import { Loading } from '../../../components/Loading'
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
import StakeModal from '../../../containers/StakeModal'
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
import { Context as BalanceContext } from '../../../providers/WalletBalance'
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
import { AccountNavigation } from '../AccountNavigation'
import { Header } from '../Header'
export function AccountStaking(): ReactElement {
const [loading, setLoading] = useState(false)
const { status, stake } = useContext(BeeContext)
const { balance } = useContext(BalanceContext)
const { status, stake, walletBalance } = useContext(BeeContext)
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
@@ -31,15 +29,15 @@ export function AccountStaking(): ReactElement {
<Header />
<AccountNavigation active="STAKING" />
<div>
{loading || stake?.toDecimal === undefined ? (
{loading || !stake ? (
<Loading />
) : (
<ExpandableList label="Staking" defaultOpen>
<ExpandableListItem label="Staked BZZ" value={`${stake?.toSignificantDigits()} xBZZ`} />
{balance?.bzz ? (
<ExpandableListItem label="Staked BZZ" value={`${stake?.toSignificantDigits(4)} xBZZ`} />
{walletBalance?.bzzBalance ? (
<ExpandableListItem
label="Available xBZZ balance"
value={`${balance?.bzz.toSignificantDigits(4)} xBZZ`}
value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`}
/>
) : null}
<ExpandableListItemActions>
+12 -12
View File
@@ -1,5 +1,5 @@
import { BeeModes, Utils } from '@ethersphere/bee-js'
import { Box, Grid, Typography } from '@material-ui/core'
import { BeeModes } from '@ethersphere/bee-js'
import { ReactElement, useContext } from 'react'
import { useNavigate } from 'react-router'
import Download from 'remixicon-react/DownloadLineIcon'
@@ -13,15 +13,13 @@ import { SwarmButton } from '../../../components/SwarmButton'
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
import { Context as SettingsContext } from '../../../providers/Settings'
import { Context as BalanceProvider } from '../../../providers/WalletBalance'
import { ROUTES } from '../../../routes'
import { AccountNavigation } from '../AccountNavigation'
import { Header } from '../Header'
export function AccountWallet(): ReactElement {
const { nodeAddresses, nodeInfo, status } = useContext(BeeContext)
const { nodeAddresses, nodeInfo, status, walletBalance } = useContext(BeeContext)
const { isDesktop } = useContext(SettingsContext)
const { balance } = useContext(BalanceProvider)
const navigate = useNavigate()
@@ -53,20 +51,22 @@ export function AccountWallet(): ReactElement {
)}
</Grid>
</Box>
{balance && nodeAddresses ? (
{walletBalance && nodeAddresses ? (
<>
<Box mb={0.25}>
<ExpandableListItemKey
label="Node wallet address"
value={Utils.capitalizeAddressERC55(nodeAddresses.ethereum)}
expanded
/>
<ExpandableListItemKey label="Node wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded />
</Box>
<Box mb={0.25}>
<ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
<ExpandableListItem
label="xDAI balance"
value={`${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`}
/>
</Box>
<Box mb={2}>
<ExpandableListItem label="xBZZ balance" value={`${balance.bzz.toSignificantDigits(4)} xBZZ`} />
<ExpandableListItem
label="xBZZ balance"
value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`}
/>
</Box>
</>
) : (
+12 -12
View File
@@ -1,3 +1,4 @@
import { BZZ } from '@ethersphere/bee-js'
import type { ReactElement } from 'react'
import CashoutModal from '../../components/CashoutModal'
import ExpandableList from '../../components/ExpandableList'
@@ -5,44 +6,43 @@ import ExpandableListItem from '../../components/ExpandableListItem'
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import { Accounting } from '../../hooks/accounting'
import type { Token } from '../../models/Token'
interface Props {
isLoadingUncashed: boolean
totalUncashed: Token
totalUncashed: BZZ
accounting: Accounting[] | null
}
export default function PeerBalances({ accounting, isLoadingUncashed, totalUncashed }: Props): ReactElement | null {
const uncashedPeers = accounting?.filter(({ uncashedAmount }) => uncashedAmount.toBigNumber.isGreaterThan('0')) || []
const uncashedPeers = accounting?.filter(({ uncashedAmount }) => uncashedAmount.gt(BZZ.fromPLUR('0'))) || []
return (
<ExpandableList
label={`Peers (${uncashedPeers.length})`}
info={`${totalUncashed.toFixedDecimal()} xBZZ (uncashed)`}
info={`${totalUncashed.toSignificantDigits(4)} xBZZ (uncashed)`}
>
<ExpandableListItem label="Uncashed Amount Total" value={`${totalUncashed.toFixedDecimal()} xBZZ`} />
<ExpandableListItem label="Uncashed Amount Total" value={`${totalUncashed.toSignificantDigits(4)} xBZZ`} />
{uncashedPeers.map(({ peer, balance, received, sent, uncashedAmount, total }) => (
<ExpandableList
key={peer}
label={`Peer ${peer.slice(0, 8)}[…]`}
level={1}
info={`${uncashedAmount.toFixedDecimal()} xBZZ (uncashed)`}
info={`${uncashedAmount.toSignificantDigits(4)} xBZZ (uncashed)`}
>
<ExpandableListItemKey label="Peer ID" value={peer} />
<ExpandableListItem label="Outstanding Balance" value={`${balance.toFixedDecimal()} xBZZ`} />
<ExpandableListItem label="Outstanding Balance" value={`${balance.toSignificantDigits(4)} xBZZ`} />
<ExpandableListItem
label="Settlements Sent / Received"
value={`-${sent.toFixedDecimal()} / ${received.toFixedDecimal()} xBZZ`}
value={`-${sent.toSignificantDigits(4)} / ${received.toSignificantDigits(4)} xBZZ`}
/>
<ExpandableListItem label="Total" value={`${total.toFixedDecimal()} xBZZ`} />
<ExpandableListItem label="Total" value={`${total.toSignificantDigits(4)} xBZZ`} />
<ExpandableListItem
label="Uncashed Amount"
value={isLoadingUncashed ? 'loading…' : `${uncashedAmount.toFixedDecimal()} xBZZ`}
value={isLoadingUncashed ? 'loading…' : `${uncashedAmount.toSignificantDigits(4)} xBZZ`}
/>
{uncashedAmount.toBigNumber.isGreaterThan('0') && (
{uncashedAmount.gt(BZZ.fromPLUR('0')) && (
<ExpandableListItemActions>
<CashoutModal uncashedAmount={uncashedAmount.toFixedDecimal()} peerId={peer} />
<CashoutModal uncashedAmount={uncashedAmount.toSignificantDigits(4)} peerId={peer} />
</ExpandableListItemActions>
)}
</ExpandableList>
+13 -7
View File
@@ -1,14 +1,13 @@
import { Bee } from '@ethersphere/bee-js'
import { FdpStorage } from '@fairdatasociety/fdp-storage'
import { Pod } from '@fairdatasociety/fdp-storage/dist/pod/types'
import { CircularProgress, Typography } from '@material-ui/core'
import { Bee, MantarayNode } from '@ethersphere/bee-js'
import { useSnackbar } from 'notistack'
import { ReactElement, useEffect, useState } from 'react'
import ImportIcon from 'remixicon-react/AddBoxLineIcon'
import PlusCircle from 'remixicon-react/AddCircleLineIcon'
import { SwarmButton } from '../../components/SwarmButton'
import { joinUrl } from '../../react-fs/Utility'
import { ManifestJs } from '../../utils/manifest'
import { FdpLogin } from './FdpLogin'
import { FdpPods } from './FdpPods'
import { Horizontal } from './Horizontal'
@@ -25,7 +24,9 @@ async function makeFdp(): Promise<FdpStorage | null> {
return null
}
return new FdpStorage('http://localhost:1633', highestCapacityBatch.batchID, {
// TODO: FDS has bad types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return new FdpStorage('http://localhost:1633', highestCapacityBatch.batchID.toHex() as any, {
ensOptions: {
rpcUrl: sepolia,
contractAddresses: {
@@ -120,11 +121,16 @@ export default function FDP(): ReactElement {
}
setCreatingPod(true)
const bee = new Bee('http://localhost:1633')
const manifestJs = new ManifestJs(bee)
const entries = await manifestJs.getHashes(importHash)
const manifest = await MantarayNode.unmarshal(bee, importHash)
await manifest.loadRecursively(bee)
const nodes = manifest.collect()
await fdp.personalStorage.create(name)
for (const [path, hash] of Object.entries(entries)) {
await fdp.file.uploadData(name, joinUrl('/', path), await bee.downloadData(hash))
for (const node of nodes) {
await fdp.file.uploadData(
name,
joinUrl('/', node.fullPathString),
(await bee.downloadData(node.targetAddress)).toUint8Array(),
)
}
const pods = await fdp.personalStorage.list()
setPods(pods.pods)
+2 -1
View File
@@ -1,4 +1,5 @@
import { Box, Grid, Typography } from '@material-ui/core'
import { NULL_TOPIC } from '@ethersphere/bee-js'
import { Form, Formik } from 'formik'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
@@ -108,7 +109,7 @@ export default function CreateNewFeed(): ReactElement {
</Box>
{values.type === 'V3' && <SwarmTextInput name="password" label="Password" password formik />}
<Box mt={2}>
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
</Box>
<Box mt={2} sx={{ bgcolor: '#fcf2e8' }} p={2}>
<Grid container justifyContent="space-between">
+1 -1
View File
@@ -122,7 +122,7 @@ export default function UpdateFeed(): ReactElement {
<Grid container>
{stamps ? (
<SwarmSelect
options={stamps.map(x => ({ value: x.batchID, label: x.batchID.slice(0, 8) }))}
options={stamps.map(x => ({ value: x.batchID.toHex(), label: x.batchID.toHex().slice(0, 8) }))}
onChange={onStampChange}
label="Stamp"
/>
+8 -7
View File
@@ -1,18 +1,19 @@
import { Box, Typography } from '@material-ui/core'
import { NULL_TOPIC } from '@ethersphere/bee-js'
import { ReactElement, useContext, useState } from 'react'
import Download from 'remixicon-react/DownloadLineIcon'
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
import Info from 'remixicon-react/InformationLineIcon'
import Trash from 'remixicon-react/DeleteBin7LineIcon'
import { useNavigate } from 'react-router'
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
import Trash from 'remixicon-react/DeleteBin7LineIcon'
import Download from 'remixicon-react/DownloadLineIcon'
import Info from 'remixicon-react/InformationLineIcon'
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItem from '../../components/ExpandableListItem'
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import { SwarmButton } from '../../components/SwarmButton'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { CheckState, Context as BeeContext } from '../../providers/Bee'
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
import { Context as BeeContext, CheckState } from '../../providers/Bee'
import { Identity, Context as IdentityContext } from '../../providers/Feeds'
import { ROUTES } from '../../routes'
import { formatEnum } from '../../utils'
import { persistIdentitiesWithoutUpdate } from '../../utils/identity'
@@ -97,7 +98,7 @@ export default function Feeds(): ReactElement {
<ExpandableListItem label="Identity type" value={formatEnum(x.type)} />
</ExpandableList>
</Box>
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
<Box mt={0.75}>
<ExpandableListItemActions>
+2 -2
View File
@@ -1,5 +1,5 @@
import { Utils } from '@ethersphere/bee-js'
import { Box } from '@material-ui/core'
import { Reference } from '@ethersphere/bee-js'
import { ReactElement } from 'react'
import { DocumentationText } from '../../components/DocumentationText'
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
@@ -11,7 +11,7 @@ interface Props {
}
export function AssetSummary({ isWebsite, reference }: Props): ReactElement {
const isHash = Utils.isHexString(reference) && reference.length === 64
const isHash = Reference.isValid(reference)
return (
<>
+2 -2
View File
@@ -1,9 +1,9 @@
import { Context as SettingsContext } from '../../providers/Settings'
import { Box } from '@material-ui/core'
import { Tag } from '@ethersphere/bee-js'
import { ReactElement, useContext, useEffect, useRef, useState } from 'react'
import { DocumentationText } from '../../components/DocumentationText'
import { LinearProgressWithLabel } from '../../components/ProgressBar'
import { Tag } from '@ethersphere/bee-js'
import { Context as SettingsContext } from '../../providers/Settings'
interface Props {
reference: string
+22 -21
View File
@@ -1,4 +1,4 @@
import { BeeModes, Utils } from '@ethersphere/bee-js'
import { BeeModes, MantarayNode, NULL_ADDRESS, Reference } from '@ethersphere/bee-js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
import { useNavigate } from 'react-router-dom'
@@ -11,7 +11,6 @@ import { Context as SettingsContext } from '../../providers/Settings'
import { ROUTES } from '../../routes'
import { recognizeEnsOrSwarmHash, regexpEns } from '../../utils'
import { HISTORY_KEYS, determineHistoryName, putHistory } from '../../utils/local-storage'
import { ManifestJs } from '../../utils/manifest'
import { FileNavigation } from './FileNavigation'
export function Download(): ReactElement {
@@ -26,41 +25,43 @@ export function Download(): ReactElement {
const navigate = useNavigate()
const validateChange = (value: string) => {
if (
Utils.isHexString(value, 64) ||
Utils.isHexString(value, 128) ||
!value.trim().length ||
regexpEns.test(value)
) {
if (Reference.isValid(value) || regexpEns.test(value)) {
setReferenceError(undefined)
} else {
setReferenceError('Incorrect format of swarm hash. Expected 64 or 128 hexstring characters or ENS domain.')
}
}
// TODO: Test this for feeds, bzz, and bytes
async function onSwarmIdentifier(identifier: string) {
setLoading(true)
if (!beeApi) {
setLoading(false)
return
}
setLoading(true)
try {
const manifestJs = new ManifestJs(beeApi)
const feedIdentifier = await manifestJs.resolveFeedManifest(identifier)
let manifest = await MantarayNode.unmarshal(beeApi, identifier)
await manifest.loadRecursively(beeApi)
if (feedIdentifier) {
identifier = feedIdentifier
}
const isManifest = await manifestJs.isManifest(identifier)
// If the manifest is a feed, resolve it and overwrite the manifest
await manifest.resolveFeed(beeApi).then(
async feed =>
await feed.ifPresentAsync(async feedUpdate => {
manifest = MantarayNode.unmarshalFromData(feedUpdate.payload.toUint8Array(), NULL_ADDRESS)
await manifest.loadRecursively(beeApi)
}),
)
if (!isManifest) {
throw Error('The specified hash does not contain valid content.')
}
const indexDocument = await manifestJs.getIndexDocumentPath(identifier)
putHistory(HISTORY_KEYS.DOWNLOAD_HISTORY, identifier, determineHistoryName(identifier, indexDocument))
const rootMetadata = manifest.getDocsMetadata()
putHistory(
HISTORY_KEYS.DOWNLOAD_HISTORY,
identifier,
determineHistoryName(identifier, rootMetadata.indexDocument),
)
setUploadOrigin(defaultUploadOrigin)
navigate(ROUTES.HASH.replace(':hash', identifier))
} catch (error: unknown) {
+2 -2
View File
@@ -27,7 +27,7 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
{stamps.map(stamp => (
<MenuItem
key={stamp.batchID}
key={stamp.batchID.toHex()}
onClick={() => {
setSelected(stamp)
handleClose()
@@ -35,7 +35,7 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
selected={stamp.batchID === selectedStamp?.batchID}
>
<ListItemIcon>{stamp.usageText}</ListItemIcon>
<Typography variant="body2">{stamp.batchID.slice(0, 8)}[]</Typography>
<Typography variant="body2">{stamp.batchID.toHex().slice(0, 8)}[]</Typography>
</MenuItem>
))}
</Menu>
+48 -35
View File
@@ -1,4 +1,5 @@
import { Box, Typography } from '@material-ui/core'
import { MantarayNode, NULL_ADDRESS } from '@ethersphere/bee-js'
import { saveAs } from 'file-saver'
import JSZip from 'jszip'
import { useSnackbar } from 'notistack'
@@ -12,11 +13,10 @@ import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { ROUTES } from '../../routes'
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
import { ManifestJs } from '../../utils/manifest'
import { AssetPreview } from './AssetPreview'
import { AssetSummary } from './AssetSummary'
import { DownloadActionBar } from './DownloadActionBar'
import { AssetSyncing } from './AssetSyncing'
import { DownloadActionBar } from './DownloadActionBar'
export function Share(): ReactElement {
const { apiUrl, beeApi } = useContext(SettingsContext)
@@ -41,44 +41,57 @@ export function Share(): ReactElement {
return
}
const manifestJs = new ManifestJs(beeApi)
const isManifest = await manifestJs.isManifest(reference)
try {
let manifest = await MantarayNode.unmarshal(beeApi, reference)
await manifest.loadRecursively(beeApi)
if (!isManifest) {
// If the manifest is a feed, resolve it and overwrite the manifest
await manifest.resolveFeed(beeApi).then(
async feed =>
await feed.ifPresentAsync(async feedUpdate => {
manifest = MantarayNode.unmarshalFromData(feedUpdate.payload.toUint8Array(), NULL_ADDRESS)
await manifest.loadRecursively(beeApi)
}),
)
const entries = manifest.collectAndMap()
delete entries[META_FILE_NAME]
setSwarmEntries(entries)
const docsMetadata = manifest.getDocsMetadata()
// needed in catch block, shadows the outer variable
const indexDocument = docsMetadata.indexDocument
setIndexDocument(indexDocument)
try {
const remoteMetadata = await beeApi.downloadFile(reference, META_FILE_NAME)
const formattedMetadata = remoteMetadata.data.toJSON() as Metadata
if (formattedMetadata.isVideo || formattedMetadata.isImage) {
setPreview(`${apiUrl}/bzz/${reference}`)
}
setMetadata({ ...formattedMetadata, hash })
} catch (e) {
// if metadata is not available or invalid go with the default one
const count = Object.keys(entries).length
setMetadata({
hash,
type: count > 1 ? 'folder' : 'unknown',
name: reference,
count,
isWebsite: Boolean(indexDocument && /.*\.html?$/i.test(indexDocument)),
isVideo: Boolean(indexDocument && /.*\.(mp4|webm|ogg|mp3|ogg|wav)$/i.test(indexDocument)),
isImage: Boolean(indexDocument && /.*\.(jpg|jpeg|png|gif|webp|svg)$/i.test(indexDocument)),
// naive assumption based on indexDocument, we don't want to download the whole manifest
})
}
} catch {
setNotFound(true)
enqueueSnackbar('The specified hash does not contain valid content.', { variant: 'error' })
return
}
const entries = await manifestJs.getHashes(reference, { exclude: [META_FILE_NAME] })
setSwarmEntries(entries)
const indexDocument = await manifestJs.getIndexDocumentPath(reference)
setIndexDocument(indexDocument)
try {
const remoteMetadata = await beeApi.downloadFile(reference, META_FILE_NAME)
const formattedMetadata = JSON.parse(remoteMetadata.data.text()) as Metadata
if (formattedMetadata.isVideo || formattedMetadata.isImage) {
setPreview(`${apiUrl}/bzz/${reference}`)
}
setMetadata({ ...formattedMetadata, hash })
} catch (e) {
// if metadata is not available or invalid go with the default one
const count = Object.keys(entries).length
setMetadata({
hash,
type: count > 1 ? 'folder' : 'unknown',
name: reference,
count,
isWebsite: Boolean(indexDocument && /.*\.html?$/i.test(indexDocument)),
isVideo: Boolean(indexDocument && /.*\.(mp4|webm|ogg|mp3|ogg|wav)$/i.test(indexDocument)),
isImage: Boolean(indexDocument && /.*\.(jpg|jpeg|png|gif|webp|svg)$/i.test(indexDocument)),
// naive assumption based on indexDocument, we don't want to donwload the whole manifest
})
}
}
function onOpen() {
@@ -119,7 +132,7 @@ export function Share(): ReactElement {
} else {
const zip = new JSZip()
for (const [path, hash] of Object.entries(swarmEntries)) {
zip.file(path, await beeApi.downloadData(hash))
zip.file(path, (await beeApi.downloadData(hash)).toUint8Array())
}
const content = await zip.generateAsync({ type: 'blob' })
saveAs(content, reference + '.zip')
+3 -3
View File
@@ -114,10 +114,10 @@ export function Upload(): ReactElement {
beeApi
.uploadFiles(stamp.batchID, fls, { indexDocument, deferred: true })
.then(hash => {
putHistory(HISTORY_KEYS.UPLOAD_HISTORY, hash.reference, getAssetNameFromFiles(files))
putHistory(HISTORY_KEYS.UPLOAD_HISTORY, hash.reference.toHex(), getAssetNameFromFiles(files))
if (uploadOrigin.origin === 'UPLOAD') {
navigate(ROUTES.HASH.replace(':hash', hash.reference), { replace: true })
navigate(ROUTES.HASH.replace(':hash', hash.reference.toHex()), { replace: true })
} else {
updateFeed(beeApi, identity as Identity, hash.reference, stamp.batchID, password as string).then(() => {
persistIdentity(identities, identity as Identity)
@@ -164,7 +164,7 @@ export function Upload(): ReactElement {
<>
<Box mb={2}>
{hasAnyStamps && stampMode === 'SELECT' ? (
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID} />
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID.toHex()} />
) : (
<PostageStampAdvancedCreation onFinished={() => setStampMode('SELECT')} />
)}
+13 -10
View File
@@ -1,4 +1,5 @@
import { Box, Tooltip, Typography } from '@material-ui/core'
import { BZZ, DAI } from '@ethersphere/bee-js'
import { Wallet } from 'ethers'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect, useState } from 'react'
@@ -11,20 +12,19 @@ import ExpandableListItemKey from '../../components/ExpandableListItemKey'
import { HistoryHeader } from '../../components/HistoryHeader'
import { Loading } from '../../components/Loading'
import { SwarmButton } from '../../components/SwarmButton'
import { Token } from '../../models/Token'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { Context as TopUpContext } from '../../providers/TopUp'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { createGiftWallet } from '../../utils/desktop'
import { ResolvedWallet } from '../../utils/wallet'
const GIFT_WALLET_FUND_DAI_AMOUNT = Token.fromDecimal('0.1', 18)
const GIFT_WALLET_FUND_BZZ_AMOUNT = Token.fromDecimal('0.5', 16)
const GIFT_WALLET_FUND_DAI_AMOUNT = DAI.fromDecimalString('0.1')
const GIFT_WALLET_FUND_BZZ_AMOUNT = BZZ.fromDecimalString('0.5')
export default function Index(): ReactElement {
const { giftWallets, addGiftWallet } = useContext(TopUpContext)
const { rpcProvider, desktopUrl } = useContext(SettingsContext)
const { balance } = useContext(BalanceProvider)
const { walletBalance } = useContext(BeeContext)
const [loading, setLoading] = useState(false)
const [balances, setBalances] = useState<ResolvedWallet[]>([])
@@ -67,13 +67,13 @@ export default function Index(): ReactElement {
navigate(-1)
}
if (!balance) {
if (!walletBalance) {
return <Loading />
}
const notEnoughFundsCheck =
balance.dai.toBigNumber.isLessThanOrEqualTo(GIFT_WALLET_FUND_DAI_AMOUNT.toBigNumber) ||
balance.bzz.toBigNumber.isLessThan(GIFT_WALLET_FUND_BZZ_AMOUNT.toBigNumber)
walletBalance.nativeTokenBalance.lte(GIFT_WALLET_FUND_DAI_AMOUNT) ||
walletBalance.bzzBalance.lt(GIFT_WALLET_FUND_BZZ_AMOUNT)
return (
<>
@@ -86,10 +86,13 @@ export default function Index(): ReactElement {
</Typography>
</Box>
<Box mb={0.25}>
<ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
<ExpandableListItem
label="xDAI balance"
value={`${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`}
/>
</Box>
<Box mb={2}>
<ExpandableListItem label="xBZZ balance" value={`${balance.bzz.toSignificantDigits(4)} xBZZ`} />
<ExpandableListItem label="xBZZ balance" value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`} />
</Box>
<Box mb={4}>
{balances.map((x, i) => (
+2 -4
View File
@@ -1,3 +1,4 @@
import { BZZ } from '@ethersphere/bee-js'
import { useContext } from 'react'
import { useNavigate } from 'react-router'
import ExchangeFunds from 'remixicon-react/ExchangeFundsLineIcon'
@@ -9,10 +10,7 @@ export function ChequebookInfoCard() {
const { chequebookBalance } = useContext(BeeContext)
const navigate = useNavigate()
if (
chequebookBalance?.availableBalance !== undefined &&
chequebookBalance?.availableBalance.toBigNumber.isGreaterThan(0)
) {
if (chequebookBalance?.availableBalance !== undefined && chequebookBalance?.availableBalance.gt(BZZ.fromPLUR('0'))) {
return (
<Card
buttonProps={{
+5 -8
View File
@@ -4,21 +4,18 @@ import Upload from 'remixicon-react/UploadLineIcon'
import Wallet from 'remixicon-react/Wallet3LineIcon'
import Card from '../../components/Card'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { ROUTES } from '../../routes'
export function WalletInfoCard() {
const { nodeInfo } = useContext(BeeContext)
const { balance, error } = useContext(BalanceProvider)
const { nodeInfo, walletBalance } = useContext(BeeContext)
const navigate = useNavigate()
let balanceText = 'Loading...'
if (error) {
balanceText = 'Could not load...'
console.error(error) // eslint-disable-line
} else if (balance) {
balanceText = `${balance.bzz.toSignificantDigits(4)} xBZZ | ${balance.dai.toSignificantDigits(4)} xDAI`
if (walletBalance) {
balanceText = `${walletBalance.bzzBalance.toSignificantDigits(
4,
)} xBZZ | ${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`
}
if (nodeInfo?.beeMode && ['light', 'full', 'dev'].includes(nodeInfo.beeMode)) {
+4 -2
View File
@@ -13,7 +13,7 @@ import NodeInfoCard from './NodeInfoCard'
import { WalletInfoCard } from './WalletInfoCard'
export default function Status(): ReactElement {
const { beeVersion, status, topology, nodeInfo, chainId } = useContext(BeeContext)
const { beeVersion, status, topology, nodeInfo, walletBalance } = useContext(BeeContext)
const { isDesktop, desktopUrl } = useContext(SettingsContext)
const { beeDesktopVersion } = useBeeDesktop(isDesktop, desktopUrl)
const { newBeeDesktopVersion } = useNewBeeDesktopVersion(isDesktop, desktopUrl, false)
@@ -64,7 +64,9 @@ export default function Status(): ReactElement {
)}
<ExpandableListItem label="Bee version" value={beeVersion} />
<ExpandableListItem label="Mode" value={nodeInfo?.beeMode} />
{chainId !== null && <ExpandableListItem label="Blockchain network" value={chainIdToName(chainId)} />}
{walletBalance !== null && (
<ExpandableListItem label="Blockchain network" value={chainIdToName(walletBalance.chainID)} />
)}
</div>
)
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { BeeModes } from '@ethersphere/bee-js'
import { Box, Grid, Typography } from '@material-ui/core'
import { BeeModes } from '@ethersphere/bee-js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect } from 'react'
import { useNavigate } from 'react-router'
+1 -1
View File
@@ -9,7 +9,7 @@ interface Props {
}
export function PostageStamp({ stamp, shorten }: Props): ReactElement {
const batchId = shorten ? stamp.batchID.slice(0, 8) : stamp.batchID
const batchId = shorten ? stamp.batchID.toHex().slice(0, 8) : stamp.batchID
const label = `${batchId}${stamp.label ? ` - ${stamp.label}` : ''}`
return (
@@ -1,5 +1,5 @@
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
import { Box, Grid, IconButton, Typography, createStyles, makeStyles } from '@material-ui/core'
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
import BigNumber from 'bignumber.js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
@@ -13,7 +13,7 @@ import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { Context as StampsContext } from '../../providers/Stamps'
import { ROUTES } from '../../routes'
import { calculateStampPrice, convertAmountToSeconds, secondsToTimeString, waitUntilStampExists } from '../../utils'
import { secondsToTimeString } from '../../utils'
import { getHumanReadableFileSize } from '../../utils/file'
interface Props {
@@ -61,18 +61,22 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
const { enqueueSnackbar } = useSnackbar()
function getTtl(amount: number): string {
function getTtl(amount: bigint): string {
const isCurrentPriceAvailable = chainState && chainState.currentPrice
if (amount <= 0 || !isCurrentPriceAvailable) {
return '-'
}
const pricePerBlock = Number.parseInt(chainState.currentPrice, 10)
const pricePerBlock = chainState.currentPrice
return `${secondsToTimeString(
convertAmountToSeconds(amount, pricePerBlock),
)} (with price of ${pricePerBlock.toFixed(0)} PLUR per block)`
try {
return `${secondsToTimeString(
Utils.getStampDuration(amount, pricePerBlock, 5).toSeconds(),
)} (with price of ${pricePerBlock} PLUR per block)`
} catch {
return `0 seconds (with price of ${pricePerBlock} PLUR per block)`
}
}
function getPrice(depth: number, amount: bigint): string {
@@ -82,9 +86,9 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
return '-'
}
const price = calculateStampPrice(depth, amount)
const price = Utils.getStampCost(depth, amount)
return `${price.toSignificantDigits()} xBZZ`
return `${price.toSignificantDigits(4)} xBZZ`
}
async function submit() {
@@ -107,8 +111,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
immutableFlag: immutable,
}
const batchId = await beeApi.createPostageBatch(amount.toString(), depth, options)
await waitUntilStampExists(batchId, beeApi)
await beeApi.createPostageBatch(amount.toString(), depth, options)
await refresh()
onFinished()
} catch (e) {
@@ -173,7 +176,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
return '-'
}
const theoreticalMaximumVolume = getHumanReadableFileSize(Utils.getStampMaximumCapacityBytes(depth))
const theoreticalMaximumVolume = getHumanReadableFileSize(Utils.getStampTheoreticalBytes(depth))
const effectiveVolume = getHumanReadableFileSize(Utils.getStampEffectiveBytes(depth))
return (
@@ -227,7 +230,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
<Box mt={0.25} sx={{ bgcolor: '#f6f6f6' }} p={2}>
<Grid container justifyContent="space-between">
<Typography>Corresponding TTL (Time to live)</Typography>
<Typography>{!amountError && amountInput ? getTtl(Number.parseInt(amountInput, 10)) : '-'}</Typography>
<Typography>{!amountError && amountInput ? getTtl(BigInt(amountInput)) : '-'}</Typography>
</Grid>
</Box>
{amountError && <Typography>{amountError}</Typography>}
+3 -3
View File
@@ -14,7 +14,7 @@ export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactEl
if (!stamps) {
return
}
const stamp = stamps.find(x => x.batchID === stampId)
const stamp = stamps.find(x => x.batchID.toHex() === stampId)
if (stamp) {
onSelect(stamp)
@@ -24,8 +24,8 @@ export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactEl
return (
<SwarmSelect
options={(stamps || []).map(x => ({
label: x.label ? x.batchID.slice(0, 8) + ' - ' + x.label : x.batchID.slice(0, 8),
value: x.batchID,
label: x.label ? x.batchID.toHex().slice(0, 8) + ' - ' + x.label : x.batchID.toHex().slice(0, 8),
value: x.batchID.toHex(),
}))}
onChange={event => onChange(event.target.value as string)}
defaultValue={defaultValue}
@@ -1,6 +1,6 @@
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
import { Box, Button, Grid, Slider, Typography } from '@material-ui/core'
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
import { Duration, PostageBatchOptions, Size, Utils } from '@ethersphere/bee-js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
import { Link } from 'react-router-dom'
@@ -10,7 +10,7 @@ import { SwarmTextInput } from '../../components/SwarmTextInput'
import { Context as SettingsContext } from '../../providers/Settings'
import { Context as StampsContext } from '../../providers/Stamps'
import { ROUTES } from '../../routes'
import { calculateStampPrice, convertAmountToSeconds, secondsToTimeString, waitUntilStampExists } from '../../utils'
import { secondsToTimeString } from '../../utils'
interface Props {
onFinished: () => void
@@ -46,8 +46,8 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
const { refresh } = useContext(StampsContext)
const { beeApi } = useContext(SettingsContext)
const [depthInput, setDepthInput] = useState<number>(Utils.getDepthForCapacity(4))
const [amountInput, setAmountInput] = useState<string>(Utils.getAmountForTtl(30))
const [depthInput, setDepthInput] = useState<number>(Utils.getDepthForSize(Size.fromGigabytes(4)))
const [amountInput, setAmountInput] = useState<bigint>(Utils.getAmountForDuration(Duration.fromDays(30), 26500, 5))
const [labelInput, setLabelInput] = useState('')
const [submitting, setSubmitting] = useState(false)
const [buttonValue, setButtonValue] = useState(4)
@@ -56,24 +56,24 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
if (typeof newValue !== 'number') {
return
}
const amountValue = Utils.getAmountForTtl(newValue)
const amountValue = Utils.getAmountForDuration(Duration.fromDays(newValue), 26500, 5)
setAmountInput(amountValue)
}
const { enqueueSnackbar } = useSnackbar()
function getTtl(amount: string): string {
function getTtl(amount: bigint): string {
const pricePerBlock = 24000
return `${secondsToTimeString(
convertAmountToSeconds(parseInt(amount, 10), pricePerBlock),
)} (with price of ${pricePerBlock.toFixed(0)} PLUR per block)`
Utils.getStampDuration(amount, pricePerBlock, 5).toSeconds(),
)} (with price of ${pricePerBlock} PLUR per block)`
}
function getPrice(depth: number, amount: bigint): string {
const price = calculateStampPrice(depth, amount)
const price = Utils.getStampCost(depth, amount)
return `${price.toSignificantDigits()} xBZZ`
return `${price.toSignificantDigits(4)} xBZZ`
}
async function submit() {
@@ -96,8 +96,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
immutableFlag: true,
}
const batchId = await beeApi.createPostageBatch(amount.toString(), depth, options)
await waitUntilStampExists(batchId, beeApi)
await beeApi.createPostageBatch(amount.toString(), depth, options)
await refresh()
onFinished()
} catch (e) {
@@ -109,7 +108,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
function handleBatchSize(gigabytes: number) {
setButtonValue(gigabytes)
const capacity = Utils.getDepthForCapacity(gigabytes)
const capacity = Utils.getDepthForSize(Size.fromGigabytes(gigabytes))
setDepthInput(capacity)
}
+3 -3
View File
@@ -4,7 +4,7 @@ import Dialog from '@material-ui/core/Dialog'
import DialogContent from '@material-ui/core/DialogContent'
import DialogTitle from '@material-ui/core/DialogTitle'
import { Check, Clear } from '@material-ui/icons'
import React, { ReactElement, useState } from 'react'
import { ReactElement, useState } from 'react'
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
import { SwarmSelect } from '../../components/SwarmSelect'
import { EnrichedPostageBatch } from '../../providers/Stamps'
@@ -39,7 +39,7 @@ export function SelectPostageStampModal({ stamps, onSelect, onClose }: Props): R
const classes = useStyles()
function onChange(stampId: string) {
const stamp = stamps.find(x => x.batchID === stampId)
const stamp = stamps.find(x => x.batchID.toHex() === stampId)
if (stamp) {
setSelectedStamp(stamp)
@@ -66,7 +66,7 @@ export function SelectPostageStampModal({ stamps, onSelect, onClose }: Props): R
</DialogTitle>
<DialogContent>
<SwarmSelect
options={stamps.map(x => ({ label: x.batchID, value: x.batchID }))}
options={stamps.map(x => ({ label: x.batchID.toHex(), value: x.batchID.toHex() }))}
onChange={event => onChange(event.target.value as string)}
/>
</DialogContent>
+3 -7
View File
@@ -28,10 +28,10 @@ function StampsTable({ postageStamps }: Props): ReactElement | null {
<ExpandableList label="Postage Stamps" defaultOpen>
{postageStamps.map(stamp => (
<ExpandableElement
key={stamp.batchID}
key={stamp.batchID.toHex()}
expandable={
<>
<ExpandableListItemKey label="Batch ID" value={stamp.batchID} />
<ExpandableListItemKey label="Batch ID" value={stamp.batchID.toHex()} />
<ExpandableListItem label="Depth" value={String(stamp.depth)} />
<ExpandableListItem
label="Capacity"
@@ -40,13 +40,9 @@ function StampsTable({ postageStamps }: Props): ReactElement | null {
)}`}
/>
<ExpandableListItem label="Amount" value={parseInt(stamp.amount, 10).toLocaleString()} />
<ExpandableListItem
label="Expires in"
value={stamp.batchTTL === -1 ? 'does not expire' : `${secondsToTimeString(stamp.batchTTL)}`}
/>
<ExpandableListItem label="Expires in" value={secondsToTimeString(stamp.duration.toSeconds())} />
<ExpandableListItem label="Label" value={stamp.label} />
<ExpandableListItem label="Usable" value={stamp.usable ? 'yes' : 'no'} />
<ExpandableListItem label="Exists" value={stamp.exists ? 'yes' : 'no'} />
<ExpandableListItem label="Immutable" value={stamp.immutableFlag ? 'yes' : 'no'} />
<ExpandableListItem label="Purchase Block Number" value={stamp.blockNumber} />
<ExpandableListItemActions>
+10 -9
View File
@@ -1,4 +1,5 @@
import { Box, Grid, Typography } from '@material-ui/core'
import { DAI } from '@ethersphere/bee-js'
import { ReactElement, useContext } from 'react'
import { useNavigate } from 'react-router'
import Check from 'remixicon-react/CheckLineIcon'
@@ -9,10 +10,9 @@ import { Loading } from '../../components/Loading'
import { SwarmButton } from '../../components/SwarmButton'
import { SwarmDivider } from '../../components/SwarmDivider'
import { Context } from '../../providers/Bee'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
const MINIMUM_XDAI = '0.5'
const MINIMUM_XDAI = DAI.fromDecimalString('0.5')
interface Props {
header: string
@@ -22,15 +22,14 @@ interface Props {
}
export default function Index({ header, title, p, next }: Props): ReactElement {
const { nodeAddresses } = useContext(Context)
const { balance } = useContext(BalanceProvider)
const { nodeAddresses, walletBalance } = useContext(Context)
const navigate = useNavigate()
if (!balance || !nodeAddresses) {
if (!walletBalance || !nodeAddresses) {
return <Loading />
}
const disabled = balance.dai.toDecimal.lt(MINIMUM_XDAI)
const disabled = walletBalance.nativeTokenBalance.lt(MINIMUM_XDAI)
return (
<>
@@ -44,17 +43,19 @@ export default function Index({ header, title, p, next }: Props): ReactElement {
<Box mb={4}>{p}</Box>
<SwarmDivider mb={4} />
<Box mb={0.25}>
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum} expanded />
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded />
</Box>
<Box mb={4}>
<ExpandableListItem label="xDAI balance" value={balance.dai.toSignificantDigits(4)} />
<ExpandableListItem label="xDAI balance" value={walletBalance.nativeTokenBalance.toSignificantDigits(4)} />
</Box>
<Grid container direction="row" justifyContent="space-between">
<SwarmButton iconType={Check} onClick={() => navigate(next)} disabled={disabled}>
Proceed
</SwarmButton>
{disabled ? (
<Typography>Please deposit at least {MINIMUM_XDAI} xDAI to the address above in order to proceed.</Typography>
<Typography>
Please deposit at least {MINIMUM_XDAI.toSignificantDigits(4)} xDAI to the address above in order to proceed.
</Typography>
) : null}
</Grid>
</>
+13 -8
View File
@@ -1,5 +1,5 @@
import { BeeModes } from '@ethersphere/bee-js'
import { Box, Typography } from '@material-ui/core'
import { BeeModes } from '@ethersphere/bee-js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router'
@@ -14,16 +14,14 @@ import { SwarmButton } from '../../components/SwarmButton'
import { SwarmDivider } from '../../components/SwarmDivider'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { ROUTES } from '../../routes'
import { sleepMs } from '../../utils'
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
import { ResolvedWallet } from '../../utils/wallet'
export function GiftCardFund(): ReactElement {
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
const { nodeAddresses, nodeInfo, walletBalance } = useContext(BeeContext)
const { isDesktop, desktopUrl, rpcProvider, rpcProviderUrl } = useContext(SettingsContext)
const { balance } = useContext(BalanceProvider)
const [loading, setLoading] = useState(false)
const [wallet, setWallet] = useState<ResolvedWallet | null>(null)
@@ -41,7 +39,7 @@ export function GiftCardFund(): ReactElement {
ResolvedWallet.make(privateKeyString, rpcProvider).then(setWallet)
}, [privateKeyString, rpcProvider])
if (!wallet || !balance) {
if (!wallet || !walletBalance) {
return <Loading />
}
@@ -108,13 +106,20 @@ export function GiftCardFund(): ReactElement {
<ArrowDown size={24} color="#aaaaaa" />
</Box>
<Box mb={0.25}>
<ExpandableListItemKey label="Node wallet address" value={nodeAddresses?.ethereum || 'N/A'} expanded />
<ExpandableListItemKey
label="Node wallet address"
value={nodeAddresses?.ethereum.toChecksum() || 'N/A'}
expanded
/>
</Box>
<Box mb={0.25}>
<ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
<ExpandableListItem
label="xDAI balance"
value={`${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`}
/>
</Box>
<Box mb={2}>
<ExpandableListItem label="xBZZ balance" value={`${balance.bzz.toSignificantDigits(4)} xBZZ`} />
<ExpandableListItem label="xBZZ balance" value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`} />
</Box>
<SwarmButton iconType={Check} onClick={onFund} disabled={loading} loading={loading}>
{canUpgradeToLightNode ? 'Send all funds to your node and Upgrade' : 'Send all funds to your node'}
+6 -7
View File
@@ -1,17 +1,16 @@
import { Box, Typography } from '@material-ui/core'
import { BZZ, DAI } from '@ethersphere/bee-js'
import { Wallet } from 'ethers'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
import { useNavigate } from 'react-router'
import { Context as SettingsContext } from '../../providers/Settings'
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
import { HistoryHeader } from '../../components/HistoryHeader'
import { ProgressIndicator } from '../../components/ProgressIndicator'
import { SwarmButton } from '../../components/SwarmButton'
import { SwarmDivider } from '../../components/SwarmDivider'
import { SwarmTextInput } from '../../components/SwarmTextInput'
import { BzzToken } from '../../models/BzzToken'
import { DaiToken } from '../../models/DaiToken'
import { Context as SettingsContext } from '../../providers/Settings'
import { ROUTES } from '../../routes'
import { Rpc } from '../../utils/rpc'
@@ -29,10 +28,10 @@ export function GiftCardTopUpIndex(): ReactElement {
setLoading(true)
try {
const wallet = new Wallet(giftCode, rpcProvider)
const dai = new DaiToken(await Rpc._eth_getBalance(wallet.address, rpcProvider))
const bzz = new BzzToken(await Rpc._eth_getBalanceERC20(wallet.address, rpcProvider))
const dai = await Rpc._eth_getBalance(wallet.address, rpcProvider)
const bzz = await Rpc._eth_getBalanceERC20(wallet.address, rpcProvider)
if (dai.toDecimal.lt(0.001) || bzz.toDecimal.lt(0.001)) {
if (dai.lt(DAI.fromDecimalString('0.001')) || bzz.lt(BZZ.fromDecimalString('0.001'))) {
throw Error('Gift wallet does not have enough funds')
}
enqueueSnackbar('Successfully verified gift wallet', { variant: 'success' })
+38 -43
View File
@@ -1,5 +1,5 @@
import { BeeModes } from '@ethersphere/bee-js'
import { Box, Typography } from '@material-ui/core'
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useEffect, useState } from 'react'
import { useNavigate } from 'react-router'
@@ -13,14 +13,11 @@ import { Loading } from '../../components/Loading'
import { SwarmButton } from '../../components/SwarmButton'
import { SwarmDivider } from '../../components/SwarmDivider'
import { SwarmTextInput } from '../../components/SwarmTextInput'
import { BZZ_DECIMAL_PLACES, BzzToken } from '../../models/BzzToken'
import { DaiToken } from '../../models/DaiToken'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { ROUTES } from '../../routes'
import { sleepMs } from '../../utils'
import { SwapError, isSwapError, wrapWithSwapError } from '../../utils/SwapError'
import { isSwapError, SwapError, wrapWithSwapError } from '../../utils/SwapError'
import {
getBzzPriceAsDai,
getDesktopConfiguration,
@@ -31,8 +28,8 @@ import {
import { Rpc } from '../../utils/rpc'
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
const MINIMUM_XDAI = '0.1'
const MINIMUM_XBZZ = '0.1'
const MINIMUM_XDAI = DAI.fromDecimalString('0.1')
const MINIMUM_XBZZ = BZZ.fromDecimalString('0.1')
const GENERIC_SWAP_FAILED_ERROR_MESSAGE = 'Failed to swap. The full error is printed to the console.'
@@ -44,15 +41,14 @@ export function Swap({ header }: Props): ReactElement {
const [loading, setLoading] = useState(false)
const [hasSwapped, setSwapped] = useState(false)
const [userInputSwap, setUserInputSwap] = useState<string | null>(null)
const [price, setPrice] = useState(DaiToken.fromDecimal('0.6'))
const [price, setPrice] = useState(DAI.fromDecimalString('0.3'))
const [error, setError] = useState<string | null>(null)
const [daiToSwap, setDaiToSwap] = useState<DaiToken | null>(null)
const [bzzAfterSwap, setBzzAfterSwap] = useState<BzzToken | null>(null)
const [daiAfterSwap, setDaiAfterSwap] = useState<DaiToken | null>(null)
const [daiToSwap, setDaiToSwap] = useState<DAI | null>(null)
const [bzzAfterSwap, setBzzAfterSwap] = useState<BZZ | null>(null)
const [daiAfterSwap, setDaiAfterSwap] = useState<DAI | null>(null)
const { rpcProviderUrl, isDesktop, desktopUrl } = useContext(SettingsContext)
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
const { balance } = useContext(BalanceProvider)
const { nodeAddresses, nodeInfo, walletBalance } = useContext(BeeContext)
const navigate = useNavigate()
const { enqueueSnackbar } = useSnackbar()
@@ -65,30 +61,30 @@ export function Swap({ header }: Props): ReactElement {
// Set the initial xDAI to swap
useEffect(() => {
if (!balance || userInputSwap) {
if (!walletBalance || userInputSwap) {
return
}
const minimumOptimalValue = DaiToken.fromDecimal('1').plusBaseUnits(MINIMUM_XDAI).toDecimal
const minimumOptimalValue = DAI.fromDecimalString('1').plus(MINIMUM_XDAI)
if (balance.dai.toDecimal.isGreaterThanOrEqualTo(minimumOptimalValue)) {
if (walletBalance.nativeTokenBalance.gte(minimumOptimalValue)) {
// Balance has at least 1 + MINIMUM_XDAI xDai
setDaiToSwap(balance.dai.minusBaseUnits('1'))
setDaiToSwap(walletBalance.nativeTokenBalance.minus(DAI.fromDecimalString('1')))
} else {
// Balance is low, halve the amount
setDaiToSwap(new DaiToken(balance.dai.toBigNumber.dividedToIntegerBy(2)))
setDaiToSwap(walletBalance.nativeTokenBalance.divide(BigInt(2)))
}
}, [balance, userInputSwap])
}, [walletBalance, userInputSwap])
// Set the xDAI to swap based on user input
useEffect(() => {
setError(null)
try {
if (userInputSwap) {
const dai = DaiToken.fromDecimal(userInputSwap)
const dai = DAI.fromDecimalString(userInputSwap)
setDaiToSwap(dai)
if (dai.toDecimal.lte(0)) {
if (dai.lte(DAI.fromDecimalString('0'))) {
setError('xDAI to swap must be a positive number')
}
}
@@ -99,25 +95,23 @@ export function Swap({ header }: Props): ReactElement {
// Calculate the amount of tokens after swap
useEffect(() => {
if (!balance || !daiToSwap || error) {
if (!walletBalance || !daiToSwap || error) {
return
}
const daiAfterSwap = new DaiToken(balance.dai.toBigNumber.minus(daiToSwap.toBigNumber))
const daiAfterSwap = walletBalance.nativeTokenBalance.minus(daiToSwap)
setDaiAfterSwap(daiAfterSwap)
const tokensConverted = BzzToken.fromDecimal(
daiToSwap.toBigNumber.dividedBy(price.toBigNumber).decimalPlaces(BZZ_DECIMAL_PLACES),
)
const bzzAfterSwap = new BzzToken(tokensConverted.toBigNumber.plus(balance.bzz.toBigNumber))
const tokensConverted = daiToSwap.exchangeToBZZ(price)
const bzzAfterSwap = tokensConverted.plus(walletBalance.bzzBalance)
setBzzAfterSwap(bzzAfterSwap)
if (daiAfterSwap.toDecimal.lt(MINIMUM_XDAI)) {
setError(`Must keep at least ${MINIMUM_XDAI} xDAI after swap!`)
} else if (bzzAfterSwap.toDecimal.lt(MINIMUM_XBZZ)) {
setError(`Must have at least ${MINIMUM_XBZZ} xBZZ after swap!`)
if (daiAfterSwap.lt(MINIMUM_XDAI)) {
setError(`Must keep at least ${MINIMUM_XDAI.toSignificantDigits(4)} xDAI after swap!`)
} else if (bzzAfterSwap.lt(MINIMUM_XBZZ)) {
setError(`Must have at least ${MINIMUM_XBZZ.toSignificantDigits(4)} xBZZ after swap!`)
}
}, [error, balance, daiToSwap, price])
}, [error, walletBalance, daiToSwap, price])
if (!balance || !nodeAddresses || !daiToSwap || !bzzAfterSwap || !daiAfterSwap) {
if (!walletBalance || !nodeAddresses || !daiToSwap || !bzzAfterSwap || !daiAfterSwap) {
return <Loading />
}
@@ -135,9 +129,9 @@ export function Swap({ header }: Props): ReactElement {
}
}
async function sendSwapRequest(daiToSwap: DaiToken) {
async function sendSwapRequest(daiToSwap: DAI) {
try {
await performSwap(desktopUrl, daiToSwap.toString)
await performSwap(desktopUrl, daiToSwap)
} catch (error) {
// eslint-disable-next-line no-console
console.error(error)
@@ -145,7 +139,7 @@ export function Swap({ header }: Props): ReactElement {
}
}
async function performSwapWithChecks(daiToSwap: DaiToken) {
async function performSwapWithChecks(daiToSwap: DAI) {
if (!localStorage.getItem('apiKey')) {
throw new SwapError('API key is not set, reopen dashboard through Swarm Desktop')
}
@@ -186,7 +180,9 @@ export function Swap({ header }: Props): ReactElement {
: 'Successfully swapped. Balances will refresh soon. You may now navigate away.'
enqueueSnackbar(message, { variant: 'success' })
if (canUpgradeToLightNode) await restart()
if (canUpgradeToLightNode) {
await restart()
}
} catch (error) {
if (isSwapError(error)) {
// we have a custom and user friendly error message
@@ -201,7 +197,6 @@ export function Swap({ header }: Props): ReactElement {
console.error(error) // eslint-disable-line
}
} finally {
balance?.refresh()
setLoading(false)
}
}
@@ -217,15 +212,15 @@ export function Swap({ header }: Props): ReactElement {
</Box>
<Box mb={4}>
<Typography>
You need to swap xDAI to xBZZ in order to use Swarm. Make sure to keep at least {MINIMUM_XDAI} xDAI in order
to pay for transaction costs on the network.
You need to swap xDAI to xBZZ in order to use Swarm. Make sure to keep at least{' '}
{MINIMUM_XDAI.toSignificantDigits(4)} xDAI in order to pay for transaction costs on the network.
</Typography>
</Box>
<SwarmDivider mb={4} />
<Box mb={4}>
<Typography>
Your current balance is {balance.dai.toSignificantDigits(4)} xDAI and {balance.bzz.toSignificantDigits(4)}{' '}
xBZZ.
Your current balance is {walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI and{' '}
{walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ.
</Typography>
</Box>
<Box mb={4}>
@@ -242,7 +237,7 @@ export function Swap({ header }: Props): ReactElement {
<ArrowDown size={24} color="#aaaaaa" />
</Box>
<Box mb={0.25}>
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum} expanded />
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded />
</Box>
<Box mb={0.25}>
<ExpandableListItem
+7 -9
View File
@@ -1,5 +1,5 @@
import { BeeModes } from '@ethersphere/bee-js'
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
import { useNavigate } from 'react-router'
@@ -15,7 +15,6 @@ import { SwarmButton } from '../../components/SwarmButton'
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
import { Context as BeeContext, CheckState } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import { Context as BalanceProvider } from '../../providers/WalletBalance'
import { ROUTES } from '../../routes'
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
@@ -33,15 +32,14 @@ const useStyles = makeStyles(() =>
}),
)
const MINIMUM_XDAI = '0.05'
const MINIMUM_XBZZ = '0.1'
const MINIMUM_XDAI = DAI.fromDecimalString('0.05')
const MINIMUM_XBZZ = BZZ.fromDecimalString('0.1')
export default function TopUp(): ReactElement {
const navigate = useNavigate()
const styles = useStyles()
const { isDesktop, desktopUrl } = useContext(SettingsContext)
const { nodeInfo, status } = useContext(BeeContext)
const { balance } = useContext(BalanceProvider)
const { nodeInfo, status, walletBalance } = useContext(BeeContext)
const { rpcProviderUrl } = useContext(SettingsContext)
const [loading, setLoading] = useState(false)
const { enqueueSnackbar } = useSnackbar()
@@ -49,8 +47,8 @@ export default function TopUp(): ReactElement {
const canUpgradeToLightNode =
isDesktop &&
nodeInfo?.beeMode === BeeModes.ULTRA_LIGHT &&
balance?.dai.toDecimal.gte(MINIMUM_XDAI) &&
balance?.bzz.toDecimal.gte(MINIMUM_XBZZ)
walletBalance?.nativeTokenBalance.gte(MINIMUM_XDAI) &&
walletBalance?.bzzBalance.gte(MINIMUM_XBZZ)
async function restart() {
setLoading(true)
@@ -67,7 +65,7 @@ export default function TopUp(): ReactElement {
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
if (!balance) {
if (!walletBalance) {
return <Loading />
}