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:
@@ -28,18 +28,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
## Try getting the node modules from cache, if failed npm ci
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
id: cache-npm
|
|
||||||
with:
|
|
||||||
path: node_modules
|
|
||||||
key: ${{ runner.os }}-node-${{ matrix.node }}-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.OS }}-node-${{ matrix.node-version }}-${{ env.cache-name }}-
|
|
||||||
${{ runner.OS }}-node-${{ matrix.node-version }}-
|
|
||||||
|
|
||||||
- name: Install npm deps
|
- name: Install npm deps
|
||||||
if: steps.cache-npm.outputs.cache-hit != 'true'
|
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Commit linting
|
- name: Commit linting
|
||||||
|
|||||||
@@ -25,21 +25,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
|
|
||||||
## Try getting the node modules from cache, if failed npm ci
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
id: cache-npm
|
|
||||||
with:
|
|
||||||
path: node_modules
|
|
||||||
key: ${{ runner.os }}-node-${{ matrix.node }}-${{ hashFiles('**/package-lock.json') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.OS }}-node-${{ matrix.node-version }}-${{ env.cache-name }}-
|
|
||||||
${{ runner.OS }}-node-${{ matrix.node-version }}-
|
|
||||||
|
|
||||||
- name: Install npm deps
|
- name: Install npm deps
|
||||||
if: steps.cache-npm.outputs.cache-hit != 'true'
|
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: npm run test
|
run: npm run test
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Generated
+193
-15298
File diff suppressed because it is too large
Load Diff
+1
-3
@@ -26,8 +26,7 @@
|
|||||||
"url": "https://github.com/ethersphere/bee-dashboard.git"
|
"url": "https://github.com/ethersphere/bee-dashboard.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ethersphere/bee-js": "^8.3.1",
|
"@ethersphere/bee-js": "^9.6.0",
|
||||||
"@ethersphere/swarm-cid": "^0.1.0",
|
|
||||||
"@fairdatasociety/fdp-storage": "^0.19.0",
|
"@fairdatasociety/fdp-storage": "^0.19.0",
|
||||||
"@material-ui/core": "4.12.3",
|
"@material-ui/core": "4.12.3",
|
||||||
"@material-ui/icons": "4.11.2",
|
"@material-ui/icons": "4.11.2",
|
||||||
@@ -43,7 +42,6 @@
|
|||||||
"formik": "2.2.9",
|
"formik": "2.2.9",
|
||||||
"formik-material-ui": "3.0.1",
|
"formik-material-ui": "3.0.1",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"mantaray-js": "^1.0.3",
|
|
||||||
"material-ui-dropzone": "3.5.0",
|
"material-ui-dropzone": "3.5.0",
|
||||||
"notistack": "^3.0.1",
|
"notistack": "^3.0.1",
|
||||||
"opener": "1.5.2",
|
"opener": "1.5.2",
|
||||||
|
|||||||
+18
-21
@@ -12,7 +12,6 @@ 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,26 +44,24 @@ const App = ({
|
|||||||
>
|
>
|
||||||
<TopUpProvider>
|
<TopUpProvider>
|
||||||
<BeeProvider>
|
<BeeProvider>
|
||||||
<BalanceProvider>
|
<StampsProvider>
|
||||||
<StampsProvider>
|
<FileProvider>
|
||||||
<FileProvider>
|
<FeedsProvider>
|
||||||
<FeedsProvider>
|
<PlatformProvider>
|
||||||
<PlatformProvider>
|
<SnackbarProvider preventDuplicate anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}>
|
||||||
<SnackbarProvider preventDuplicate anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}>
|
<Router>
|
||||||
<Router>
|
<>
|
||||||
<>
|
<CssBaseline />
|
||||||
<CssBaseline />
|
<Dashboard errorReporting={errorReporting}>
|
||||||
<Dashboard errorReporting={errorReporting}>
|
<BaseRouter />
|
||||||
<BaseRouter />
|
</Dashboard>
|
||||||
</Dashboard>
|
</>
|
||||||
</>
|
</Router>
|
||||||
</Router>
|
</SnackbarProvider>
|
||||||
</SnackbarProvider>
|
</PlatformProvider>
|
||||||
</PlatformProvider>
|
</FeedsProvider>
|
||||||
</FeedsProvider>
|
</FileProvider>
|
||||||
</FileProvider>
|
</StampsProvider>
|
||||||
</StampsProvider>
|
|
||||||
</BalanceProvider>
|
|
||||||
</BeeProvider>
|
</BeeProvider>
|
||||||
</TopUpProvider>
|
</TopUpProvider>
|
||||||
</SettingsProvider>
|
</SettingsProvider>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { useSnackbar } from 'notistack'
|
|||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import Zap from 'remixicon-react/FlashlightLineIcon'
|
import Zap from 'remixicon-react/FlashlightLineIcon'
|
||||||
import { Context as SettingsContext } from '../providers/Settings'
|
import { Context as SettingsContext } from '../providers/Settings'
|
||||||
import EthereumAddress from './EthereumAddress'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
peerId: string
|
peerId: string
|
||||||
@@ -37,13 +36,9 @@ export default function CheckoutModal({ peerId, uncashedAmount }: Props): ReactE
|
|||||||
.cashoutLastCheque(peerId)
|
.cashoutLastCheque(peerId)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
enqueueSnackbar(
|
enqueueSnackbar(<span>Successfully cashed out cheque. Transaction {res.toHex()}</span>, {
|
||||||
<span>
|
variant: 'success',
|
||||||
Successfully cashed out cheque. Transaction
|
})
|
||||||
<EthereumAddress hideBlockie transaction address={res} />
|
|
||||||
</span>,
|
|
||||||
{ variant: 'success' },
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.catch((e: Error) => {
|
.catch((e: Error) => {
|
||||||
console.error(e) // eslint-disable-line
|
console.error(e) // eslint-disable-line
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Utils } from '@ethersphere/bee-js'
|
|
||||||
import { Typography } from '@material-ui/core/'
|
import { Typography } from '@material-ui/core/'
|
||||||
|
import { EthAddress } from '@ethersphere/bee-js'
|
||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
import Identicon from 'react-identicons'
|
import Identicon from 'react-identicons'
|
||||||
import { BLOCKCHAIN_EXPLORER_URL } from '../constants'
|
import { BLOCKCHAIN_EXPLORER_URL } from '../constants'
|
||||||
@@ -8,7 +8,7 @@ import { Flex } from './Flex'
|
|||||||
import QRCodeModal from './QRCodeModal'
|
import QRCodeModal from './QRCodeModal'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
address: string | undefined
|
address: EthAddress | undefined
|
||||||
hideBlockie?: boolean
|
hideBlockie?: boolean
|
||||||
transaction?: boolean
|
transaction?: boolean
|
||||||
truncate?: boolean
|
truncate?: boolean
|
||||||
@@ -21,7 +21,7 @@ export default function EthereumAddress(props: Props): ReactElement {
|
|||||||
<Flex>
|
<Flex>
|
||||||
{props.hideBlockie ? null : (
|
{props.hideBlockie ? null : (
|
||||||
<div style={{ paddingTop: '5px', marginRight: '10px' }}>
|
<div style={{ paddingTop: '5px', marginRight: '10px' }}>
|
||||||
<Identicon size={20} string={Utils.capitalizeAddressERC55(props.address)} />
|
<Identicon size={20} string={props.address.toChecksum()} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div>
|
<div>
|
||||||
@@ -45,8 +45,8 @@ export default function EthereumAddress(props: Props): ReactElement {
|
|||||||
{props.address}
|
{props.address}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<QRCodeModal value={Utils.capitalizeAddressERC55(props.address)} label={'Ethereum Address'} />
|
<QRCodeModal value={props.address.toChecksum()} label={'Ethereum Address'} />
|
||||||
<ClipboardCopy value={Utils.capitalizeAddressERC55(props.address)} />
|
<ClipboardCopy value={props.address.toChecksum()} />
|
||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
'-'
|
'-'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
|
||||||
import { Box, Divider, Drawer, Grid, List, Link as MUILink, Typography } from '@material-ui/core'
|
import { Box, Divider, Drawer, Grid, List, Link as MUILink, Typography } from '@material-ui/core'
|
||||||
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
|
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import FilesIcon from 'remixicon-react/ArrowUpDownLineIcon'
|
import FilesIcon from 'remixicon-react/ArrowUpDownLineIcon'
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Bee } from '@ethersphere/bee-js'
|
|
||||||
import { Box } from '@material-ui/core'
|
import { Box } from '@material-ui/core'
|
||||||
import Button from '@material-ui/core/Button'
|
import Button from '@material-ui/core/Button'
|
||||||
import Dialog from '@material-ui/core/Dialog'
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
@@ -6,6 +5,7 @@ import DialogActions from '@material-ui/core/DialogActions'
|
|||||||
import DialogContent from '@material-ui/core/DialogContent'
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import Input from '@material-ui/core/Input'
|
import Input from '@material-ui/core/Input'
|
||||||
|
import { BatchId, Bee } from '@ethersphere/bee-js'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, ReactNode, useState } from 'react'
|
import { ReactElement, ReactNode, useState } from 'react'
|
||||||
|
|
||||||
@@ -13,14 +13,14 @@ interface Props {
|
|||||||
type: 'Topup' | 'Dilute'
|
type: 'Topup' | 'Dilute'
|
||||||
icon: ReactNode
|
icon: ReactNode
|
||||||
bee: Bee
|
bee: Bee
|
||||||
stamp: string
|
stamp: BatchId
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function StampExtensionModal({ type, icon, bee, stamp }: Props): ReactElement {
|
export default function StampExtensionModal({ type, icon, bee, stamp }: Props): ReactElement {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [amount, setAmount] = useState('')
|
const [amount, setAmount] = useState('')
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
const label = `${type} ${stamp.substring(0, 8)}`
|
const label = `${type} ${stamp.toHex().substring(0, 8)}`
|
||||||
|
|
||||||
const handleClickOpen = (e: React.MouseEvent<HTMLButtonElement>) => {
|
const handleClickOpen = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||||
setOpen(true)
|
setOpen(true)
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
import { ReactElement, ReactNode, useState } from 'react'
|
|
||||||
import Button from '@material-ui/core/Button'
|
import Button from '@material-ui/core/Button'
|
||||||
import Input from '@material-ui/core/Input'
|
|
||||||
import Dialog from '@material-ui/core/Dialog'
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
import DialogActions from '@material-ui/core/DialogActions'
|
import DialogActions from '@material-ui/core/DialogActions'
|
||||||
import DialogContent from '@material-ui/core/DialogContent'
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import DialogContentText from '@material-ui/core/DialogContentText'
|
import DialogContentText from '@material-ui/core/DialogContentText'
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import FormHelperText from '@material-ui/core/FormHelperText'
|
import FormHelperText from '@material-ui/core/FormHelperText'
|
||||||
import { Token } from '../models/Token'
|
import Input from '@material-ui/core/Input'
|
||||||
import type { BigNumber } from 'bignumber.js'
|
import { BZZ, TransactionId } from '@ethersphere/bee-js'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
|
import { ReactElement, ReactNode, useState } from 'react'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
successMessage: string
|
successMessage: string
|
||||||
errorMessage: string
|
errorMessage: string
|
||||||
dialogMessage: string
|
dialogMessage: string
|
||||||
label: string
|
label: string
|
||||||
max?: BigNumber
|
max?: BZZ
|
||||||
min?: BigNumber
|
min?: BZZ
|
||||||
action: (amount: bigint) => Promise<string>
|
action: (amount: BZZ) => Promise<TransactionId>
|
||||||
icon?: ReactNode
|
icon?: ReactNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +33,7 @@ export default function WithdrawDepositModal({
|
|||||||
}: Props): ReactElement {
|
}: Props): ReactElement {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const [amount, setAmount] = useState('')
|
const [amount, setAmount] = useState('')
|
||||||
const [amountToken, setAmountToken] = useState<Token | null>(null)
|
const [amountToken, setAmountToken] = useState<BZZ | null>(null)
|
||||||
const [amountError, setAmountError] = useState<Error | null>(null)
|
const [amountError, setAmountError] = useState<Error | null>(null)
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
|
|
||||||
@@ -51,7 +50,7 @@ export default function WithdrawDepositModal({
|
|||||||
if (amountToken === null) return
|
if (amountToken === null) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const transactionHash = await action(amountToken.toBigInt as bigint)
|
const transactionHash = await action(amountToken)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
enqueueSnackbar(`${successMessage} Transaction ${transactionHash}`, { variant: 'success' })
|
enqueueSnackbar(`${successMessage} Transaction ${transactionHash}`, { variant: 'success' })
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -65,12 +64,12 @@ export default function WithdrawDepositModal({
|
|||||||
setAmount(value)
|
setAmount(value)
|
||||||
setAmountError(null)
|
setAmountError(null)
|
||||||
try {
|
try {
|
||||||
const t = Token.fromDecimal(value)
|
const t = BZZ.fromDecimalString(value)
|
||||||
setAmountToken(t)
|
setAmountToken(t)
|
||||||
|
|
||||||
if (min && t.toDecimal.isLessThan(min)) setAmountError(new Error(`Needs to be more than ${min}`))
|
if (min && t.lt(min)) setAmountError(new Error(`Needs to be more than ${min.toSignificantDigits(4)}`))
|
||||||
|
|
||||||
if (max && t.toDecimal.isGreaterThan(max)) setAmountError(new Error(`Needs to be less than ${max}`))
|
if (max && t.gt(max)) setAmountError(new Error(`Needs to be less than ${max.toSignificantDigits(4)}`))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setAmountError(e as Error)
|
setAmountError(e as Error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BigNumber } from 'bignumber.js'
|
import { BZZ } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import Download from 'remixicon-react/DownloadLineIcon'
|
import Download from 'remixicon-react/DownloadLineIcon'
|
||||||
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
||||||
@@ -16,13 +16,13 @@ export default function DepositModal(): ReactElement {
|
|||||||
dialogMessage="Amount of xBZZ to deposit to the checkbook, from your node."
|
dialogMessage="Amount of xBZZ to deposit to the checkbook, from your node."
|
||||||
label="Deposit"
|
label="Deposit"
|
||||||
icon={<Download size="1rem" />}
|
icon={<Download size="1rem" />}
|
||||||
min={new BigNumber(0)}
|
min={BZZ.fromPLUR('1')}
|
||||||
action={async (amount: bigint) => {
|
action={async (amount: BZZ) => {
|
||||||
if (!beeApi) {
|
if (!beeApi) {
|
||||||
throw new Error('Bee URL is not valid')
|
throw new Error('Bee URL is not valid')
|
||||||
}
|
}
|
||||||
|
|
||||||
const transactionHash = await beeApi.depositTokens(amount.toString())
|
const transactionHash = await beeApi.depositTokens(amount)
|
||||||
refresh()
|
refresh()
|
||||||
|
|
||||||
return transactionHash
|
return transactionHash
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BigNumber } from 'bignumber.js'
|
import { BZZ } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import Download from 'remixicon-react/DownloadLineIcon'
|
import Download from 'remixicon-react/DownloadLineIcon'
|
||||||
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
||||||
@@ -21,8 +21,8 @@ export default function StakeModal({ onStarted, onFinished }: Props): ReactEleme
|
|||||||
dialogMessage="Specify the amount of xBZZ you would like to stake. Your first stake must be at least 10 xBZZ. This will lock your tokens."
|
dialogMessage="Specify the amount of xBZZ you would like to stake. Your first stake must be at least 10 xBZZ. This will lock your tokens."
|
||||||
label="Stake"
|
label="Stake"
|
||||||
icon={<Download size="1rem" />}
|
icon={<Download size="1rem" />}
|
||||||
min={new BigNumber(0)}
|
min={BZZ.fromPLUR('1')}
|
||||||
action={async (amount: bigint) => {
|
action={async (amount: BZZ) => {
|
||||||
if (!beeApi) {
|
if (!beeApi) {
|
||||||
throw new Error('Bee URL is not valid')
|
throw new Error('Bee URL is not valid')
|
||||||
}
|
}
|
||||||
@@ -30,13 +30,13 @@ export default function StakeModal({ onStarted, onFinished }: Props): ReactEleme
|
|||||||
onStarted()
|
onStarted()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await beeApi.depositStake(amount.toString())
|
const transactionHash = await beeApi.depositStake(amount)
|
||||||
|
|
||||||
|
return transactionHash
|
||||||
} finally {
|
} finally {
|
||||||
refresh()
|
refresh()
|
||||||
onFinished()
|
onFinished()
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'unknown'
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BigNumber } from 'bignumber.js'
|
import { BZZ } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import Upload from 'remixicon-react/UploadLineIcon'
|
import Upload from 'remixicon-react/UploadLineIcon'
|
||||||
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
import WithdrawDepositModal from '../components/WithdrawDepositModal'
|
||||||
@@ -16,13 +16,13 @@ export default function WithdrawModal(): ReactElement {
|
|||||||
dialogMessage="Amount of xBZZ to withdraw from the checkbook to your node."
|
dialogMessage="Amount of xBZZ to withdraw from the checkbook to your node."
|
||||||
label="Withdraw"
|
label="Withdraw"
|
||||||
icon={<Upload size="1rem" />}
|
icon={<Upload size="1rem" />}
|
||||||
min={new BigNumber(0)}
|
min={BZZ.fromPLUR('1')}
|
||||||
action={async (amount: bigint) => {
|
action={async (amount: BZZ) => {
|
||||||
if (!beeApi) {
|
if (!beeApi) {
|
||||||
throw new Error('Bee URL is not valid')
|
throw new Error('Bee URL is not valid')
|
||||||
}
|
}
|
||||||
|
|
||||||
const transactionHash = await beeApi.withdrawTokens(amount.toString())
|
const transactionHash = await beeApi.withdrawTokens(amount)
|
||||||
refresh()
|
refresh()
|
||||||
|
|
||||||
return transactionHash
|
return transactionHash
|
||||||
|
|||||||
+23
-25
@@ -1,22 +1,20 @@
|
|||||||
import { Bee, LastCashoutActionResponse } from '@ethersphere/bee-js'
|
import { AllSettlements, Bee, BZZ, LastCashoutActionResponse, PeerBalance, Settlements } from '@ethersphere/bee-js'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Token } from '../models/Token'
|
|
||||||
import { Balance, Settlement, Settlements } from '../types'
|
|
||||||
import { makeRetriablePromise, unwrapPromiseSettlements } from '../utils'
|
import { makeRetriablePromise, unwrapPromiseSettlements } from '../utils'
|
||||||
|
|
||||||
interface UseAccountingHook {
|
interface UseAccountingHook {
|
||||||
isLoadingUncashed: boolean
|
isLoadingUncashed: boolean
|
||||||
totalUncashed: Token
|
totalUncashed: BZZ
|
||||||
accounting: Accounting[] | null
|
accounting: Accounting[] | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Accounting {
|
export interface Accounting {
|
||||||
peer: string
|
peer: string
|
||||||
uncashedAmount: Token
|
uncashedAmount: BZZ
|
||||||
balance: Token
|
balance: BZZ
|
||||||
received: Token
|
received: BZZ
|
||||||
sent: Token
|
sent: BZZ
|
||||||
total: Token
|
total: BZZ
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,8 +27,8 @@ export interface Accounting {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
function mergeAccounting(
|
function mergeAccounting(
|
||||||
balances: Balance[] | null,
|
balances: PeerBalance[] | null,
|
||||||
settlements?: Settlement[],
|
settlements?: Settlements[],
|
||||||
uncashedAmounts?: LastCashoutActionResponse[],
|
uncashedAmounts?: LastCashoutActionResponse[],
|
||||||
): Accounting[] | null {
|
): Accounting[] | null {
|
||||||
// Settlements or balances are still loading or there is an error -> return null
|
// Settlements or balances are still loading or there is an error -> return null
|
||||||
@@ -44,9 +42,9 @@ function mergeAccounting(
|
|||||||
(accounting[peer] = {
|
(accounting[peer] = {
|
||||||
peer,
|
peer,
|
||||||
balance,
|
balance,
|
||||||
sent: new Token('0'),
|
sent: BZZ.fromPLUR('0'),
|
||||||
received: new Token('0'),
|
received: BZZ.fromPLUR('0'),
|
||||||
uncashedAmount: new Token('0'),
|
uncashedAmount: BZZ.fromPLUR('0'),
|
||||||
total: balance,
|
total: balance,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@@ -57,7 +55,7 @@ function mergeAccounting(
|
|||||||
...accounting[peer],
|
...accounting[peer],
|
||||||
sent,
|
sent,
|
||||||
received,
|
received,
|
||||||
total: new Token(accounting[peer].balance.toBigNumber.plus(received.toBigNumber).minus(sent.toBigNumber)),
|
total: accounting[peer].balance.plus(received).minus(sent),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -65,14 +63,16 @@ function mergeAccounting(
|
|||||||
if (!uncashedAmounts) return Object.values(accounting).sort((a, b) => (a.peer < b.peer ? -1 : 1))
|
if (!uncashedAmounts) return Object.values(accounting).sort((a, b) => (a.peer < b.peer ? -1 : 1))
|
||||||
|
|
||||||
uncashedAmounts?.forEach(({ peer, uncashedAmount }) => {
|
uncashedAmounts?.forEach(({ peer, uncashedAmount }) => {
|
||||||
accounting[peer].uncashedAmount = new Token(uncashedAmount)
|
accounting[peer].uncashedAmount = uncashedAmount
|
||||||
})
|
})
|
||||||
|
|
||||||
// Return sorted by the uncashed amount first and then by the peer id
|
// Return sorted by the uncashed amount first and then by the peer id
|
||||||
return Object.values(accounting).sort((a, b) => {
|
return Object.values(accounting).sort((a, b) => {
|
||||||
const diff = b.uncashedAmount.toBigNumber.minus(a.uncashedAmount.toBigNumber).toNumber()
|
const diff = Number(b.uncashedAmount.minus(a.uncashedAmount))
|
||||||
|
|
||||||
if (diff !== 0) return diff
|
if (diff !== 0) {
|
||||||
|
return diff
|
||||||
|
}
|
||||||
|
|
||||||
return a.peer < b.peer ? -1 : 1
|
return a.peer < b.peer ? -1 : 1
|
||||||
})
|
})
|
||||||
@@ -80,8 +80,8 @@ function mergeAccounting(
|
|||||||
|
|
||||||
export const useAccounting = (
|
export const useAccounting = (
|
||||||
beeApi: Bee | null,
|
beeApi: Bee | null,
|
||||||
settlements: Settlements | null,
|
settlements: AllSettlements | null,
|
||||||
balances: Balance[] | null,
|
balances: PeerBalance[] | null,
|
||||||
): UseAccountingHook => {
|
): UseAccountingHook => {
|
||||||
const [isLoadingUncashed, setIsloadingUncashed] = useState<boolean>(false)
|
const [isLoadingUncashed, setIsloadingUncashed] = useState<boolean>(false)
|
||||||
const [uncashedAmounts, setUncashedAmounts] = useState<LastCashoutActionResponse[] | undefined>(undefined)
|
const [uncashedAmounts, setUncashedAmounts] = useState<LastCashoutActionResponse[] | undefined>(undefined)
|
||||||
@@ -92,7 +92,7 @@ export const useAccounting = (
|
|||||||
|
|
||||||
setIsloadingUncashed(true)
|
setIsloadingUncashed(true)
|
||||||
const promises = settlements.settlements
|
const promises = settlements.settlements
|
||||||
.filter(({ received }) => received.toBigNumber.gt('0'))
|
.filter(({ received }) => received.gt(BZZ.fromPLUR('0')))
|
||||||
.map(({ peer }) => makeRetriablePromise(() => beeApi.getLastCashoutAction(peer)))
|
.map(({ peer }) => makeRetriablePromise(() => beeApi.getLastCashoutAction(peer)))
|
||||||
|
|
||||||
Promise.allSettled(promises).then(settlements => {
|
Promise.allSettled(promises).then(settlements => {
|
||||||
@@ -104,10 +104,8 @@ export const useAccounting = (
|
|||||||
|
|
||||||
const accounting = mergeAccounting(balances, settlements?.settlements, uncashedAmounts)
|
const accounting = mergeAccounting(balances, settlements?.settlements, uncashedAmounts)
|
||||||
|
|
||||||
let totalUncashed: Token = new Token('0')
|
let totalUncashed = BZZ.fromPLUR('0')
|
||||||
accounting?.forEach(
|
accounting?.forEach(({ uncashedAmount }) => (totalUncashed = totalUncashed.plus(uncashedAmount)))
|
||||||
({ uncashedAmount }) => (totalUncashed = new Token(totalUncashed.toBigNumber.plus(uncashedAmount.toBigNumber))),
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isLoadingUncashed,
|
isLoadingUncashed,
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { BigNumber } from 'bignumber.js'
|
|
||||||
import { Token } from './Token'
|
|
||||||
|
|
||||||
export const BZZ_DECIMAL_PLACES = 16
|
|
||||||
|
|
||||||
export class BzzToken extends Token {
|
|
||||||
constructor(value: BigNumber | string | bigint) {
|
|
||||||
super(value, BZZ_DECIMAL_PLACES)
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromDecimal(value: BigNumber | string | bigint): BzzToken {
|
|
||||||
return Token.fromDecimal(value, BZZ_DECIMAL_PLACES)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { BigNumber } from 'bignumber.js'
|
|
||||||
import { Token } from './Token'
|
|
||||||
|
|
||||||
const DAI_DECIMAL_PLACES = 18
|
|
||||||
|
|
||||||
export class DaiToken extends Token {
|
|
||||||
constructor(value: BigNumber | string | bigint) {
|
|
||||||
super(value, DAI_DECIMAL_PLACES)
|
|
||||||
}
|
|
||||||
|
|
||||||
static fromDecimal(value: BigNumber | string | bigint): DaiToken {
|
|
||||||
return Token.fromDecimal(value, DAI_DECIMAL_PLACES)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import BigNumber from 'bignumber.js'
|
|
||||||
import { Token } from './Token'
|
|
||||||
|
|
||||||
describe('models/Token', () => {
|
|
||||||
describe('Token.fromDecimal', () => {
|
|
||||||
const values = [
|
|
||||||
{ bzz: '0', baseUnits: '0' },
|
|
||||||
{ bzz: '0.1', baseUnits: BigInt('1000000000000000') },
|
|
||||||
{ bzz: '9.9', baseUnits: BigInt('99000000000000000') },
|
|
||||||
]
|
|
||||||
|
|
||||||
// Test with default 16 decimal places
|
|
||||||
values.forEach(({ bzz, baseUnits }) => {
|
|
||||||
test(`converting ${bzz} => ${baseUnits}`, () => {
|
|
||||||
expect(Token.fromDecimal(bzz).toBigNumber.eq(baseUnits.toString())).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// Test with 0 decimal places
|
|
||||||
values.forEach(({ baseUnits }) => {
|
|
||||||
test(`converting ${baseUnits} => ${baseUnits} with 0 decimals`, () => {
|
|
||||||
expect(Token.fromDecimal(baseUnits, 0).toBigNumber.eq(baseUnits.toString())).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('new Token', () => {
|
|
||||||
const cs = ['0', '1234567890', '99000000000000000']
|
|
||||||
const correctValues = [...cs, ...cs.map(BigInt), ...cs.map(v => new BigNumber(v))]
|
|
||||||
|
|
||||||
correctValues.forEach(v => {
|
|
||||||
test(`New Token ${v} of type ${typeof v}`, () => {
|
|
||||||
const t = new Token(v)
|
|
||||||
|
|
||||||
expect(t.toBigNumber.eq(v.toString())).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
import { BigNumber } from 'bignumber.js'
|
|
||||||
import { isInteger, makeBigNumber } from '../utils'
|
|
||||||
|
|
||||||
const POSSIBLE_DECIMALS = [18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
|
|
||||||
type digits = typeof POSSIBLE_DECIMALS[number]
|
|
||||||
|
|
||||||
const BZZ_DECIMALS = 16
|
|
||||||
|
|
||||||
export class Token {
|
|
||||||
private amount: BigNumber // Represented in the base units, so it is always an integer value
|
|
||||||
private readonly decimals: digits
|
|
||||||
|
|
||||||
constructor(amount: BigNumber | string | bigint, decimals: digits = BZZ_DECIMALS) {
|
|
||||||
const a = makeBigNumber(amount)
|
|
||||||
|
|
||||||
if (!isInteger(a) || !POSSIBLE_DECIMALS.includes(decimals)) {
|
|
||||||
throw new TypeError(`Not a valid token values: ${amount} ${decimals}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.amount = a
|
|
||||||
this.decimals = decimals
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct new Token from a digit representation
|
|
||||||
*
|
|
||||||
* @param amount Amount of a token in the digits (1 token = 10^decimals)
|
|
||||||
* @param decimals Number of decimals for the token (must be integer)
|
|
||||||
*
|
|
||||||
* @throws {TypeError} If the decimals is not an integer or the amount after conversion is not an integer
|
|
||||||
*
|
|
||||||
* @returns new Token
|
|
||||||
*/
|
|
||||||
static fromDecimal(amount: BigNumber | string | bigint, decimals: digits = BZZ_DECIMALS): Token | never {
|
|
||||||
const a = makeBigNumber(amount)
|
|
||||||
|
|
||||||
// No need to do any validation here, it is done when the new token is created
|
|
||||||
const t = a.multipliedBy(new BigNumber(10).pow(decimals))
|
|
||||||
|
|
||||||
return new Token(t, decimals)
|
|
||||||
}
|
|
||||||
|
|
||||||
get toBigInt(): bigint {
|
|
||||||
return BigInt(this.amount.toFixed(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
get toString(): string {
|
|
||||||
return this.amount.toFixed(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
get toBigNumber(): BigNumber {
|
|
||||||
return new BigNumber(this.amount)
|
|
||||||
}
|
|
||||||
|
|
||||||
get toDecimal(): BigNumber {
|
|
||||||
return this.amount.dividedBy(new BigNumber(10).pow(this.decimals))
|
|
||||||
}
|
|
||||||
|
|
||||||
toFixedDecimal(digits = 7): string {
|
|
||||||
return this.toDecimal.toFixed(digits)
|
|
||||||
}
|
|
||||||
|
|
||||||
toSignificantDigits(digits = 4): string {
|
|
||||||
const asString = this.toDecimal.toFixed(this.decimals)
|
|
||||||
|
|
||||||
let indexOfSignificantDigit = -1
|
|
||||||
let reachedDecimalPoint = false
|
|
||||||
|
|
||||||
for (let i = 0; i < asString.length; i++) {
|
|
||||||
const char = asString[i]
|
|
||||||
|
|
||||||
if (char === '.') {
|
|
||||||
reachedDecimalPoint = true
|
|
||||||
indexOfSignificantDigit = i + 1
|
|
||||||
} else if (reachedDecimalPoint && char !== '0') {
|
|
||||||
indexOfSignificantDigit = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return asString.slice(0, indexOfSignificantDigit + digits)
|
|
||||||
}
|
|
||||||
|
|
||||||
minusBaseUnits(amount: string | BigNumber | bigint): Token {
|
|
||||||
const baseUnits = makeBigNumber(amount)
|
|
||||||
|
|
||||||
return new Token(
|
|
||||||
this.toBigNumber.minus(baseUnits.multipliedBy(new BigNumber(10).pow(this.decimals))),
|
|
||||||
this.decimals,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
plusBaseUnits(amount: string | BigNumber | bigint): Token {
|
|
||||||
const baseUnits = makeBigNumber(amount)
|
|
||||||
|
|
||||||
return new Token(this.toBigNumber.plus(baseUnits.multipliedBy(new BigNumber(10).pow(this.decimals))), this.decimals)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
|
||||||
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
|
import { createStyles, makeStyles, Tab, Tabs, Theme } from '@material-ui/core'
|
||||||
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Context } from '../../providers/Bee'
|
import { Context } from '../../providers/Bee'
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { Utils } from '@ethersphere/bee-js'
|
|
||||||
import { Box } from '@material-ui/core'
|
import { Box } from '@material-ui/core'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import ExpandableList from '../../../components/ExpandableList'
|
import ExpandableList from '../../../components/ExpandableList'
|
||||||
@@ -35,20 +34,20 @@ export function AccountChequebook(): ReactElement {
|
|||||||
<ExpandableList label="Chequebook" defaultOpen>
|
<ExpandableList label="Chequebook" defaultOpen>
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label="Total Balance"
|
label="Total Balance"
|
||||||
value={`${chequebookBalance?.totalBalance.toFixedDecimal()} xBZZ`}
|
value={`${chequebookBalance?.totalBalance.toSignificantDigits(4)} xBZZ`}
|
||||||
/>
|
/>
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label="Available Uncommitted Balance"
|
label="Available Uncommitted Balance"
|
||||||
value={`${chequebookBalance?.availableBalance.toFixedDecimal()} xBZZ`}
|
value={`${chequebookBalance?.availableBalance.toSignificantDigits(4)} xBZZ`}
|
||||||
/>
|
/>
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label="Total Cheques Amount Sent"
|
label="Total Cheques Amount Sent"
|
||||||
value={`${settlements?.totalSent.toFixedDecimal()} xBZZ`}
|
value={`${settlements?.totalSent.toSignificantDigits(4)} xBZZ`}
|
||||||
/>
|
/>
|
||||||
<Box mb={2}>
|
<Box mb={2}>
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label="Total Cheques Amount Received"
|
label="Total Cheques Amount Received"
|
||||||
value={`${settlements?.totalReceived.toFixedDecimal()} xBZZ`}
|
value={`${settlements?.totalReceived.toSignificantDigits(4)} xBZZ`}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
<ExpandableListItemActions>
|
<ExpandableListItemActions>
|
||||||
@@ -60,7 +59,7 @@ export function AccountChequebook(): ReactElement {
|
|||||||
<ExpandableList label="Blockchain" defaultOpen>
|
<ExpandableList label="Blockchain" defaultOpen>
|
||||||
<ExpandableListItemKey
|
<ExpandableListItemKey
|
||||||
label="Ethereum address"
|
label="Ethereum address"
|
||||||
value={nodeAddresses?.ethereum ? Utils.capitalizeAddressERC55(nodeAddresses.ethereum) : ''}
|
value={nodeAddresses?.ethereum ? nodeAddresses.ethereum.toChecksum() : ''}
|
||||||
/>
|
/>
|
||||||
<ExpandableListItemKey
|
<ExpandableListItemKey
|
||||||
label="Chequebook contract address"
|
label="Chequebook contract address"
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import { Box } from '@material-ui/core'
|
import { Box } from '@material-ui/core'
|
||||||
|
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import Download from 'remixicon-react/Download2LineIcon'
|
import { useNavigate } from 'react-router'
|
||||||
import Info from 'remixicon-react/InformationLineIcon'
|
|
||||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||||
import Trash from 'remixicon-react/DeleteBin7LineIcon'
|
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 ExpandableList from '../../../components/ExpandableList'
|
||||||
import ExpandableListItem from '../../../components/ExpandableListItem'
|
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||||
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
||||||
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
|
||||||
import { SwarmButton } from '../../../components/SwarmButton'
|
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 { ROUTES } from '../../../routes'
|
||||||
import { formatEnum } from '../../../utils'
|
import { formatEnum } from '../../../utils'
|
||||||
import { persistIdentitiesWithoutUpdate } from '../../../utils/identity'
|
import { persistIdentitiesWithoutUpdate } from '../../../utils/identity'
|
||||||
@@ -19,8 +22,6 @@ import { ExportFeedDialog } from '../../feeds/ExportFeedDialog'
|
|||||||
import { ImportFeedDialog } from '../../feeds/ImportFeedDialog'
|
import { ImportFeedDialog } from '../../feeds/ImportFeedDialog'
|
||||||
import { AccountNavigation } from '../AccountNavigation'
|
import { AccountNavigation } from '../AccountNavigation'
|
||||||
import { Header } from '../Header'
|
import { Header } from '../Header'
|
||||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
|
||||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
|
||||||
|
|
||||||
export function AccountFeeds(): ReactElement {
|
export function AccountFeeds(): ReactElement {
|
||||||
const { identities, setIdentities } = useContext(IdentityContext)
|
const { identities, setIdentities } = useContext(IdentityContext)
|
||||||
@@ -98,7 +99,7 @@ export function AccountFeeds(): ReactElement {
|
|||||||
<ExpandableListItem label="Identity type" value={formatEnum(x.type)} />
|
<ExpandableListItem label="Identity type" value={formatEnum(x.type)} />
|
||||||
</ExpandableList>
|
</ExpandableList>
|
||||||
</Box>
|
</Box>
|
||||||
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
|
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
|
||||||
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
|
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
|
||||||
<Box mt={0.75}>
|
<Box mt={0.75}>
|
||||||
<ExpandableListItemActions>
|
<ExpandableListItemActions>
|
||||||
|
|||||||
@@ -5,16 +5,14 @@ import ExpandableListItemActions from '../../../components/ExpandableListItemAct
|
|||||||
import { Loading } from '../../../components/Loading'
|
import { Loading } from '../../../components/Loading'
|
||||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||||
import StakeModal from '../../../containers/StakeModal'
|
import StakeModal from '../../../containers/StakeModal'
|
||||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||||
import { Context as BalanceContext } from '../../../providers/WalletBalance'
|
|
||||||
import { AccountNavigation } from '../AccountNavigation'
|
import { AccountNavigation } from '../AccountNavigation'
|
||||||
import { Header } from '../Header'
|
import { Header } from '../Header'
|
||||||
|
|
||||||
export function AccountStaking(): ReactElement {
|
export function AccountStaking(): ReactElement {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const { status, stake } = useContext(BeeContext)
|
const { status, stake, walletBalance } = useContext(BeeContext)
|
||||||
const { balance } = useContext(BalanceContext)
|
|
||||||
|
|
||||||
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
|
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
|
||||||
|
|
||||||
@@ -31,15 +29,15 @@ export function AccountStaking(): ReactElement {
|
|||||||
<Header />
|
<Header />
|
||||||
<AccountNavigation active="STAKING" />
|
<AccountNavigation active="STAKING" />
|
||||||
<div>
|
<div>
|
||||||
{loading || stake?.toDecimal === undefined ? (
|
{loading || !stake ? (
|
||||||
<Loading />
|
<Loading />
|
||||||
) : (
|
) : (
|
||||||
<ExpandableList label="Staking" defaultOpen>
|
<ExpandableList label="Staking" defaultOpen>
|
||||||
<ExpandableListItem label="Staked BZZ" value={`${stake?.toSignificantDigits()} xBZZ`} />
|
<ExpandableListItem label="Staked BZZ" value={`${stake?.toSignificantDigits(4)} xBZZ`} />
|
||||||
{balance?.bzz ? (
|
{walletBalance?.bzzBalance ? (
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label="Available xBZZ balance"
|
label="Available xBZZ balance"
|
||||||
value={`${balance?.bzz.toSignificantDigits(4)} xBZZ`}
|
value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<ExpandableListItemActions>
|
<ExpandableListItemActions>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BeeModes, Utils } from '@ethersphere/bee-js'
|
|
||||||
import { Box, Grid, Typography } from '@material-ui/core'
|
import { Box, Grid, Typography } from '@material-ui/core'
|
||||||
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext } from 'react'
|
import { ReactElement, useContext } from 'react'
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
import Download from 'remixicon-react/DownloadLineIcon'
|
import Download from 'remixicon-react/DownloadLineIcon'
|
||||||
@@ -13,15 +13,13 @@ 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 { AccountNavigation } from '../AccountNavigation'
|
import { AccountNavigation } from '../AccountNavigation'
|
||||||
import { Header } from '../Header'
|
import { Header } from '../Header'
|
||||||
|
|
||||||
export function AccountWallet(): ReactElement {
|
export function AccountWallet(): ReactElement {
|
||||||
const { nodeAddresses, nodeInfo, status } = useContext(BeeContext)
|
const { nodeAddresses, nodeInfo, status, walletBalance } = useContext(BeeContext)
|
||||||
const { isDesktop } = useContext(SettingsContext)
|
const { isDesktop } = useContext(SettingsContext)
|
||||||
const { balance } = useContext(BalanceProvider)
|
|
||||||
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
@@ -53,20 +51,22 @@ export function AccountWallet(): ReactElement {
|
|||||||
)}
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
{balance && nodeAddresses ? (
|
{walletBalance && nodeAddresses ? (
|
||||||
<>
|
<>
|
||||||
<Box mb={0.25}>
|
<Box mb={0.25}>
|
||||||
<ExpandableListItemKey
|
<ExpandableListItemKey label="Node wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded />
|
||||||
label="Node wallet address"
|
|
||||||
value={Utils.capitalizeAddressERC55(nodeAddresses.ethereum)}
|
|
||||||
expanded
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={0.25}>
|
<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>
|
||||||
<Box mb={2}>
|
<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>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { BZZ } from '@ethersphere/bee-js'
|
||||||
import type { ReactElement } from 'react'
|
import type { ReactElement } from 'react'
|
||||||
import CashoutModal from '../../components/CashoutModal'
|
import CashoutModal from '../../components/CashoutModal'
|
||||||
import ExpandableList from '../../components/ExpandableList'
|
import ExpandableList from '../../components/ExpandableList'
|
||||||
@@ -5,44 +6,43 @@ import ExpandableListItem from '../../components/ExpandableListItem'
|
|||||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
import { Accounting } from '../../hooks/accounting'
|
import { Accounting } from '../../hooks/accounting'
|
||||||
import type { Token } from '../../models/Token'
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLoadingUncashed: boolean
|
isLoadingUncashed: boolean
|
||||||
totalUncashed: Token
|
totalUncashed: BZZ
|
||||||
accounting: Accounting[] | null
|
accounting: Accounting[] | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PeerBalances({ accounting, isLoadingUncashed, totalUncashed }: Props): ReactElement | 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 (
|
return (
|
||||||
<ExpandableList
|
<ExpandableList
|
||||||
label={`Peers (${uncashedPeers.length})`}
|
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 }) => (
|
{uncashedPeers.map(({ peer, balance, received, sent, uncashedAmount, total }) => (
|
||||||
<ExpandableList
|
<ExpandableList
|
||||||
key={peer}
|
key={peer}
|
||||||
label={`Peer ${peer.slice(0, 8)}[…]`}
|
label={`Peer ${peer.slice(0, 8)}[…]`}
|
||||||
level={1}
|
level={1}
|
||||||
info={`${uncashedAmount.toFixedDecimal()} xBZZ (uncashed)`}
|
info={`${uncashedAmount.toSignificantDigits(4)} xBZZ (uncashed)`}
|
||||||
>
|
>
|
||||||
<ExpandableListItemKey label="Peer ID" value={peer} />
|
<ExpandableListItemKey label="Peer ID" value={peer} />
|
||||||
<ExpandableListItem label="Outstanding Balance" value={`${balance.toFixedDecimal()} xBZZ`} />
|
<ExpandableListItem label="Outstanding Balance" value={`${balance.toSignificantDigits(4)} xBZZ`} />
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label="Settlements Sent / Received"
|
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
|
<ExpandableListItem
|
||||||
label="Uncashed Amount"
|
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>
|
<ExpandableListItemActions>
|
||||||
<CashoutModal uncashedAmount={uncashedAmount.toFixedDecimal()} peerId={peer} />
|
<CashoutModal uncashedAmount={uncashedAmount.toSignificantDigits(4)} peerId={peer} />
|
||||||
</ExpandableListItemActions>
|
</ExpandableListItemActions>
|
||||||
)}
|
)}
|
||||||
</ExpandableList>
|
</ExpandableList>
|
||||||
|
|||||||
+13
-7
@@ -1,14 +1,13 @@
|
|||||||
import { Bee } from '@ethersphere/bee-js'
|
|
||||||
import { FdpStorage } from '@fairdatasociety/fdp-storage'
|
import { FdpStorage } from '@fairdatasociety/fdp-storage'
|
||||||
import { Pod } from '@fairdatasociety/fdp-storage/dist/pod/types'
|
import { Pod } from '@fairdatasociety/fdp-storage/dist/pod/types'
|
||||||
import { CircularProgress, Typography } from '@material-ui/core'
|
import { CircularProgress, Typography } from '@material-ui/core'
|
||||||
|
import { Bee, MantarayNode } from '@ethersphere/bee-js'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useEffect, useState } from 'react'
|
import { ReactElement, useEffect, useState } from 'react'
|
||||||
import ImportIcon from 'remixicon-react/AddBoxLineIcon'
|
import ImportIcon from 'remixicon-react/AddBoxLineIcon'
|
||||||
import PlusCircle from 'remixicon-react/AddCircleLineIcon'
|
import PlusCircle from 'remixicon-react/AddCircleLineIcon'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
import { joinUrl } from '../../react-fs/Utility'
|
import { joinUrl } from '../../react-fs/Utility'
|
||||||
import { ManifestJs } from '../../utils/manifest'
|
|
||||||
import { FdpLogin } from './FdpLogin'
|
import { FdpLogin } from './FdpLogin'
|
||||||
import { FdpPods } from './FdpPods'
|
import { FdpPods } from './FdpPods'
|
||||||
import { Horizontal } from './Horizontal'
|
import { Horizontal } from './Horizontal'
|
||||||
@@ -25,7 +24,9 @@ async function makeFdp(): Promise<FdpStorage | null> {
|
|||||||
return 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: {
|
ensOptions: {
|
||||||
rpcUrl: sepolia,
|
rpcUrl: sepolia,
|
||||||
contractAddresses: {
|
contractAddresses: {
|
||||||
@@ -120,11 +121,16 @@ export default function FDP(): ReactElement {
|
|||||||
}
|
}
|
||||||
setCreatingPod(true)
|
setCreatingPod(true)
|
||||||
const bee = new Bee('http://localhost:1633')
|
const bee = new Bee('http://localhost:1633')
|
||||||
const manifestJs = new ManifestJs(bee)
|
const manifest = await MantarayNode.unmarshal(bee, importHash)
|
||||||
const entries = await manifestJs.getHashes(importHash)
|
await manifest.loadRecursively(bee)
|
||||||
|
const nodes = manifest.collect()
|
||||||
await fdp.personalStorage.create(name)
|
await fdp.personalStorage.create(name)
|
||||||
for (const [path, hash] of Object.entries(entries)) {
|
for (const node of nodes) {
|
||||||
await fdp.file.uploadData(name, joinUrl('/', path), await bee.downloadData(hash))
|
await fdp.file.uploadData(
|
||||||
|
name,
|
||||||
|
joinUrl('/', node.fullPathString),
|
||||||
|
(await bee.downloadData(node.targetAddress)).toUint8Array(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
const pods = await fdp.personalStorage.list()
|
const pods = await fdp.personalStorage.list()
|
||||||
setPods(pods.pods)
|
setPods(pods.pods)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Box, Grid, Typography } from '@material-ui/core'
|
import { Box, Grid, Typography } from '@material-ui/core'
|
||||||
|
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||||
import { Form, Formik } from 'formik'
|
import { Form, Formik } from 'formik'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
@@ -108,7 +109,7 @@ export default function CreateNewFeed(): ReactElement {
|
|||||||
</Box>
|
</Box>
|
||||||
{values.type === 'V3' && <SwarmTextInput name="password" label="Password" password formik />}
|
{values.type === 'V3' && <SwarmTextInput name="password" label="Password" password formik />}
|
||||||
<Box mt={2}>
|
<Box mt={2}>
|
||||||
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
|
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
|
||||||
</Box>
|
</Box>
|
||||||
<Box mt={2} sx={{ bgcolor: '#fcf2e8' }} p={2}>
|
<Box mt={2} sx={{ bgcolor: '#fcf2e8' }} p={2}>
|
||||||
<Grid container justifyContent="space-between">
|
<Grid container justifyContent="space-between">
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ export default function UpdateFeed(): ReactElement {
|
|||||||
<Grid container>
|
<Grid container>
|
||||||
{stamps ? (
|
{stamps ? (
|
||||||
<SwarmSelect
|
<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}
|
onChange={onStampChange}
|
||||||
label="Stamp"
|
label="Stamp"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import { Box, Typography } from '@material-ui/core'
|
import { Box, Typography } from '@material-ui/core'
|
||||||
|
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
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 { 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 ExpandableList from '../../components/ExpandableList'
|
||||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
import { Context as BeeContext, CheckState } from '../../providers/Bee'
|
||||||
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
|
import { Identity, Context as IdentityContext } from '../../providers/Feeds'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { formatEnum } from '../../utils'
|
import { formatEnum } from '../../utils'
|
||||||
import { persistIdentitiesWithoutUpdate } from '../../utils/identity'
|
import { persistIdentitiesWithoutUpdate } from '../../utils/identity'
|
||||||
@@ -97,7 +98,7 @@ export default function Feeds(): ReactElement {
|
|||||||
<ExpandableListItem label="Identity type" value={formatEnum(x.type)} />
|
<ExpandableListItem label="Identity type" value={formatEnum(x.type)} />
|
||||||
</ExpandableList>
|
</ExpandableList>
|
||||||
</Box>
|
</Box>
|
||||||
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
|
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
|
||||||
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
|
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
|
||||||
<Box mt={0.75}>
|
<Box mt={0.75}>
|
||||||
<ExpandableListItemActions>
|
<ExpandableListItemActions>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Utils } from '@ethersphere/bee-js'
|
|
||||||
import { Box } from '@material-ui/core'
|
import { Box } from '@material-ui/core'
|
||||||
|
import { Reference } from '@ethersphere/bee-js'
|
||||||
import { ReactElement } from 'react'
|
import { ReactElement } from 'react'
|
||||||
import { DocumentationText } from '../../components/DocumentationText'
|
import { DocumentationText } from '../../components/DocumentationText'
|
||||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||||
@@ -11,7 +11,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function AssetSummary({ isWebsite, reference }: Props): ReactElement {
|
export function AssetSummary({ isWebsite, reference }: Props): ReactElement {
|
||||||
const isHash = Utils.isHexString(reference) && reference.length === 64
|
const isHash = Reference.isValid(reference)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
|
||||||
import { Box } from '@material-ui/core'
|
import { Box } from '@material-ui/core'
|
||||||
|
import { Tag } from '@ethersphere/bee-js'
|
||||||
import { ReactElement, useContext, useEffect, useRef, useState } from 'react'
|
import { ReactElement, useContext, useEffect, useRef, useState } from 'react'
|
||||||
import { DocumentationText } from '../../components/DocumentationText'
|
import { DocumentationText } from '../../components/DocumentationText'
|
||||||
import { LinearProgressWithLabel } from '../../components/ProgressBar'
|
import { LinearProgressWithLabel } from '../../components/ProgressBar'
|
||||||
import { Tag } from '@ethersphere/bee-js'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
reference: string
|
reference: string
|
||||||
|
|||||||
@@ -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 { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
@@ -11,7 +11,6 @@ import { Context as SettingsContext } from '../../providers/Settings'
|
|||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { recognizeEnsOrSwarmHash, regexpEns } from '../../utils'
|
import { recognizeEnsOrSwarmHash, regexpEns } from '../../utils'
|
||||||
import { HISTORY_KEYS, determineHistoryName, putHistory } from '../../utils/local-storage'
|
import { HISTORY_KEYS, determineHistoryName, putHistory } from '../../utils/local-storage'
|
||||||
import { ManifestJs } from '../../utils/manifest'
|
|
||||||
import { FileNavigation } from './FileNavigation'
|
import { FileNavigation } from './FileNavigation'
|
||||||
|
|
||||||
export function Download(): ReactElement {
|
export function Download(): ReactElement {
|
||||||
@@ -26,41 +25,43 @@ export function Download(): ReactElement {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const validateChange = (value: string) => {
|
const validateChange = (value: string) => {
|
||||||
if (
|
if (Reference.isValid(value) || regexpEns.test(value)) {
|
||||||
Utils.isHexString(value, 64) ||
|
|
||||||
Utils.isHexString(value, 128) ||
|
|
||||||
!value.trim().length ||
|
|
||||||
regexpEns.test(value)
|
|
||||||
) {
|
|
||||||
setReferenceError(undefined)
|
setReferenceError(undefined)
|
||||||
} else {
|
} else {
|
||||||
setReferenceError('Incorrect format of swarm hash. Expected 64 or 128 hexstring characters or ENS domain.')
|
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) {
|
async function onSwarmIdentifier(identifier: string) {
|
||||||
setLoading(true)
|
|
||||||
|
|
||||||
if (!beeApi) {
|
if (!beeApi) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const manifestJs = new ManifestJs(beeApi)
|
let manifest = await MantarayNode.unmarshal(beeApi, identifier)
|
||||||
const feedIdentifier = await manifestJs.resolveFeedManifest(identifier)
|
await manifest.loadRecursively(beeApi)
|
||||||
|
|
||||||
if (feedIdentifier) {
|
// If the manifest is a feed, resolve it and overwrite the manifest
|
||||||
identifier = feedIdentifier
|
await manifest.resolveFeed(beeApi).then(
|
||||||
}
|
async feed =>
|
||||||
const isManifest = await manifestJs.isManifest(identifier)
|
await feed.ifPresentAsync(async feedUpdate => {
|
||||||
|
manifest = MantarayNode.unmarshalFromData(feedUpdate.payload.toUint8Array(), NULL_ADDRESS)
|
||||||
|
await manifest.loadRecursively(beeApi)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
if (!isManifest) {
|
const rootMetadata = manifest.getDocsMetadata()
|
||||||
throw Error('The specified hash does not contain valid content.')
|
|
||||||
}
|
putHistory(
|
||||||
const indexDocument = await manifestJs.getIndexDocumentPath(identifier)
|
HISTORY_KEYS.DOWNLOAD_HISTORY,
|
||||||
putHistory(HISTORY_KEYS.DOWNLOAD_HISTORY, identifier, determineHistoryName(identifier, indexDocument))
|
identifier,
|
||||||
|
determineHistoryName(identifier, rootMetadata.indexDocument),
|
||||||
|
)
|
||||||
setUploadOrigin(defaultUploadOrigin)
|
setUploadOrigin(defaultUploadOrigin)
|
||||||
navigate(ROUTES.HASH.replace(':hash', identifier))
|
navigate(ROUTES.HASH.replace(':hash', identifier))
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
|
|||||||
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
||||||
{stamps.map(stamp => (
|
{stamps.map(stamp => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={stamp.batchID}
|
key={stamp.batchID.toHex()}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelected(stamp)
|
setSelected(stamp)
|
||||||
handleClose()
|
handleClose()
|
||||||
@@ -35,7 +35,7 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
|
|||||||
selected={stamp.batchID === selectedStamp?.batchID}
|
selected={stamp.batchID === selectedStamp?.batchID}
|
||||||
>
|
>
|
||||||
<ListItemIcon>{stamp.usageText}</ListItemIcon>
|
<ListItemIcon>{stamp.usageText}</ListItemIcon>
|
||||||
<Typography variant="body2">{stamp.batchID.slice(0, 8)}[…]</Typography>
|
<Typography variant="body2">{stamp.batchID.toHex().slice(0, 8)}[…]</Typography>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
+48
-35
@@ -1,4 +1,5 @@
|
|||||||
import { Box, Typography } from '@material-ui/core'
|
import { Box, Typography } from '@material-ui/core'
|
||||||
|
import { MantarayNode, NULL_ADDRESS } from '@ethersphere/bee-js'
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
import JSZip from 'jszip'
|
import JSZip from 'jszip'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
@@ -12,11 +13,10 @@ import { Context as BeeContext } from '../../providers/Bee'
|
|||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
||||||
import { ManifestJs } from '../../utils/manifest'
|
|
||||||
import { AssetPreview } from './AssetPreview'
|
import { AssetPreview } from './AssetPreview'
|
||||||
import { AssetSummary } from './AssetSummary'
|
import { AssetSummary } from './AssetSummary'
|
||||||
import { DownloadActionBar } from './DownloadActionBar'
|
|
||||||
import { AssetSyncing } from './AssetSyncing'
|
import { AssetSyncing } from './AssetSyncing'
|
||||||
|
import { DownloadActionBar } from './DownloadActionBar'
|
||||||
|
|
||||||
export function Share(): ReactElement {
|
export function Share(): ReactElement {
|
||||||
const { apiUrl, beeApi } = useContext(SettingsContext)
|
const { apiUrl, beeApi } = useContext(SettingsContext)
|
||||||
@@ -41,44 +41,57 @@ export function Share(): ReactElement {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const manifestJs = new ManifestJs(beeApi)
|
try {
|
||||||
const isManifest = await manifestJs.isManifest(reference)
|
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)
|
setNotFound(true)
|
||||||
enqueueSnackbar('The specified hash does not contain valid content.', { variant: 'error' })
|
enqueueSnackbar('The specified hash does not contain valid content.', { variant: 'error' })
|
||||||
|
|
||||||
return
|
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() {
|
function onOpen() {
|
||||||
@@ -119,7 +132,7 @@ export function Share(): ReactElement {
|
|||||||
} else {
|
} else {
|
||||||
const zip = new JSZip()
|
const zip = new JSZip()
|
||||||
for (const [path, hash] of Object.entries(swarmEntries)) {
|
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' })
|
const content = await zip.generateAsync({ type: 'blob' })
|
||||||
saveAs(content, reference + '.zip')
|
saveAs(content, reference + '.zip')
|
||||||
|
|||||||
@@ -114,10 +114,10 @@ export function Upload(): ReactElement {
|
|||||||
beeApi
|
beeApi
|
||||||
.uploadFiles(stamp.batchID, fls, { indexDocument, deferred: true })
|
.uploadFiles(stamp.batchID, fls, { indexDocument, deferred: true })
|
||||||
.then(hash => {
|
.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') {
|
if (uploadOrigin.origin === 'UPLOAD') {
|
||||||
navigate(ROUTES.HASH.replace(':hash', hash.reference), { replace: true })
|
navigate(ROUTES.HASH.replace(':hash', hash.reference.toHex()), { replace: true })
|
||||||
} else {
|
} else {
|
||||||
updateFeed(beeApi, identity as Identity, hash.reference, stamp.batchID, password as string).then(() => {
|
updateFeed(beeApi, identity as Identity, hash.reference, stamp.batchID, password as string).then(() => {
|
||||||
persistIdentity(identities, identity as Identity)
|
persistIdentity(identities, identity as Identity)
|
||||||
@@ -164,7 +164,7 @@ export function Upload(): ReactElement {
|
|||||||
<>
|
<>
|
||||||
<Box mb={2}>
|
<Box mb={2}>
|
||||||
{hasAnyStamps && stampMode === 'SELECT' ? (
|
{hasAnyStamps && stampMode === 'SELECT' ? (
|
||||||
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID} />
|
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID.toHex()} />
|
||||||
) : (
|
) : (
|
||||||
<PostageStampAdvancedCreation onFinished={() => setStampMode('SELECT')} />
|
<PostageStampAdvancedCreation onFinished={() => setStampMode('SELECT')} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Box, Tooltip, Typography } from '@material-ui/core'
|
import { Box, Tooltip, Typography } from '@material-ui/core'
|
||||||
|
import { BZZ, DAI } from '@ethersphere/bee-js'
|
||||||
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'
|
||||||
@@ -11,20 +12,19 @@ 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 { Token } from '../../models/Token'
|
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'
|
||||||
|
|
||||||
const GIFT_WALLET_FUND_DAI_AMOUNT = Token.fromDecimal('0.1', 18)
|
const GIFT_WALLET_FUND_DAI_AMOUNT = DAI.fromDecimalString('0.1')
|
||||||
const GIFT_WALLET_FUND_BZZ_AMOUNT = Token.fromDecimal('0.5', 16)
|
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 { balance } = useContext(BalanceProvider)
|
const { walletBalance } = useContext(BeeContext)
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [balances, setBalances] = useState<ResolvedWallet[]>([])
|
const [balances, setBalances] = useState<ResolvedWallet[]>([])
|
||||||
@@ -67,13 +67,13 @@ export default function Index(): ReactElement {
|
|||||||
navigate(-1)
|
navigate(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!balance) {
|
if (!walletBalance) {
|
||||||
return <Loading />
|
return <Loading />
|
||||||
}
|
}
|
||||||
|
|
||||||
const notEnoughFundsCheck =
|
const notEnoughFundsCheck =
|
||||||
balance.dai.toBigNumber.isLessThanOrEqualTo(GIFT_WALLET_FUND_DAI_AMOUNT.toBigNumber) ||
|
walletBalance.nativeTokenBalance.lte(GIFT_WALLET_FUND_DAI_AMOUNT) ||
|
||||||
balance.bzz.toBigNumber.isLessThan(GIFT_WALLET_FUND_BZZ_AMOUNT.toBigNumber)
|
walletBalance.bzzBalance.lt(GIFT_WALLET_FUND_BZZ_AMOUNT)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -86,10 +86,13 @@ export default function Index(): ReactElement {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={0.25}>
|
<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>
|
||||||
<Box mb={2}>
|
<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>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
{balances.map((x, i) => (
|
{balances.map((x, i) => (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { BZZ } from '@ethersphere/bee-js'
|
||||||
import { useContext } from 'react'
|
import { useContext } from 'react'
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
import ExchangeFunds from 'remixicon-react/ExchangeFundsLineIcon'
|
import ExchangeFunds from 'remixicon-react/ExchangeFundsLineIcon'
|
||||||
@@ -9,10 +10,7 @@ export function ChequebookInfoCard() {
|
|||||||
const { chequebookBalance } = useContext(BeeContext)
|
const { chequebookBalance } = useContext(BeeContext)
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
if (
|
if (chequebookBalance?.availableBalance !== undefined && chequebookBalance?.availableBalance.gt(BZZ.fromPLUR('0'))) {
|
||||||
chequebookBalance?.availableBalance !== undefined &&
|
|
||||||
chequebookBalance?.availableBalance.toBigNumber.isGreaterThan(0)
|
|
||||||
) {
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
buttonProps={{
|
buttonProps={{
|
||||||
|
|||||||
@@ -4,21 +4,18 @@ import Upload from 'remixicon-react/UploadLineIcon'
|
|||||||
import Wallet from 'remixicon-react/Wallet3LineIcon'
|
import Wallet from 'remixicon-react/Wallet3LineIcon'
|
||||||
import Card from '../../components/Card'
|
import Card from '../../components/Card'
|
||||||
import { Context as BeeContext } from '../../providers/Bee'
|
import { Context as BeeContext } from '../../providers/Bee'
|
||||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
|
|
||||||
export function WalletInfoCard() {
|
export function WalletInfoCard() {
|
||||||
const { nodeInfo } = useContext(BeeContext)
|
const { nodeInfo, walletBalance } = useContext(BeeContext)
|
||||||
const { balance, error } = useContext(BalanceProvider)
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
let balanceText = 'Loading...'
|
let balanceText = 'Loading...'
|
||||||
|
|
||||||
if (error) {
|
if (walletBalance) {
|
||||||
balanceText = 'Could not load...'
|
balanceText = `${walletBalance.bzzBalance.toSignificantDigits(
|
||||||
console.error(error) // eslint-disable-line
|
4,
|
||||||
} else if (balance) {
|
)} xBZZ | ${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`
|
||||||
balanceText = `${balance.bzz.toSignificantDigits(4)} xBZZ | ${balance.dai.toSignificantDigits(4)} xDAI`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodeInfo?.beeMode && ['light', 'full', 'dev'].includes(nodeInfo.beeMode)) {
|
if (nodeInfo?.beeMode && ['light', 'full', 'dev'].includes(nodeInfo.beeMode)) {
|
||||||
|
|||||||
@@ -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, chainId } = useContext(BeeContext)
|
const { beeVersion, status, topology, nodeInfo, 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)
|
||||||
@@ -64,7 +64,9 @@ export default function Status(): ReactElement {
|
|||||||
)}
|
)}
|
||||||
<ExpandableListItem label="Bee version" value={beeVersion} />
|
<ExpandableListItem label="Bee version" value={beeVersion} />
|
||||||
<ExpandableListItem label="Mode" value={nodeInfo?.beeMode} />
|
<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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
|
||||||
import { Box, Grid, Typography } from '@material-ui/core'
|
import { Box, Grid, Typography } from '@material-ui/core'
|
||||||
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useEffect } from 'react'
|
import { ReactElement, useContext, useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function PostageStamp({ stamp, shorten }: Props): ReactElement {
|
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}` : ''}`
|
const label = `${batchId}${stamp.label ? ` - ${stamp.label}` : ''}`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
|
|
||||||
import { Box, Grid, IconButton, Typography, createStyles, makeStyles } from '@material-ui/core'
|
import { Box, Grid, IconButton, Typography, createStyles, makeStyles } from '@material-ui/core'
|
||||||
|
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
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 SettingsContext } from '../../providers/Settings'
|
||||||
import { Context as StampsContext } from '../../providers/Stamps'
|
import { Context as StampsContext } from '../../providers/Stamps'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { calculateStampPrice, convertAmountToSeconds, secondsToTimeString, waitUntilStampExists } from '../../utils'
|
import { secondsToTimeString } from '../../utils'
|
||||||
import { getHumanReadableFileSize } from '../../utils/file'
|
import { getHumanReadableFileSize } from '../../utils/file'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -61,18 +61,22 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
|||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
|
|
||||||
function getTtl(amount: number): string {
|
function getTtl(amount: bigint): string {
|
||||||
const isCurrentPriceAvailable = chainState && chainState.currentPrice
|
const isCurrentPriceAvailable = chainState && chainState.currentPrice
|
||||||
|
|
||||||
if (amount <= 0 || !isCurrentPriceAvailable) {
|
if (amount <= 0 || !isCurrentPriceAvailable) {
|
||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
const pricePerBlock = Number.parseInt(chainState.currentPrice, 10)
|
const pricePerBlock = chainState.currentPrice
|
||||||
|
|
||||||
return `${secondsToTimeString(
|
try {
|
||||||
convertAmountToSeconds(amount, pricePerBlock),
|
return `${secondsToTimeString(
|
||||||
)} (with price of ${pricePerBlock.toFixed(0)} PLUR per block)`
|
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 {
|
function getPrice(depth: number, amount: bigint): string {
|
||||||
@@ -82,9 +86,9 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
|||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
const price = calculateStampPrice(depth, amount)
|
const price = Utils.getStampCost(depth, amount)
|
||||||
|
|
||||||
return `${price.toSignificantDigits()} xBZZ`
|
return `${price.toSignificantDigits(4)} xBZZ`
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
@@ -107,8 +111,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
|||||||
immutableFlag: immutable,
|
immutableFlag: immutable,
|
||||||
}
|
}
|
||||||
|
|
||||||
const batchId = await beeApi.createPostageBatch(amount.toString(), depth, options)
|
await beeApi.createPostageBatch(amount.toString(), depth, options)
|
||||||
await waitUntilStampExists(batchId, beeApi)
|
|
||||||
await refresh()
|
await refresh()
|
||||||
onFinished()
|
onFinished()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -173,7 +176,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
|||||||
return '-'
|
return '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
const theoreticalMaximumVolume = getHumanReadableFileSize(Utils.getStampMaximumCapacityBytes(depth))
|
const theoreticalMaximumVolume = getHumanReadableFileSize(Utils.getStampTheoreticalBytes(depth))
|
||||||
const effectiveVolume = getHumanReadableFileSize(Utils.getStampEffectiveBytes(depth))
|
const effectiveVolume = getHumanReadableFileSize(Utils.getStampEffectiveBytes(depth))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -227,7 +230,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
|||||||
<Box mt={0.25} sx={{ bgcolor: '#f6f6f6' }} p={2}>
|
<Box mt={0.25} sx={{ bgcolor: '#f6f6f6' }} p={2}>
|
||||||
<Grid container justifyContent="space-between">
|
<Grid container justifyContent="space-between">
|
||||||
<Typography>Corresponding TTL (Time to live)</Typography>
|
<Typography>Corresponding TTL (Time to live)</Typography>
|
||||||
<Typography>{!amountError && amountInput ? getTtl(Number.parseInt(amountInput, 10)) : '-'}</Typography>
|
<Typography>{!amountError && amountInput ? getTtl(BigInt(amountInput)) : '-'}</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Box>
|
</Box>
|
||||||
{amountError && <Typography>{amountError}</Typography>}
|
{amountError && <Typography>{amountError}</Typography>}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactEl
|
|||||||
if (!stamps) {
|
if (!stamps) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const stamp = stamps.find(x => x.batchID === stampId)
|
const stamp = stamps.find(x => x.batchID.toHex() === stampId)
|
||||||
|
|
||||||
if (stamp) {
|
if (stamp) {
|
||||||
onSelect(stamp)
|
onSelect(stamp)
|
||||||
@@ -24,8 +24,8 @@ export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactEl
|
|||||||
return (
|
return (
|
||||||
<SwarmSelect
|
<SwarmSelect
|
||||||
options={(stamps || []).map(x => ({
|
options={(stamps || []).map(x => ({
|
||||||
label: x.label ? x.batchID.slice(0, 8) + ' - ' + x.label : x.batchID.slice(0, 8),
|
label: x.label ? x.batchID.toHex().slice(0, 8) + ' - ' + x.label : x.batchID.toHex().slice(0, 8),
|
||||||
value: x.batchID,
|
value: x.batchID.toHex(),
|
||||||
}))}
|
}))}
|
||||||
onChange={event => onChange(event.target.value as string)}
|
onChange={event => onChange(event.target.value as string)}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
|
|
||||||
import { Box, Button, Grid, Slider, Typography } from '@material-ui/core'
|
import { Box, Button, Grid, Slider, Typography } from '@material-ui/core'
|
||||||
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
|
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { Duration, PostageBatchOptions, Size, Utils } from '@ethersphere/bee-js'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
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 SettingsContext } from '../../providers/Settings'
|
||||||
import { Context as StampsContext } from '../../providers/Stamps'
|
import { Context as StampsContext } from '../../providers/Stamps'
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { calculateStampPrice, convertAmountToSeconds, secondsToTimeString, waitUntilStampExists } from '../../utils'
|
import { secondsToTimeString } from '../../utils'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onFinished: () => void
|
onFinished: () => void
|
||||||
@@ -46,8 +46,8 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
|||||||
const { refresh } = useContext(StampsContext)
|
const { refresh } = useContext(StampsContext)
|
||||||
const { beeApi } = useContext(SettingsContext)
|
const { beeApi } = useContext(SettingsContext)
|
||||||
|
|
||||||
const [depthInput, setDepthInput] = useState<number>(Utils.getDepthForCapacity(4))
|
const [depthInput, setDepthInput] = useState<number>(Utils.getDepthForSize(Size.fromGigabytes(4)))
|
||||||
const [amountInput, setAmountInput] = useState<string>(Utils.getAmountForTtl(30))
|
const [amountInput, setAmountInput] = useState<bigint>(Utils.getAmountForDuration(Duration.fromDays(30), 26500, 5))
|
||||||
const [labelInput, setLabelInput] = useState('')
|
const [labelInput, setLabelInput] = useState('')
|
||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
const [buttonValue, setButtonValue] = useState(4)
|
const [buttonValue, setButtonValue] = useState(4)
|
||||||
@@ -56,24 +56,24 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
|||||||
if (typeof newValue !== 'number') {
|
if (typeof newValue !== 'number') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const amountValue = Utils.getAmountForTtl(newValue)
|
const amountValue = Utils.getAmountForDuration(Duration.fromDays(newValue), 26500, 5)
|
||||||
setAmountInput(amountValue)
|
setAmountInput(amountValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
|
|
||||||
function getTtl(amount: string): string {
|
function getTtl(amount: bigint): string {
|
||||||
const pricePerBlock = 24000
|
const pricePerBlock = 24000
|
||||||
|
|
||||||
return `${secondsToTimeString(
|
return `${secondsToTimeString(
|
||||||
convertAmountToSeconds(parseInt(amount, 10), pricePerBlock),
|
Utils.getStampDuration(amount, pricePerBlock, 5).toSeconds(),
|
||||||
)} (with price of ${pricePerBlock.toFixed(0)} PLUR per block)`
|
)} (with price of ${pricePerBlock} PLUR per block)`
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPrice(depth: number, amount: bigint): string {
|
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() {
|
async function submit() {
|
||||||
@@ -96,8 +96,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
|||||||
immutableFlag: true,
|
immutableFlag: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
const batchId = await beeApi.createPostageBatch(amount.toString(), depth, options)
|
await beeApi.createPostageBatch(amount.toString(), depth, options)
|
||||||
await waitUntilStampExists(batchId, beeApi)
|
|
||||||
await refresh()
|
await refresh()
|
||||||
onFinished()
|
onFinished()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -109,7 +108,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
|||||||
|
|
||||||
function handleBatchSize(gigabytes: number) {
|
function handleBatchSize(gigabytes: number) {
|
||||||
setButtonValue(gigabytes)
|
setButtonValue(gigabytes)
|
||||||
const capacity = Utils.getDepthForCapacity(gigabytes)
|
const capacity = Utils.getDepthForSize(Size.fromGigabytes(gigabytes))
|
||||||
setDepthInput(capacity)
|
setDepthInput(capacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import Dialog from '@material-ui/core/Dialog'
|
|||||||
import DialogContent from '@material-ui/core/DialogContent'
|
import DialogContent from '@material-ui/core/DialogContent'
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||||
import { Check, Clear } from '@material-ui/icons'
|
import { Check, Clear } from '@material-ui/icons'
|
||||||
import React, { ReactElement, useState } from 'react'
|
import { ReactElement, useState } from 'react'
|
||||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||||
import { SwarmSelect } from '../../components/SwarmSelect'
|
import { SwarmSelect } from '../../components/SwarmSelect'
|
||||||
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
||||||
@@ -39,7 +39,7 @@ export function SelectPostageStampModal({ stamps, onSelect, onClose }: Props): R
|
|||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
function onChange(stampId: string) {
|
function onChange(stampId: string) {
|
||||||
const stamp = stamps.find(x => x.batchID === stampId)
|
const stamp = stamps.find(x => x.batchID.toHex() === stampId)
|
||||||
|
|
||||||
if (stamp) {
|
if (stamp) {
|
||||||
setSelectedStamp(stamp)
|
setSelectedStamp(stamp)
|
||||||
@@ -66,7 +66,7 @@ export function SelectPostageStampModal({ stamps, onSelect, onClose }: Props): R
|
|||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<SwarmSelect
|
<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)}
|
onChange={event => onChange(event.target.value as string)}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ function StampsTable({ postageStamps }: Props): ReactElement | null {
|
|||||||
<ExpandableList label="Postage Stamps" defaultOpen>
|
<ExpandableList label="Postage Stamps" defaultOpen>
|
||||||
{postageStamps.map(stamp => (
|
{postageStamps.map(stamp => (
|
||||||
<ExpandableElement
|
<ExpandableElement
|
||||||
key={stamp.batchID}
|
key={stamp.batchID.toHex()}
|
||||||
expandable={
|
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="Depth" value={String(stamp.depth)} />
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
label="Capacity"
|
label="Capacity"
|
||||||
@@ -40,13 +40,9 @@ function StampsTable({ postageStamps }: Props): ReactElement | null {
|
|||||||
)}`}
|
)}`}
|
||||||
/>
|
/>
|
||||||
<ExpandableListItem label="Amount" value={parseInt(stamp.amount, 10).toLocaleString()} />
|
<ExpandableListItem label="Amount" value={parseInt(stamp.amount, 10).toLocaleString()} />
|
||||||
<ExpandableListItem
|
<ExpandableListItem label="Expires in" value={secondsToTimeString(stamp.duration.toSeconds())} />
|
||||||
label="Expires in"
|
|
||||||
value={stamp.batchTTL === -1 ? 'does not expire' : `${secondsToTimeString(stamp.batchTTL)}`}
|
|
||||||
/>
|
|
||||||
<ExpandableListItem label="Label" value={stamp.label} />
|
<ExpandableListItem label="Label" value={stamp.label} />
|
||||||
<ExpandableListItem label="Usable" value={stamp.usable ? 'yes' : 'no'} />
|
<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="Immutable" value={stamp.immutableFlag ? 'yes' : 'no'} />
|
||||||
<ExpandableListItem label="Purchase Block Number" value={stamp.blockNumber} />
|
<ExpandableListItem label="Purchase Block Number" value={stamp.blockNumber} />
|
||||||
<ExpandableListItemActions>
|
<ExpandableListItemActions>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Box, Grid, Typography } from '@material-ui/core'
|
import { Box, Grid, Typography } from '@material-ui/core'
|
||||||
|
import { DAI } from '@ethersphere/bee-js'
|
||||||
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'
|
||||||
@@ -9,10 +10,9 @@ 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 = '0.5'
|
const MINIMUM_XDAI = DAI.fromDecimalString('0.5')
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
header: string
|
header: string
|
||||||
@@ -22,15 +22,14 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Index({ header, title, p, next }: Props): ReactElement {
|
export default function Index({ header, title, p, next }: Props): ReactElement {
|
||||||
const { nodeAddresses } = useContext(Context)
|
const { nodeAddresses, walletBalance } = useContext(Context)
|
||||||
const { balance } = useContext(BalanceProvider)
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
||||||
if (!balance || !nodeAddresses) {
|
if (!walletBalance || !nodeAddresses) {
|
||||||
return <Loading />
|
return <Loading />
|
||||||
}
|
}
|
||||||
|
|
||||||
const disabled = balance.dai.toDecimal.lt(MINIMUM_XDAI)
|
const disabled = walletBalance.nativeTokenBalance.lt(MINIMUM_XDAI)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -44,17 +43,19 @@ 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} expanded />
|
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded />
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<ExpandableListItem label="xDAI balance" value={balance.dai.toSignificantDigits(4)} />
|
<ExpandableListItem label="xDAI balance" value={walletBalance.nativeTokenBalance.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}>
|
||||||
Proceed
|
Proceed
|
||||||
</SwarmButton>
|
</SwarmButton>
|
||||||
{disabled ? (
|
{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}
|
) : null}
|
||||||
</Grid>
|
</Grid>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
|
||||||
import { Box, Typography } from '@material-ui/core'
|
import { Box, Typography } from '@material-ui/core'
|
||||||
|
import { BeeModes } from '@ethersphere/bee-js'
|
||||||
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,16 +14,14 @@ 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 } = useContext(BeeContext)
|
const { nodeAddresses, nodeInfo, walletBalance } = 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)
|
||||||
@@ -41,7 +39,7 @@ export function GiftCardFund(): ReactElement {
|
|||||||
ResolvedWallet.make(privateKeyString, rpcProvider).then(setWallet)
|
ResolvedWallet.make(privateKeyString, rpcProvider).then(setWallet)
|
||||||
}, [privateKeyString, rpcProvider])
|
}, [privateKeyString, rpcProvider])
|
||||||
|
|
||||||
if (!wallet || !balance) {
|
if (!wallet || !walletBalance) {
|
||||||
return <Loading />
|
return <Loading />
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,13 +106,20 @@ export function GiftCardFund(): ReactElement {
|
|||||||
<ArrowDown size={24} color="#aaaaaa" />
|
<ArrowDown size={24} color="#aaaaaa" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={0.25}>
|
<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>
|
||||||
<Box mb={0.25}>
|
<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>
|
||||||
<Box mb={2}>
|
<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>
|
||||||
<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'}
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
import { Box, Typography } from '@material-ui/core'
|
import { Box, Typography } from '@material-ui/core'
|
||||||
|
import { BZZ, DAI } from '@ethersphere/bee-js'
|
||||||
import { Wallet } from 'ethers'
|
import { Wallet } from 'ethers'
|
||||||
import { useSnackbar } from 'notistack'
|
import { useSnackbar } from 'notistack'
|
||||||
import { ReactElement, useContext, useState } from 'react'
|
import { ReactElement, useContext, useState } from 'react'
|
||||||
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
|
||||||
import { useNavigate } from 'react-router'
|
import { useNavigate } from 'react-router'
|
||||||
import { Context as SettingsContext } from '../../providers/Settings'
|
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
||||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||||
import { ProgressIndicator } from '../../components/ProgressIndicator'
|
import { ProgressIndicator } from '../../components/ProgressIndicator'
|
||||||
import { SwarmButton } from '../../components/SwarmButton'
|
import { SwarmButton } from '../../components/SwarmButton'
|
||||||
import { SwarmDivider } from '../../components/SwarmDivider'
|
import { SwarmDivider } from '../../components/SwarmDivider'
|
||||||
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||||
import { BzzToken } from '../../models/BzzToken'
|
import { Context as SettingsContext } from '../../providers/Settings'
|
||||||
import { DaiToken } from '../../models/DaiToken'
|
|
||||||
import { ROUTES } from '../../routes'
|
import { ROUTES } from '../../routes'
|
||||||
import { Rpc } from '../../utils/rpc'
|
import { Rpc } from '../../utils/rpc'
|
||||||
|
|
||||||
@@ -29,10 +28,10 @@ export function GiftCardTopUpIndex(): ReactElement {
|
|||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const wallet = new Wallet(giftCode, rpcProvider)
|
const wallet = new Wallet(giftCode, rpcProvider)
|
||||||
const dai = new DaiToken(await Rpc._eth_getBalance(wallet.address, rpcProvider))
|
const dai = await Rpc._eth_getBalance(wallet.address, rpcProvider)
|
||||||
const bzz = new BzzToken(await Rpc._eth_getBalanceERC20(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')
|
throw Error('Gift wallet does not have enough funds')
|
||||||
}
|
}
|
||||||
enqueueSnackbar('Successfully verified gift wallet', { variant: 'success' })
|
enqueueSnackbar('Successfully verified gift wallet', { variant: 'success' })
|
||||||
|
|||||||
+38
-43
@@ -1,5 +1,5 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
|
||||||
import { Box, Typography } from '@material-ui/core'
|
import { Box, Typography } from '@material-ui/core'
|
||||||
|
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
|
||||||
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'
|
||||||
@@ -13,14 +13,11 @@ 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 { SwarmTextInput } from '../../components/SwarmTextInput'
|
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 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 { SwapError, isSwapError, wrapWithSwapError } from '../../utils/SwapError'
|
import { isSwapError, SwapError, wrapWithSwapError } from '../../utils/SwapError'
|
||||||
import {
|
import {
|
||||||
getBzzPriceAsDai,
|
getBzzPriceAsDai,
|
||||||
getDesktopConfiguration,
|
getDesktopConfiguration,
|
||||||
@@ -31,8 +28,8 @@ import {
|
|||||||
import { Rpc } from '../../utils/rpc'
|
import { Rpc } from '../../utils/rpc'
|
||||||
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
|
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
|
||||||
|
|
||||||
const MINIMUM_XDAI = '0.1'
|
const MINIMUM_XDAI = DAI.fromDecimalString('0.1')
|
||||||
const MINIMUM_XBZZ = '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.'
|
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 [loading, setLoading] = useState(false)
|
||||||
const [hasSwapped, setSwapped] = useState(false)
|
const [hasSwapped, setSwapped] = useState(false)
|
||||||
const [userInputSwap, setUserInputSwap] = useState<string | null>(null)
|
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 [error, setError] = useState<string | null>(null)
|
||||||
const [daiToSwap, setDaiToSwap] = useState<DaiToken | null>(null)
|
const [daiToSwap, setDaiToSwap] = useState<DAI | null>(null)
|
||||||
const [bzzAfterSwap, setBzzAfterSwap] = useState<BzzToken | null>(null)
|
const [bzzAfterSwap, setBzzAfterSwap] = useState<BZZ | null>(null)
|
||||||
const [daiAfterSwap, setDaiAfterSwap] = useState<DaiToken | null>(null)
|
const [daiAfterSwap, setDaiAfterSwap] = useState<DAI | null>(null)
|
||||||
|
|
||||||
const { rpcProviderUrl, isDesktop, desktopUrl } = useContext(SettingsContext)
|
const { rpcProviderUrl, isDesktop, desktopUrl } = useContext(SettingsContext)
|
||||||
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
|
const { nodeAddresses, nodeInfo, walletBalance } = useContext(BeeContext)
|
||||||
const { balance } = useContext(BalanceProvider)
|
|
||||||
|
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { enqueueSnackbar } = useSnackbar()
|
const { enqueueSnackbar } = useSnackbar()
|
||||||
@@ -65,30 +61,30 @@ export function Swap({ header }: Props): ReactElement {
|
|||||||
|
|
||||||
// Set the initial xDAI to swap
|
// Set the initial xDAI to swap
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!balance || userInputSwap) {
|
if (!walletBalance || userInputSwap) {
|
||||||
return
|
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
|
// Balance has at least 1 + MINIMUM_XDAI xDai
|
||||||
setDaiToSwap(balance.dai.minusBaseUnits('1'))
|
setDaiToSwap(walletBalance.nativeTokenBalance.minus(DAI.fromDecimalString('1')))
|
||||||
} else {
|
} else {
|
||||||
// Balance is low, halve the amount
|
// 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
|
// Set the xDAI to swap based on user input
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setError(null)
|
setError(null)
|
||||||
try {
|
try {
|
||||||
if (userInputSwap) {
|
if (userInputSwap) {
|
||||||
const dai = DaiToken.fromDecimal(userInputSwap)
|
const dai = DAI.fromDecimalString(userInputSwap)
|
||||||
setDaiToSwap(dai)
|
setDaiToSwap(dai)
|
||||||
|
|
||||||
if (dai.toDecimal.lte(0)) {
|
if (dai.lte(DAI.fromDecimalString('0'))) {
|
||||||
setError('xDAI to swap must be a positive number')
|
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
|
// Calculate the amount of tokens after swap
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!balance || !daiToSwap || error) {
|
if (!walletBalance || !daiToSwap || error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const daiAfterSwap = new DaiToken(balance.dai.toBigNumber.minus(daiToSwap.toBigNumber))
|
const daiAfterSwap = walletBalance.nativeTokenBalance.minus(daiToSwap)
|
||||||
setDaiAfterSwap(daiAfterSwap)
|
setDaiAfterSwap(daiAfterSwap)
|
||||||
const tokensConverted = BzzToken.fromDecimal(
|
const tokensConverted = daiToSwap.exchangeToBZZ(price)
|
||||||
daiToSwap.toBigNumber.dividedBy(price.toBigNumber).decimalPlaces(BZZ_DECIMAL_PLACES),
|
const bzzAfterSwap = tokensConverted.plus(walletBalance.bzzBalance)
|
||||||
)
|
|
||||||
const bzzAfterSwap = new BzzToken(tokensConverted.toBigNumber.plus(balance.bzz.toBigNumber))
|
|
||||||
setBzzAfterSwap(bzzAfterSwap)
|
setBzzAfterSwap(bzzAfterSwap)
|
||||||
|
|
||||||
if (daiAfterSwap.toDecimal.lt(MINIMUM_XDAI)) {
|
if (daiAfterSwap.lt(MINIMUM_XDAI)) {
|
||||||
setError(`Must keep at least ${MINIMUM_XDAI} xDAI after swap!`)
|
setError(`Must keep at least ${MINIMUM_XDAI.toSignificantDigits(4)} xDAI after swap!`)
|
||||||
} else if (bzzAfterSwap.toDecimal.lt(MINIMUM_XBZZ)) {
|
} else if (bzzAfterSwap.lt(MINIMUM_XBZZ)) {
|
||||||
setError(`Must have at least ${MINIMUM_XBZZ} xBZZ after swap!`)
|
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 />
|
return <Loading />
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,9 +129,9 @@ export function Swap({ header }: Props): ReactElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendSwapRequest(daiToSwap: DaiToken) {
|
async function sendSwapRequest(daiToSwap: DAI) {
|
||||||
try {
|
try {
|
||||||
await performSwap(desktopUrl, daiToSwap.toString)
|
await performSwap(desktopUrl, daiToSwap)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(error)
|
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')) {
|
if (!localStorage.getItem('apiKey')) {
|
||||||
throw new SwapError('API key is not set, reopen dashboard through Swarm Desktop')
|
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.'
|
: 'Successfully swapped. Balances will refresh soon. You may now navigate away.'
|
||||||
enqueueSnackbar(message, { variant: 'success' })
|
enqueueSnackbar(message, { variant: 'success' })
|
||||||
|
|
||||||
if (canUpgradeToLightNode) await restart()
|
if (canUpgradeToLightNode) {
|
||||||
|
await restart()
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isSwapError(error)) {
|
if (isSwapError(error)) {
|
||||||
// we have a custom and user friendly error message
|
// 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
|
console.error(error) // eslint-disable-line
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
balance?.refresh()
|
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,15 +212,15 @@ export function Swap({ header }: Props): ReactElement {
|
|||||||
</Box>
|
</Box>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<Typography>
|
<Typography>
|
||||||
You need to swap xDAI to xBZZ in order to use Swarm. Make sure to keep at least {MINIMUM_XDAI} xDAI in order
|
You need to swap xDAI to xBZZ in order to use Swarm. Make sure to keep at least{' '}
|
||||||
to pay for transaction costs on the network.
|
{MINIMUM_XDAI.toSignificantDigits(4)} xDAI in order to pay for transaction costs on the network.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<SwarmDivider mb={4} />
|
<SwarmDivider mb={4} />
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
<Typography>
|
<Typography>
|
||||||
Your current balance is {balance.dai.toSignificantDigits(4)} xDAI and {balance.bzz.toSignificantDigits(4)}{' '}
|
Your current balance is {walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI and{' '}
|
||||||
xBZZ.
|
{walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ.
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={4}>
|
<Box mb={4}>
|
||||||
@@ -242,7 +237,7 @@ export function Swap({ header }: Props): ReactElement {
|
|||||||
<ArrowDown size={24} color="#aaaaaa" />
|
<ArrowDown size={24} color="#aaaaaa" />
|
||||||
</Box>
|
</Box>
|
||||||
<Box mb={0.25}>
|
<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>
|
||||||
<Box mb={0.25}>
|
<Box mb={0.25}>
|
||||||
<ExpandableListItem
|
<ExpandableListItem
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BeeModes } from '@ethersphere/bee-js'
|
|
||||||
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
||||||
|
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
|
||||||
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,7 +15,6 @@ 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'
|
||||||
|
|
||||||
@@ -33,15 +32,14 @@ const useStyles = makeStyles(() =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const MINIMUM_XDAI = '0.05'
|
const MINIMUM_XDAI = DAI.fromDecimalString('0.05')
|
||||||
const MINIMUM_XBZZ = '0.1'
|
const MINIMUM_XBZZ = BZZ.fromDecimalString('0.1')
|
||||||
|
|
||||||
export default function TopUp(): ReactElement {
|
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 } = useContext(BeeContext)
|
const { nodeInfo, status, walletBalance } = 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()
|
||||||
@@ -49,8 +47,8 @@ export default function TopUp(): ReactElement {
|
|||||||
const canUpgradeToLightNode =
|
const canUpgradeToLightNode =
|
||||||
isDesktop &&
|
isDesktop &&
|
||||||
nodeInfo?.beeMode === BeeModes.ULTRA_LIGHT &&
|
nodeInfo?.beeMode === BeeModes.ULTRA_LIGHT &&
|
||||||
balance?.dai.toDecimal.gte(MINIMUM_XDAI) &&
|
walletBalance?.nativeTokenBalance.gte(MINIMUM_XDAI) &&
|
||||||
balance?.bzz.toDecimal.gte(MINIMUM_XBZZ)
|
walletBalance?.bzzBalance.gte(MINIMUM_XBZZ)
|
||||||
|
|
||||||
async function restart() {
|
async function restart() {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
@@ -67,7 +65,7 @@ export default function TopUp(): ReactElement {
|
|||||||
|
|
||||||
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
|
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
|
||||||
|
|
||||||
if (!balance) {
|
if (!walletBalance) {
|
||||||
return <Loading />
|
return <Loading />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
-56
@@ -1,18 +1,20 @@
|
|||||||
import {
|
import {
|
||||||
|
AllSettlements,
|
||||||
BeeModes,
|
BeeModes,
|
||||||
|
BZZ,
|
||||||
ChainState,
|
ChainState,
|
||||||
ChequebookAddressResponse,
|
ChequebookAddressResponse,
|
||||||
|
ChequebookBalanceResponse,
|
||||||
LastChequesResponse,
|
LastChequesResponse,
|
||||||
NodeAddresses,
|
NodeAddresses,
|
||||||
NodeInfo,
|
NodeInfo,
|
||||||
Peer,
|
Peer,
|
||||||
|
PeerBalance,
|
||||||
Topology,
|
Topology,
|
||||||
|
WalletBalance,
|
||||||
} from '@ethersphere/bee-js'
|
} from '@ethersphere/bee-js'
|
||||||
import { ReactChild, ReactElement, createContext, useContext, useEffect, useState } from 'react'
|
import { createContext, ReactChild, ReactElement, useContext, useEffect, useState } from 'react'
|
||||||
import { useLatestBeeRelease } from '../hooks/apiHooks'
|
import { useLatestBeeRelease } from '../hooks/apiHooks'
|
||||||
import { BzzToken } from '../models/BzzToken'
|
|
||||||
import { Token } from '../models/Token'
|
|
||||||
import type { Balance, ChequebookBalance, Settlements } from '../types'
|
|
||||||
import { Context as SettingsContext } from './Settings'
|
import { Context as SettingsContext } from './Settings'
|
||||||
|
|
||||||
const LAUNCH_GRACE_PERIOD = 15_000
|
const LAUNCH_GRACE_PERIOD = 15_000
|
||||||
@@ -50,13 +52,13 @@ interface ContextInterface {
|
|||||||
topology: Topology | null
|
topology: Topology | null
|
||||||
chequebookAddress: ChequebookAddressResponse | null
|
chequebookAddress: ChequebookAddressResponse | null
|
||||||
peers: Peer[] | null
|
peers: Peer[] | null
|
||||||
chequebookBalance: ChequebookBalance | null
|
chequebookBalance: ChequebookBalanceResponse | null
|
||||||
stake: BzzToken | null
|
stake: BZZ | null
|
||||||
peerBalances: Balance[] | null
|
peerBalances: PeerBalance[] | null
|
||||||
peerCheques: LastChequesResponse | null
|
peerCheques: LastChequesResponse | null
|
||||||
settlements: Settlements | null
|
settlements: AllSettlements | null
|
||||||
chainState: ChainState | null
|
chainState: ChainState | null
|
||||||
chainId: number | null
|
walletBalance: WalletBalance | null
|
||||||
latestBeeRelease: LatestBeeRelease | null
|
latestBeeRelease: LatestBeeRelease | null
|
||||||
isLoading: boolean
|
isLoading: boolean
|
||||||
lastUpdate: number | null
|
lastUpdate: number | null
|
||||||
@@ -86,7 +88,7 @@ const initialValues: ContextInterface = {
|
|||||||
peerCheques: null,
|
peerCheques: null,
|
||||||
settlements: null,
|
settlements: null,
|
||||||
chainState: null,
|
chainState: null,
|
||||||
chainId: null,
|
walletBalance: null,
|
||||||
latestBeeRelease: null,
|
latestBeeRelease: null,
|
||||||
isLoading: true,
|
isLoading: true,
|
||||||
lastUpdate: null,
|
lastUpdate: null,
|
||||||
@@ -107,7 +109,7 @@ function getStatus(
|
|||||||
apiHealth: boolean,
|
apiHealth: boolean,
|
||||||
topology: Topology | null,
|
topology: Topology | null,
|
||||||
chequebookAddress: ChequebookAddressResponse | null,
|
chequebookAddress: ChequebookAddressResponse | null,
|
||||||
chequebookBalance: ChequebookBalance | null,
|
chequebookBalance: ChequebookBalanceResponse | null,
|
||||||
error: Error | null,
|
error: Error | null,
|
||||||
startedAt: number,
|
startedAt: number,
|
||||||
): Status {
|
): Status {
|
||||||
@@ -173,13 +175,13 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
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)
|
||||||
const [chequebookBalance, setChequebookBalance] = useState<ChequebookBalance | null>(null)
|
const [chequebookBalance, setChequebookBalance] = useState<ChequebookBalanceResponse | null>(null)
|
||||||
const [stake, setStake] = useState<BzzToken | null>(null)
|
const [stake, setStake] = useState<BZZ | null>(null)
|
||||||
const [peerBalances, setPeerBalances] = useState<Balance[] | null>(null)
|
const [peerBalances, setPeerBalances] = useState<PeerBalance[] | null>(null)
|
||||||
const [peerCheques, setPeerCheques] = useState<LastChequesResponse | null>(null)
|
const [peerCheques, setPeerCheques] = useState<LastChequesResponse | null>(null)
|
||||||
const [settlements, setSettlements] = useState<Settlements | null>(null)
|
const [settlements, setSettlements] = useState<AllSettlements | null>(null)
|
||||||
const [chainState, setChainState] = useState<ChainState | null>(null)
|
const [chainState, setChainState] = useState<ChainState | null>(null)
|
||||||
const [chainId, setChainId] = useState<number | null>(null)
|
const [walletBalance, setWalletBalance] = useState<WalletBalance | null>(null)
|
||||||
const [startedAt] = useState(Date.now())
|
const [startedAt] = useState(Date.now())
|
||||||
|
|
||||||
const { latestBeeRelease } = useLatestBeeRelease()
|
const { latestBeeRelease } = useLatestBeeRelease()
|
||||||
@@ -232,38 +234,6 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
isRefreshing = true
|
isRefreshing = true
|
||||||
setError(null)
|
setError(null)
|
||||||
|
|
||||||
// Wrap the chequebook balance call to return BZZ values as Token object
|
|
||||||
const chequeBalanceWrapper = async () => {
|
|
||||||
const { totalBalance, availableBalance } = await beeApi.getChequebookBalance({ timeout: TIMEOUT })
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalBalance: new Token(totalBalance),
|
|
||||||
availableBalance: new Token(availableBalance),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap the balances call to return BZZ values as Token object
|
|
||||||
const peerBalanceWrapper = async () => {
|
|
||||||
const { balances } = await beeApi.getAllBalances({ timeout: TIMEOUT })
|
|
||||||
|
|
||||||
return balances.map(({ peer, balance }) => ({ peer, balance: new Token(balance) }))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap the settlements call to return BZZ values as Token object
|
|
||||||
const settlementsWrapper = async () => {
|
|
||||||
const { totalReceived, settlements, totalSent } = await beeApi.getAllSettlements({ timeout: TIMEOUT })
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalReceived: new Token(totalReceived),
|
|
||||||
totalSent: new Token(totalSent),
|
|
||||||
settlements: settlements.map(({ peer, received, sent }) => ({
|
|
||||||
peer,
|
|
||||||
received: new Token(received),
|
|
||||||
sent: new Token(sent),
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const promises = [
|
const promises = [
|
||||||
// API health
|
// API health
|
||||||
beeApi
|
beeApi
|
||||||
@@ -320,26 +290,29 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
// Wallet
|
// Wallet
|
||||||
beeApi
|
beeApi
|
||||||
.getWalletBalance({ timeout: TIMEOUT })
|
.getWalletBalance({ timeout: TIMEOUT })
|
||||||
.then(({ chainID }) => setChainId(chainID))
|
.then(setWalletBalance)
|
||||||
.catch(() => setChainId(null)),
|
.catch(() => setWalletBalance(null)),
|
||||||
|
|
||||||
// Chequebook balance
|
// Chequebook balance
|
||||||
chequeBalanceWrapper()
|
beeApi
|
||||||
|
.getChequebookBalance({ timeout: TIMEOUT })
|
||||||
.then(setChequebookBalance)
|
.then(setChequebookBalance)
|
||||||
.catch(() => setChequebookBalance(null)),
|
.catch(() => setChequebookBalance(null)),
|
||||||
|
|
||||||
beeApi
|
beeApi
|
||||||
.getStake({ timeout: TIMEOUT })
|
.getStake({ timeout: TIMEOUT })
|
||||||
.then(stake => setStake(new BzzToken(stake)))
|
.then(stake => setStake(stake))
|
||||||
.catch(() => setStake(null)),
|
.catch(() => setStake(null)),
|
||||||
|
|
||||||
// Peer balances
|
// Peer balances
|
||||||
peerBalanceWrapper()
|
beeApi
|
||||||
.then(setPeerBalances)
|
.getAllBalances({ timeout: TIMEOUT })
|
||||||
|
.then(x => setPeerBalances(x.balances))
|
||||||
.catch(() => setPeerBalances(null)),
|
.catch(() => setPeerBalances(null)),
|
||||||
|
|
||||||
// Settlements
|
// Settlements
|
||||||
settlementsWrapper()
|
beeApi
|
||||||
|
.getAllSettlements()
|
||||||
.then(setSettlements)
|
.then(setSettlements)
|
||||||
.catch(() => setSettlements(null)),
|
.catch(() => setSettlements(null)),
|
||||||
]
|
]
|
||||||
@@ -398,7 +371,7 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
peerCheques,
|
peerCheques,
|
||||||
settlements,
|
settlements,
|
||||||
chainState,
|
chainState,
|
||||||
chainId,
|
walletBalance,
|
||||||
latestBeeRelease,
|
latestBeeRelease,
|
||||||
isLoading,
|
isLoading,
|
||||||
lastUpdate,
|
lastUpdate,
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export function Provider({ children }: Props): ReactElement {
|
|||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
const stamps = await beeApi.getAllPostageBatch()
|
const stamps = await beeApi.getAllPostageBatch()
|
||||||
|
|
||||||
setStamps(stamps.filter(x => x.exists).map(enrichStamp))
|
setStamps(stamps.map(enrichStamp))
|
||||||
setLastUpdate(Date.now())
|
setLastUpdate(Date.now())
|
||||||
setError(null)
|
setError(null)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
import { createContext, ReactChild, ReactElement, useContext, useEffect, useState } from 'react'
|
|
||||||
import { Context as SettingsContext } from './Settings'
|
|
||||||
import { Context as BeeContext } from './Bee'
|
|
||||||
import { WalletAddress } from '../utils/wallet'
|
|
||||||
|
|
||||||
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, 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,3 +3,7 @@
|
|||||||
// expect(element).toHaveTextContent(/react/i)
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
import '@testing-library/jest-dom'
|
import '@testing-library/jest-dom'
|
||||||
|
|
||||||
|
import { TextDecoder, TextEncoder } from 'util'
|
||||||
|
|
||||||
|
Object.assign(global, { TextDecoder, TextEncoder })
|
||||||
|
|||||||
+2
-23
@@ -1,5 +1,4 @@
|
|||||||
import type { NodeAddresses, Topology } from '@ethersphere/bee-js'
|
import type { NodeAddresses, Topology } from '@ethersphere/bee-js'
|
||||||
import type { Token } from './models/Token'
|
|
||||||
import { CheckState } from './providers/Bee'
|
import { CheckState } from './providers/Bee'
|
||||||
|
|
||||||
export interface StatusHookCommon {
|
export interface StatusHookCommon {
|
||||||
@@ -12,31 +11,11 @@ export interface StatusNodeVersionHook extends StatusHookCommon {
|
|||||||
latestUrl: string
|
latestUrl: string
|
||||||
isLatestBeeVersion: boolean
|
isLatestBeeVersion: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusEthereumConnectionHook extends StatusHookCommon {
|
export interface StatusEthereumConnectionHook extends StatusHookCommon {
|
||||||
nodeAddresses: NodeAddresses | null
|
nodeAddresses: NodeAddresses | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusTopologyHook extends StatusHookCommon {
|
export interface StatusTopologyHook extends StatusHookCommon {
|
||||||
topology: Topology | null
|
topology: Topology | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChequebookBalance {
|
|
||||||
totalBalance: Token
|
|
||||||
availableBalance: Token
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Balance {
|
|
||||||
peer: string
|
|
||||||
balance: Token
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Settlement {
|
|
||||||
peer: string
|
|
||||||
received: Token
|
|
||||||
sent: Token
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Settlements {
|
|
||||||
totalReceived: Token
|
|
||||||
totalSent: Token
|
|
||||||
settlements: Settlement[]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export async function requestBzz(address: string): Promise<void> {
|
|
||||||
await axios.post(`https://xbzz-faucet.apyos.dev/xbzz/${address}`)
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
|
import { DAI } from '@ethersphere/bee-js'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { BEE_DESKTOP_LATEST_RELEASE_PAGE_API } from '../constants'
|
import { BEE_DESKTOP_LATEST_RELEASE_PAGE_API } from '../constants'
|
||||||
import { DaiToken } from '../models/DaiToken'
|
|
||||||
import { Token } from '../models/Token'
|
|
||||||
import { getJson, postJson } from './net'
|
import { getJson, postJson } from './net'
|
||||||
|
|
||||||
export interface BeeConfig {
|
export interface BeeConfig {
|
||||||
@@ -18,10 +17,10 @@ export interface BeeConfig {
|
|||||||
'blockchain-rpc-endpoint'?: string
|
'blockchain-rpc-endpoint'?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBzzPriceAsDai(desktopUrl: string): Promise<Token> {
|
export async function getBzzPriceAsDai(desktopUrl: string): Promise<DAI> {
|
||||||
const response = await axios.get(`${desktopUrl}/price`)
|
const response = await axios.get(`${desktopUrl}/price`)
|
||||||
|
|
||||||
return DaiToken.fromDecimal(response.data)
|
return DAI.fromDecimalString(response.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function upgradeToLightNode(desktopUrl: string, rpcProvider: string): Promise<BeeConfig> {
|
export function upgradeToLightNode(desktopUrl: string, rpcProvider: string): Promise<BeeConfig> {
|
||||||
@@ -53,8 +52,8 @@ export async function createGiftWallet(desktopUrl: string, address: string): Pro
|
|||||||
await postJson(`${desktopUrl}/gift-wallet/${address}`)
|
await postJson(`${desktopUrl}/gift-wallet/${address}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function performSwap(desktopUrl: string, daiAmount: string): Promise<void> {
|
export async function performSwap(desktopUrl: string, daiAmount: DAI): Promise<void> {
|
||||||
await postJson(`${desktopUrl}/swap`, { dai: daiAmount })
|
await postJson(`${desktopUrl}/swap`, { dai: daiAmount.toWeiString() })
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getLatestBeeDesktopVersion(): Promise<string> {
|
export async function getLatestBeeDesktopVersion(): Promise<string> {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BatchId, Bee, Reference } from '@ethersphere/bee-js'
|
import { BatchId, Bee, NULL_TOPIC, Reference } from '@ethersphere/bee-js'
|
||||||
import { Wallet } from 'ethers'
|
import { Wallet } from 'ethers'
|
||||||
import { uuidV4, waitUntilStampUsable } from '.'
|
import { uuidV4, waitUntilStampUsable } from '.'
|
||||||
import { Identity, IdentityType } from '../providers/Feeds'
|
import { Identity, IdentityType } from '../providers/Feeds'
|
||||||
@@ -80,18 +80,18 @@ async function getWallet(type: IdentityType, data: string, password?: string): P
|
|||||||
export async function updateFeed(
|
export async function updateFeed(
|
||||||
beeApi: Bee,
|
beeApi: Bee,
|
||||||
identity: Identity,
|
identity: Identity,
|
||||||
hash: string,
|
hash: Reference | string,
|
||||||
stamp: string,
|
stamp: BatchId | string,
|
||||||
password?: string,
|
password?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const wallet = await getWalletFromIdentity(identity, password)
|
const wallet = await getWalletFromIdentity(identity, password)
|
||||||
|
|
||||||
if (!identity.feedHash) {
|
if (!identity.feedHash) {
|
||||||
identity.feedHash = (await beeApi.createFeedManifest(stamp, 'sequence', '00'.repeat(32), wallet.address)).reference
|
identity.feedHash = (await beeApi.createFeedManifest(stamp, NULL_TOPIC, wallet.address)).toHex()
|
||||||
}
|
}
|
||||||
|
|
||||||
const writer = beeApi.makeFeedWriter('sequence', '00'.repeat(32), wallet.privateKey)
|
const writer = beeApi.makeFeedWriter(NULL_TOPIC, wallet.privateKey)
|
||||||
|
|
||||||
await waitUntilStampUsable(stamp as BatchId, beeApi)
|
await waitUntilStampUsable(stamp, beeApi)
|
||||||
await writer.upload(stamp, hash as Reference)
|
await writer.upload(stamp, hash)
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-47
@@ -1,8 +1,6 @@
|
|||||||
import { BatchId, Bee, PostageBatch } from '@ethersphere/bee-js'
|
import { BatchId, Bee, PostageBatch, Reference } from '@ethersphere/bee-js'
|
||||||
import { decodeCid } from '@ethersphere/swarm-cid'
|
|
||||||
import { BigNumber } from 'bignumber.js'
|
import { BigNumber } from 'bignumber.js'
|
||||||
import { BZZ_LINK_DOMAIN } from '../constants'
|
import { BZZ_LINK_DOMAIN } from '../constants'
|
||||||
import { Token } from '../models/Token'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if value is an integer
|
* Test if value is an integer
|
||||||
@@ -131,13 +129,7 @@ export function extractSwarmCid(s: string): string | undefined {
|
|||||||
|
|
||||||
const cid = matches[1]
|
const cid = matches[1]
|
||||||
try {
|
try {
|
||||||
const decodeResult = decodeCid(cid)
|
return new Reference(cid).toHex()
|
||||||
|
|
||||||
if (!decodeResult.type) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeResult.reference
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -171,48 +163,36 @@ export function formatEnum(string: string): string {
|
|||||||
return (string.charAt(0).toUpperCase() + string.slice(1).toLowerCase()).replaceAll('_', ' ')
|
return (string.charAt(0).toUpperCase() + string.slice(1).toLowerCase()).replaceAll('_', ' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function secondsToTimeString(seconds: number): string {
|
export function secondsToTimeString(seconds: number | bigint): string {
|
||||||
|
seconds = BigInt(seconds)
|
||||||
let unit = seconds
|
let unit = seconds
|
||||||
|
|
||||||
if (unit < 120) {
|
if (unit < 120) {
|
||||||
return `${seconds} seconds`
|
return `${seconds} seconds`
|
||||||
}
|
}
|
||||||
unit /= 60
|
unit /= BigInt(60)
|
||||||
|
|
||||||
if (unit < 120) {
|
if (unit < 120) {
|
||||||
return `${Math.round(unit)} minutes`
|
return `${unit} minutes`
|
||||||
}
|
}
|
||||||
unit /= 60
|
unit /= BigInt(60)
|
||||||
|
|
||||||
if (unit < 48) {
|
if (unit < 48) {
|
||||||
return `${Math.round(unit)} hours`
|
return `${unit} hours`
|
||||||
}
|
}
|
||||||
unit /= 24
|
unit /= BigInt(24)
|
||||||
|
|
||||||
if (unit < 14) {
|
if (unit < 14) {
|
||||||
return `${Math.round(unit)} days`
|
return `${unit} days`
|
||||||
}
|
}
|
||||||
unit /= 7
|
unit /= BigInt(7)
|
||||||
|
|
||||||
if (unit < 52) {
|
if (unit < 52) {
|
||||||
return `${Math.round(unit)} weeks`
|
return `${unit} weeks`
|
||||||
}
|
}
|
||||||
unit /= 52
|
unit /= BigInt(52)
|
||||||
|
|
||||||
return `${unit.toFixed(1)} years`
|
return `${unit} years`
|
||||||
}
|
|
||||||
|
|
||||||
export function convertAmountToSeconds(amount: number, pricePerBlock: number): number {
|
|
||||||
// TODO: blocktime should come directly from the blockchain as it may differ between different networks
|
|
||||||
const blockTime = 5 // On mainnet there is 5 seconds between blocks
|
|
||||||
|
|
||||||
// See https://github.com/ethersphere/bee/blob/66f079930d739182c4c79eb6008784afeeba1096/pkg/debugapi/postage.go#L410-L413
|
|
||||||
return (amount * blockTime) / pricePerBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
export function calculateStampPrice(depth: number, amount: bigint): Token {
|
|
||||||
// See https://github.com/ethersphere/bee/blob/66f079930d739182c4c79eb6008784afeeba1096/pkg/debugapi/postage.go#L410-L413
|
|
||||||
return new Token(amount * BigInt(2 ** depth)) // FIXME: the 2 ** depth should be performed on bigint already
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shortenText(text: string, length = 20, separator = '[…]'): string {
|
export function shortenText(text: string, length = 20, separator = '[…]'): string {
|
||||||
@@ -231,20 +211,11 @@ interface Options {
|
|||||||
timeout?: number
|
timeout?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitUntilStampUsable(batchId: BatchId, bee: Bee, options?: Options): Promise<PostageBatch> {
|
export function waitUntilStampUsable(batchId: BatchId | string, bee: Bee, options?: Options): Promise<PostageBatch> {
|
||||||
return waitForStamp(batchId, bee, 'usable', options)
|
return waitForStamp(batchId, bee, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitUntilStampExists(batchId: BatchId, bee: Bee, options?: Options): Promise<PostageBatch> {
|
async function waitForStamp(batchId: BatchId | string, bee: Bee, options?: Options): Promise<PostageBatch> {
|
||||||
return waitForStamp(batchId, bee, 'exists', options)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function waitForStamp(
|
|
||||||
batchId: BatchId,
|
|
||||||
bee: Bee,
|
|
||||||
field: 'exists' | 'usable',
|
|
||||||
options?: Options,
|
|
||||||
): Promise<PostageBatch> {
|
|
||||||
const timeout = options?.timeout || DEFAULT_STAMP_USABLE_TIMEOUT
|
const timeout = options?.timeout || DEFAULT_STAMP_USABLE_TIMEOUT
|
||||||
const pollingFrequency = options?.pollingFrequency || DEFAULT_POLLING_FREQUENCY
|
const pollingFrequency = options?.pollingFrequency || DEFAULT_POLLING_FREQUENCY
|
||||||
|
|
||||||
@@ -252,7 +223,9 @@ async function waitForStamp(
|
|||||||
try {
|
try {
|
||||||
const stamp = await bee.getPostageBatch(batchId)
|
const stamp = await bee.getPostageBatch(batchId)
|
||||||
|
|
||||||
if (stamp[field]) return stamp
|
if (stamp.usable) {
|
||||||
|
return stamp
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
import { Bee, Utils } from '@ethersphere/bee-js'
|
|
||||||
import { MantarayNode, MetadataMapping, Reference, loadAllNodes } from 'mantaray-js'
|
|
||||||
|
|
||||||
interface ValueNode extends MantarayNode {
|
|
||||||
getEntry: Reference
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ASCII code of the character `/`.
|
|
||||||
*
|
|
||||||
* This prefix of the root node holds metadata such as the index document.
|
|
||||||
*/
|
|
||||||
const INDEX_DOCUMENT_FORK_PREFIX = '47'
|
|
||||||
|
|
||||||
export class ManifestJs {
|
|
||||||
private bee: Bee
|
|
||||||
|
|
||||||
constructor(bee: Bee) {
|
|
||||||
this.bee = bee
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests whether a given Swarm hash is a valid mantaray manifest
|
|
||||||
*/
|
|
||||||
public async isManifest(hash: string): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
const data = await this.bee.downloadData(hash)
|
|
||||||
const node = new MantarayNode()
|
|
||||||
node.deserialize(data)
|
|
||||||
|
|
||||||
return true
|
|
||||||
} catch {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves `website-index-document` from a Swarm hash, or `null` if it is not present
|
|
||||||
*/
|
|
||||||
public async getIndexDocumentPath(hash: string): Promise<string | null> {
|
|
||||||
const metadata = await this.getRootSlashMetadata(hash)
|
|
||||||
|
|
||||||
if (!metadata) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata['website-index-document'] || null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves all paths with the associated hashes from a Swarm manifest
|
|
||||||
*/
|
|
||||||
public async getHashes(hash: string, options?: { exclude: string[] }): Promise<Record<string, string>> {
|
|
||||||
const data = await this.bee.downloadData(hash)
|
|
||||||
const node = new MantarayNode()
|
|
||||||
node.deserialize(data)
|
|
||||||
await loadAllNodes(this.load.bind(this), node)
|
|
||||||
const result: Record<string, string> = {}
|
|
||||||
this.extractHashes(result, node)
|
|
||||||
|
|
||||||
if (options?.exclude) {
|
|
||||||
for (const path of options.exclude) {
|
|
||||||
delete result[path]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves an arbitrary Swarm feed manifest to its latest update reference.
|
|
||||||
* @returns `/bzz` root manifest hash, or `Promise<null>` if hash is not a feed manifest
|
|
||||||
* @throws in case of network errors or bad input
|
|
||||||
*/
|
|
||||||
public async resolveFeedManifest(hash: string): Promise<string | null> {
|
|
||||||
const metadata = await this.getRootSlashMetadata(hash)
|
|
||||||
|
|
||||||
if (!metadata) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const owner = metadata['swarm-feed-owner']
|
|
||||||
const topic = metadata['swarm-feed-topic']
|
|
||||||
|
|
||||||
if (!owner || !topic) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = this.bee.makeFeedReader('sequence', topic, owner)
|
|
||||||
const response = await reader.download()
|
|
||||||
|
|
||||||
return response.reference
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getRootSlashMetadata(hash: string): Promise<MetadataMapping | null> {
|
|
||||||
const data = await this.bee.downloadData(hash)
|
|
||||||
const node = new MantarayNode()
|
|
||||||
node.deserialize(data)
|
|
||||||
|
|
||||||
if (!node.forks) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const fork = node.forks[INDEX_DOCUMENT_FORK_PREFIX]
|
|
||||||
|
|
||||||
if (!fork) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const metadataNode = fork.node
|
|
||||||
|
|
||||||
if (!metadataNode.IsWithMetadataType()) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
const metadata = metadataNode.getMetadata
|
|
||||||
|
|
||||||
if (!metadata) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
private extractHashes(result: Record<string, string>, node: MantarayNode, prefix = ''): void {
|
|
||||||
if (!node.forks) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for (const fork of Object.values(node.forks)) {
|
|
||||||
const path = prefix + this.bytesToUtf8(fork.prefix)
|
|
||||||
const childNode = fork.node
|
|
||||||
|
|
||||||
if (this.isValueNode(childNode, path)) {
|
|
||||||
result[path] = Utils.bytesToHex(childNode.getEntry)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childNode.isEdgeType()) {
|
|
||||||
this.extractHashes(result, childNode, path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private load(reference: Uint8Array) {
|
|
||||||
return this.bee.downloadData(Utils.bytesToHex(reference))
|
|
||||||
}
|
|
||||||
|
|
||||||
private bytesToUtf8(bytes: Uint8Array): string {
|
|
||||||
return new TextDecoder('utf-8').decode(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
private isValueNode(node: MantarayNode, path: string): node is ValueNode {
|
|
||||||
return !this.isRootSlash(node, path) && node.isValueType() && typeof node.getEntry !== 'undefined'
|
|
||||||
}
|
|
||||||
|
|
||||||
private isRootSlash(node: MantarayNode, path: string): boolean {
|
|
||||||
return path === '/' && node.IsWithMetadataType()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+39
-32
@@ -1,6 +1,7 @@
|
|||||||
import { debounce } from '@material-ui/core'
|
import { debounce } from '@material-ui/core'
|
||||||
import { Contract, providers, Wallet, BigNumber as BN } from 'ethers'
|
import { BZZ, DAI, EthAddress, PrivateKey } from '@ethersphere/bee-js'
|
||||||
import { bzzABI, BZZ_TOKEN_ADDRESS } from './bzz-abi'
|
import { BigNumber as BN, Contract, providers, Wallet } from 'ethers'
|
||||||
|
import { BZZ_TOKEN_ADDRESS, bzzABI } from './bzz-abi'
|
||||||
|
|
||||||
const NETWORK_ID = 100
|
const NETWORK_ID = 100
|
||||||
|
|
||||||
@@ -12,13 +13,12 @@ async function getNetworkChainId(url: string): Promise<number> {
|
|||||||
return network.chainId
|
return network.chainId
|
||||||
}
|
}
|
||||||
|
|
||||||
async function eth_getBalance(address: string, provider: providers.JsonRpcProvider): Promise<string> {
|
async function eth_getBalance(address: EthAddress | string, provider: providers.JsonRpcProvider): Promise<DAI> {
|
||||||
if (!address.startsWith('0x')) {
|
address = new EthAddress(address)
|
||||||
address = `0x${address}`
|
|
||||||
}
|
|
||||||
const balance = await provider.getBalance(address)
|
|
||||||
|
|
||||||
return balance.toString()
|
const balance = await provider.getBalance(address.toHex())
|
||||||
|
|
||||||
|
return DAI.fromWei(balance.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
async function eth_getBlockByNumber(provider: providers.JsonRpcProvider): Promise<string> {
|
async function eth_getBlockByNumber(provider: providers.JsonRpcProvider): Promise<string> {
|
||||||
@@ -28,17 +28,16 @@ async function eth_getBlockByNumber(provider: providers.JsonRpcProvider): Promis
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function eth_getBalanceERC20(
|
async function eth_getBalanceERC20(
|
||||||
address: string,
|
address: EthAddress | string,
|
||||||
provider: providers.JsonRpcProvider,
|
provider: providers.JsonRpcProvider,
|
||||||
tokenAddress = BZZ_TOKEN_ADDRESS,
|
tokenAddress = BZZ_TOKEN_ADDRESS,
|
||||||
): Promise<string> {
|
): Promise<BZZ> {
|
||||||
if (!address.startsWith('0x')) {
|
address = new EthAddress(address)
|
||||||
address = `0x${address}`
|
|
||||||
}
|
|
||||||
const contract = new Contract(tokenAddress, bzzABI, provider)
|
|
||||||
const balance = await contract.balanceOf(address)
|
|
||||||
|
|
||||||
return balance.toString()
|
const contract = new Contract(tokenAddress, bzzABI, provider)
|
||||||
|
const balance = await contract.balanceOf(address.toHex())
|
||||||
|
|
||||||
|
return BZZ.fromPLUR(balance.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TransferResponse {
|
interface TransferResponse {
|
||||||
@@ -47,29 +46,34 @@ interface TransferResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function estimateNativeTransferTransactionCost(
|
export async function estimateNativeTransferTransactionCost(
|
||||||
privateKey: string,
|
privateKey: PrivateKey | string,
|
||||||
jsonRpcProvider: string,
|
jsonRpcProvider: string,
|
||||||
): Promise<{ gasPrice: BN; totalCost: BN }> {
|
): Promise<{ gasPrice: DAI; totalCost: DAI }> {
|
||||||
|
privateKey = new PrivateKey(privateKey)
|
||||||
|
|
||||||
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
|
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
|
||||||
const gasLimit = '21000'
|
const gasLimit = '21000'
|
||||||
const gasPrice = await signer.getGasPrice()
|
const gasPrice = await signer.getGasPrice()
|
||||||
|
|
||||||
return { gasPrice, totalCost: gasPrice.mul(gasLimit) }
|
return { gasPrice: DAI.fromWei(gasPrice.toString()), totalCost: DAI.fromWei(gasPrice.mul(gasLimit).toString()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendNativeTransaction(
|
export async function sendNativeTransaction(
|
||||||
privateKey: string,
|
privateKey: PrivateKey | string,
|
||||||
to: string,
|
to: EthAddress | string,
|
||||||
value: string,
|
value: DAI,
|
||||||
jsonRpcProvider: string,
|
jsonRpcProvider: string,
|
||||||
externalGasPrice?: BN,
|
externalGasPrice?: DAI,
|
||||||
): Promise<TransferResponse> {
|
): Promise<TransferResponse> {
|
||||||
|
privateKey = new PrivateKey(privateKey)
|
||||||
|
to = new EthAddress(to)
|
||||||
|
|
||||||
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
|
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
|
||||||
const gasPrice = externalGasPrice ?? (await signer.getGasPrice())
|
const gasPrice = externalGasPrice ?? DAI.fromWei((await signer.getGasPrice()).toString())
|
||||||
const transaction = await signer.sendTransaction({
|
const transaction = await signer.sendTransaction({
|
||||||
to,
|
to: to.toHex(),
|
||||||
value: BN.from(value),
|
value: BN.from(value.toWeiString()),
|
||||||
gasPrice,
|
gasPrice: BN.from(gasPrice.toWeiString()),
|
||||||
gasLimit: BN.from(21000),
|
gasLimit: BN.from(21000),
|
||||||
type: 0,
|
type: 0,
|
||||||
})
|
})
|
||||||
@@ -79,11 +83,14 @@ export async function sendNativeTransaction(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function sendBzzTransaction(
|
export async function sendBzzTransaction(
|
||||||
privateKey: string,
|
privateKey: PrivateKey | string,
|
||||||
to: string,
|
to: EthAddress | string,
|
||||||
value: string,
|
value: BZZ,
|
||||||
jsonRpcProvider: string,
|
jsonRpcProvider: string,
|
||||||
): Promise<TransferResponse> {
|
): Promise<TransferResponse> {
|
||||||
|
privateKey = new PrivateKey(privateKey)
|
||||||
|
to = new EthAddress(to)
|
||||||
|
|
||||||
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
|
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
|
||||||
const gasPrice = await signer.getGasPrice()
|
const gasPrice = await signer.getGasPrice()
|
||||||
const bzz = new Contract(BZZ_TOKEN_ADDRESS, bzzABI, signer)
|
const bzz = new Contract(BZZ_TOKEN_ADDRESS, bzzABI, signer)
|
||||||
@@ -93,10 +100,10 @@ export async function sendBzzTransaction(
|
|||||||
return { transaction, receipt }
|
return { transaction, receipt }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function makeReadySigner(privateKey: string, jsonRpcProvider: string) {
|
async function makeReadySigner(privateKey: PrivateKey, jsonRpcProvider: string) {
|
||||||
const provider = new providers.JsonRpcProvider(jsonRpcProvider, NETWORK_ID)
|
const provider = new providers.JsonRpcProvider(jsonRpcProvider, NETWORK_ID)
|
||||||
await provider.ready
|
await provider.ready
|
||||||
const signer = new Wallet(privateKey, provider)
|
const signer = new Wallet(privateKey.toUint8Array(), provider)
|
||||||
|
|
||||||
return signer
|
return signer
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-36
@@ -1,39 +1,15 @@
|
|||||||
|
import { BZZ, DAI, EthAddress } from '@ethersphere/bee-js'
|
||||||
import { providers, Wallet } from 'ethers'
|
import { providers, Wallet } from 'ethers'
|
||||||
import { BzzToken } from '../models/BzzToken'
|
|
||||||
import { DaiToken } from '../models/DaiToken'
|
|
||||||
import { estimateNativeTransferTransactionCost, Rpc } from './rpc'
|
import { estimateNativeTransferTransactionCost, Rpc } from './rpc'
|
||||||
|
|
||||||
export class WalletAddress {
|
|
||||||
private constructor(
|
|
||||||
public address: string,
|
|
||||||
public bzz: BzzToken,
|
|
||||||
public dai: DaiToken,
|
|
||||||
public provider: providers.JsonRpcProvider,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
static async make(address: string, provider: providers.JsonRpcProvider): Promise<WalletAddress> {
|
|
||||||
const bzz = new BzzToken(await Rpc._eth_getBalanceERC20(address, provider))
|
|
||||||
const dai = new DaiToken(await Rpc._eth_getBalance(address, provider))
|
|
||||||
|
|
||||||
return new WalletAddress(address, bzz, dai, provider)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async refresh(): Promise<WalletAddress> {
|
|
||||||
this.bzz = new BzzToken(await Rpc._eth_getBalanceERC20(this.address, this.provider))
|
|
||||||
this.dai = new DaiToken(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
|
||||||
|
|
||||||
private constructor(
|
private constructor(
|
||||||
public wallet: Wallet,
|
public wallet: Wallet,
|
||||||
public bzz: BzzToken,
|
public bzz: BZZ,
|
||||||
public dai: DaiToken,
|
public dai: DAI,
|
||||||
public provider: providers.JsonRpcProvider,
|
public provider: providers.JsonRpcProvider,
|
||||||
) {
|
) {
|
||||||
this.address = wallet.address
|
this.address = wallet.address
|
||||||
@@ -44,32 +20,32 @@ export class ResolvedWallet {
|
|||||||
const wallet =
|
const wallet =
|
||||||
typeof privateKeyOrWallet === 'string' ? new Wallet(privateKeyOrWallet, provider) : privateKeyOrWallet
|
typeof privateKeyOrWallet === 'string' ? new Wallet(privateKeyOrWallet, provider) : privateKeyOrWallet
|
||||||
const address = wallet.address
|
const address = wallet.address
|
||||||
const bzz = new BzzToken(await Rpc._eth_getBalanceERC20(address, provider))
|
const bzz = await Rpc._eth_getBalanceERC20(address, provider)
|
||||||
const dai = new DaiToken(await Rpc._eth_getBalance(address, provider))
|
const dai = await Rpc._eth_getBalance(address, provider)
|
||||||
|
|
||||||
return new ResolvedWallet(wallet, bzz, dai, provider)
|
return new ResolvedWallet(wallet, bzz, dai, provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async refresh(): Promise<ResolvedWallet> {
|
public async refresh(): Promise<ResolvedWallet> {
|
||||||
this.bzz = new BzzToken(await Rpc._eth_getBalanceERC20(this.address, this.provider))
|
this.bzz = await Rpc._eth_getBalanceERC20(this.address, this.provider)
|
||||||
this.dai = new DaiToken(await Rpc._eth_getBalance(this.address, this.provider))
|
this.dai = await Rpc._eth_getBalance(this.address, this.provider)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
public async transfer(destination: string, jsonRpcProvider: string): Promise<void> {
|
public async transfer(destination: EthAddress | string, jsonRpcProvider: string): Promise<void> {
|
||||||
if (this.bzz.toDecimal.gt(0.05)) {
|
if (this.bzz.gt(BZZ.fromDecimalString('0.05'))) {
|
||||||
await Rpc.sendBzzTransaction(this.privateKey, destination, this.bzz.toString, jsonRpcProvider)
|
await Rpc.sendBzzTransaction(this.privateKey, destination, this.bzz, jsonRpcProvider)
|
||||||
await this.refresh()
|
await this.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { gasPrice, totalCost } = await estimateNativeTransferTransactionCost(this.privateKey, jsonRpcProvider)
|
const { gasPrice, totalCost } = await estimateNativeTransferTransactionCost(this.privateKey, jsonRpcProvider)
|
||||||
|
|
||||||
if (this.dai.toBigNumber.gt(totalCost.toString())) {
|
if (this.dai.gt(totalCost)) {
|
||||||
await Rpc.sendNativeTransaction(
|
await Rpc.sendNativeTransaction(
|
||||||
this.privateKey,
|
this.privateKey,
|
||||||
destination,
|
destination,
|
||||||
this.dai.toBigNumber.minus(totalCost.toString()).toString(),
|
this.dai.minus(totalCost),
|
||||||
jsonRpcProvider,
|
jsonRpcProvider,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user