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:
+18
-21
@@ -12,7 +12,6 @@ import { Provider as PlatformProvider } from './providers/Platform'
|
||||
import { Provider as SettingsProvider } from './providers/Settings'
|
||||
import { Provider as StampsProvider } from './providers/Stamps'
|
||||
import { Provider as TopUpProvider } from './providers/TopUp'
|
||||
import { Provider as BalanceProvider } from './providers/WalletBalance'
|
||||
import BaseRouter from './routes'
|
||||
import { theme } from './theme'
|
||||
|
||||
@@ -45,26 +44,24 @@ const App = ({
|
||||
>
|
||||
<TopUpProvider>
|
||||
<BeeProvider>
|
||||
<BalanceProvider>
|
||||
<StampsProvider>
|
||||
<FileProvider>
|
||||
<FeedsProvider>
|
||||
<PlatformProvider>
|
||||
<SnackbarProvider preventDuplicate anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}>
|
||||
<Router>
|
||||
<>
|
||||
<CssBaseline />
|
||||
<Dashboard errorReporting={errorReporting}>
|
||||
<BaseRouter />
|
||||
</Dashboard>
|
||||
</>
|
||||
</Router>
|
||||
</SnackbarProvider>
|
||||
</PlatformProvider>
|
||||
</FeedsProvider>
|
||||
</FileProvider>
|
||||
</StampsProvider>
|
||||
</BalanceProvider>
|
||||
<StampsProvider>
|
||||
<FileProvider>
|
||||
<FeedsProvider>
|
||||
<PlatformProvider>
|
||||
<SnackbarProvider preventDuplicate anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}>
|
||||
<Router>
|
||||
<>
|
||||
<CssBaseline />
|
||||
<Dashboard errorReporting={errorReporting}>
|
||||
<BaseRouter />
|
||||
</Dashboard>
|
||||
</>
|
||||
</Router>
|
||||
</SnackbarProvider>
|
||||
</PlatformProvider>
|
||||
</FeedsProvider>
|
||||
</FileProvider>
|
||||
</StampsProvider>
|
||||
</BeeProvider>
|
||||
</TopUpProvider>
|
||||
</SettingsProvider>
|
||||
|
||||
@@ -9,7 +9,6 @@ import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import Zap from 'remixicon-react/FlashlightLineIcon'
|
||||
import { Context as SettingsContext } from '../providers/Settings'
|
||||
import EthereumAddress from './EthereumAddress'
|
||||
|
||||
interface Props {
|
||||
peerId: string
|
||||
@@ -37,13 +36,9 @@ export default function CheckoutModal({ peerId, uncashedAmount }: Props): ReactE
|
||||
.cashoutLastCheque(peerId)
|
||||
.then(res => {
|
||||
setOpen(false)
|
||||
enqueueSnackbar(
|
||||
<span>
|
||||
Successfully cashed out cheque. Transaction
|
||||
<EthereumAddress hideBlockie transaction address={res} />
|
||||
</span>,
|
||||
{ variant: 'success' },
|
||||
)
|
||||
enqueueSnackbar(<span>Successfully cashed out cheque. Transaction {res.toHex()}</span>, {
|
||||
variant: 'success',
|
||||
})
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
console.error(e) // eslint-disable-line
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Utils } from '@ethersphere/bee-js'
|
||||
import { Typography } from '@material-ui/core/'
|
||||
import { EthAddress } from '@ethersphere/bee-js'
|
||||
import { ReactElement } from 'react'
|
||||
import Identicon from 'react-identicons'
|
||||
import { BLOCKCHAIN_EXPLORER_URL } from '../constants'
|
||||
@@ -8,7 +8,7 @@ import { Flex } from './Flex'
|
||||
import QRCodeModal from './QRCodeModal'
|
||||
|
||||
interface Props {
|
||||
address: string | undefined
|
||||
address: EthAddress | undefined
|
||||
hideBlockie?: boolean
|
||||
transaction?: boolean
|
||||
truncate?: boolean
|
||||
@@ -21,7 +21,7 @@ export default function EthereumAddress(props: Props): ReactElement {
|
||||
<Flex>
|
||||
{props.hideBlockie ? null : (
|
||||
<div style={{ paddingTop: '5px', marginRight: '10px' }}>
|
||||
<Identicon size={20} string={Utils.capitalizeAddressERC55(props.address)} />
|
||||
<Identicon size={20} string={props.address.toChecksum()} />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
@@ -45,8 +45,8 @@ export default function EthereumAddress(props: Props): ReactElement {
|
||||
{props.address}
|
||||
</a>
|
||||
</div>
|
||||
<QRCodeModal value={Utils.capitalizeAddressERC55(props.address)} label={'Ethereum Address'} />
|
||||
<ClipboardCopy value={Utils.capitalizeAddressERC55(props.address)} />
|
||||
<QRCodeModal value={props.address.toChecksum()} label={'Ethereum Address'} />
|
||||
<ClipboardCopy value={props.address.toChecksum()} />
|
||||
</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 { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import FilesIcon from 'remixicon-react/ArrowUpDownLineIcon'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Bee } from '@ethersphere/bee-js'
|
||||
import { Box } from '@material-ui/core'
|
||||
import Button from '@material-ui/core/Button'
|
||||
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 DialogTitle from '@material-ui/core/DialogTitle'
|
||||
import Input from '@material-ui/core/Input'
|
||||
import { BatchId, Bee } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, ReactNode, useState } from 'react'
|
||||
|
||||
@@ -13,14 +13,14 @@ interface Props {
|
||||
type: 'Topup' | 'Dilute'
|
||||
icon: ReactNode
|
||||
bee: Bee
|
||||
stamp: string
|
||||
stamp: BatchId
|
||||
}
|
||||
|
||||
export default function StampExtensionModal({ type, icon, bee, stamp }: Props): ReactElement {
|
||||
const [open, setOpen] = useState(false)
|
||||
const [amount, setAmount] = useState('')
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
const label = `${type} ${stamp.substring(0, 8)}`
|
||||
const label = `${type} ${stamp.toHex().substring(0, 8)}`
|
||||
|
||||
const handleClickOpen = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
setOpen(true)
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import { ReactElement, ReactNode, useState } from 'react'
|
||||
import Button from '@material-ui/core/Button'
|
||||
import Input from '@material-ui/core/Input'
|
||||
import Dialog from '@material-ui/core/Dialog'
|
||||
import DialogActions from '@material-ui/core/DialogActions'
|
||||
import DialogContent from '@material-ui/core/DialogContent'
|
||||
import DialogContentText from '@material-ui/core/DialogContentText'
|
||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||
import FormHelperText from '@material-ui/core/FormHelperText'
|
||||
import { Token } from '../models/Token'
|
||||
import type { BigNumber } from 'bignumber.js'
|
||||
import Input from '@material-ui/core/Input'
|
||||
import { BZZ, TransactionId } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, ReactNode, useState } from 'react'
|
||||
|
||||
interface Props {
|
||||
successMessage: string
|
||||
errorMessage: string
|
||||
dialogMessage: string
|
||||
label: string
|
||||
max?: BigNumber
|
||||
min?: BigNumber
|
||||
action: (amount: bigint) => Promise<string>
|
||||
max?: BZZ
|
||||
min?: BZZ
|
||||
action: (amount: BZZ) => Promise<TransactionId>
|
||||
icon?: ReactNode
|
||||
}
|
||||
|
||||
@@ -34,7 +33,7 @@ export default function WithdrawDepositModal({
|
||||
}: Props): ReactElement {
|
||||
const [open, setOpen] = useState(false)
|
||||
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 { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
@@ -51,7 +50,7 @@ export default function WithdrawDepositModal({
|
||||
if (amountToken === null) return
|
||||
|
||||
try {
|
||||
const transactionHash = await action(amountToken.toBigInt as bigint)
|
||||
const transactionHash = await action(amountToken)
|
||||
setOpen(false)
|
||||
enqueueSnackbar(`${successMessage} Transaction ${transactionHash}`, { variant: 'success' })
|
||||
} catch (e) {
|
||||
@@ -65,12 +64,12 @@ export default function WithdrawDepositModal({
|
||||
setAmount(value)
|
||||
setAmountError(null)
|
||||
try {
|
||||
const t = Token.fromDecimal(value)
|
||||
const t = BZZ.fromDecimalString(value)
|
||||
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) {
|
||||
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 Download from 'remixicon-react/DownloadLineIcon'
|
||||
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."
|
||||
label="Deposit"
|
||||
icon={<Download size="1rem" />}
|
||||
min={new BigNumber(0)}
|
||||
action={async (amount: bigint) => {
|
||||
min={BZZ.fromPLUR('1')}
|
||||
action={async (amount: BZZ) => {
|
||||
if (!beeApi) {
|
||||
throw new Error('Bee URL is not valid')
|
||||
}
|
||||
|
||||
const transactionHash = await beeApi.depositTokens(amount.toString())
|
||||
const transactionHash = await beeApi.depositTokens(amount)
|
||||
refresh()
|
||||
|
||||
return transactionHash
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import { BZZ } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
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."
|
||||
label="Stake"
|
||||
icon={<Download size="1rem" />}
|
||||
min={new BigNumber(0)}
|
||||
action={async (amount: bigint) => {
|
||||
min={BZZ.fromPLUR('1')}
|
||||
action={async (amount: BZZ) => {
|
||||
if (!beeApi) {
|
||||
throw new Error('Bee URL is not valid')
|
||||
}
|
||||
@@ -30,13 +30,13 @@ export default function StakeModal({ onStarted, onFinished }: Props): ReactEleme
|
||||
onStarted()
|
||||
|
||||
try {
|
||||
await beeApi.depositStake(amount.toString())
|
||||
const transactionHash = await beeApi.depositStake(amount)
|
||||
|
||||
return transactionHash
|
||||
} finally {
|
||||
refresh()
|
||||
onFinished()
|
||||
}
|
||||
|
||||
return 'unknown'
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import { BZZ } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import Upload from 'remixicon-react/UploadLineIcon'
|
||||
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."
|
||||
label="Withdraw"
|
||||
icon={<Upload size="1rem" />}
|
||||
min={new BigNumber(0)}
|
||||
action={async (amount: bigint) => {
|
||||
min={BZZ.fromPLUR('1')}
|
||||
action={async (amount: BZZ) => {
|
||||
if (!beeApi) {
|
||||
throw new Error('Bee URL is not valid')
|
||||
}
|
||||
|
||||
const transactionHash = await beeApi.withdrawTokens(amount.toString())
|
||||
const transactionHash = await beeApi.withdrawTokens(amount)
|
||||
refresh()
|
||||
|
||||
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 { Token } from '../models/Token'
|
||||
import { Balance, Settlement, Settlements } from '../types'
|
||||
import { makeRetriablePromise, unwrapPromiseSettlements } from '../utils'
|
||||
|
||||
interface UseAccountingHook {
|
||||
isLoadingUncashed: boolean
|
||||
totalUncashed: Token
|
||||
totalUncashed: BZZ
|
||||
accounting: Accounting[] | null
|
||||
}
|
||||
|
||||
export interface Accounting {
|
||||
peer: string
|
||||
uncashedAmount: Token
|
||||
balance: Token
|
||||
received: Token
|
||||
sent: Token
|
||||
total: Token
|
||||
uncashedAmount: BZZ
|
||||
balance: BZZ
|
||||
received: BZZ
|
||||
sent: BZZ
|
||||
total: BZZ
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -29,8 +27,8 @@ export interface Accounting {
|
||||
* @returns
|
||||
*/
|
||||
function mergeAccounting(
|
||||
balances: Balance[] | null,
|
||||
settlements?: Settlement[],
|
||||
balances: PeerBalance[] | null,
|
||||
settlements?: Settlements[],
|
||||
uncashedAmounts?: LastCashoutActionResponse[],
|
||||
): Accounting[] | null {
|
||||
// Settlements or balances are still loading or there is an error -> return null
|
||||
@@ -44,9 +42,9 @@ function mergeAccounting(
|
||||
(accounting[peer] = {
|
||||
peer,
|
||||
balance,
|
||||
sent: new Token('0'),
|
||||
received: new Token('0'),
|
||||
uncashedAmount: new Token('0'),
|
||||
sent: BZZ.fromPLUR('0'),
|
||||
received: BZZ.fromPLUR('0'),
|
||||
uncashedAmount: BZZ.fromPLUR('0'),
|
||||
total: balance,
|
||||
}),
|
||||
)
|
||||
@@ -57,7 +55,7 @@ function mergeAccounting(
|
||||
...accounting[peer],
|
||||
sent,
|
||||
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))
|
||||
|
||||
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 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
|
||||
})
|
||||
@@ -80,8 +80,8 @@ function mergeAccounting(
|
||||
|
||||
export const useAccounting = (
|
||||
beeApi: Bee | null,
|
||||
settlements: Settlements | null,
|
||||
balances: Balance[] | null,
|
||||
settlements: AllSettlements | null,
|
||||
balances: PeerBalance[] | null,
|
||||
): UseAccountingHook => {
|
||||
const [isLoadingUncashed, setIsloadingUncashed] = useState<boolean>(false)
|
||||
const [uncashedAmounts, setUncashedAmounts] = useState<LastCashoutActionResponse[] | undefined>(undefined)
|
||||
@@ -92,7 +92,7 @@ export const useAccounting = (
|
||||
|
||||
setIsloadingUncashed(true)
|
||||
const promises = settlements.settlements
|
||||
.filter(({ received }) => received.toBigNumber.gt('0'))
|
||||
.filter(({ received }) => received.gt(BZZ.fromPLUR('0')))
|
||||
.map(({ peer }) => makeRetriablePromise(() => beeApi.getLastCashoutAction(peer)))
|
||||
|
||||
Promise.allSettled(promises).then(settlements => {
|
||||
@@ -104,10 +104,8 @@ export const useAccounting = (
|
||||
|
||||
const accounting = mergeAccounting(balances, settlements?.settlements, uncashedAmounts)
|
||||
|
||||
let totalUncashed: Token = new Token('0')
|
||||
accounting?.forEach(
|
||||
({ uncashedAmount }) => (totalUncashed = new Token(totalUncashed.toBigNumber.plus(uncashedAmount.toBigNumber))),
|
||||
)
|
||||
let totalUncashed = BZZ.fromPLUR('0')
|
||||
accounting?.forEach(({ uncashedAmount }) => (totalUncashed = totalUncashed.plus(uncashedAmount)))
|
||||
|
||||
return {
|
||||
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 { BeeModes } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Context } from '../../providers/Bee'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Utils } from '@ethersphere/bee-js'
|
||||
import { Box } from '@material-ui/core'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import ExpandableList from '../../../components/ExpandableList'
|
||||
@@ -35,20 +34,20 @@ export function AccountChequebook(): ReactElement {
|
||||
<ExpandableList label="Chequebook" defaultOpen>
|
||||
<ExpandableListItem
|
||||
label="Total Balance"
|
||||
value={`${chequebookBalance?.totalBalance.toFixedDecimal()} xBZZ`}
|
||||
value={`${chequebookBalance?.totalBalance.toSignificantDigits(4)} xBZZ`}
|
||||
/>
|
||||
<ExpandableListItem
|
||||
label="Available Uncommitted Balance"
|
||||
value={`${chequebookBalance?.availableBalance.toFixedDecimal()} xBZZ`}
|
||||
value={`${chequebookBalance?.availableBalance.toSignificantDigits(4)} xBZZ`}
|
||||
/>
|
||||
<ExpandableListItem
|
||||
label="Total Cheques Amount Sent"
|
||||
value={`${settlements?.totalSent.toFixedDecimal()} xBZZ`}
|
||||
value={`${settlements?.totalSent.toSignificantDigits(4)} xBZZ`}
|
||||
/>
|
||||
<Box mb={2}>
|
||||
<ExpandableListItem
|
||||
label="Total Cheques Amount Received"
|
||||
value={`${settlements?.totalReceived.toFixedDecimal()} xBZZ`}
|
||||
value={`${settlements?.totalReceived.toSignificantDigits(4)} xBZZ`}
|
||||
/>
|
||||
</Box>
|
||||
<ExpandableListItemActions>
|
||||
@@ -60,7 +59,7 @@ export function AccountChequebook(): ReactElement {
|
||||
<ExpandableList label="Blockchain" defaultOpen>
|
||||
<ExpandableListItemKey
|
||||
label="Ethereum address"
|
||||
value={nodeAddresses?.ethereum ? Utils.capitalizeAddressERC55(nodeAddresses.ethereum) : ''}
|
||||
value={nodeAddresses?.ethereum ? nodeAddresses.ethereum.toChecksum() : ''}
|
||||
/>
|
||||
<ExpandableListItemKey
|
||||
label="Chequebook contract address"
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { Box } from '@material-ui/core'
|
||||
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import Download from 'remixicon-react/Download2LineIcon'
|
||||
import Info from 'remixicon-react/InformationLineIcon'
|
||||
import { useNavigate } from 'react-router'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import Trash from 'remixicon-react/DeleteBin7LineIcon'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Download from 'remixicon-react/Download2LineIcon'
|
||||
import Info from 'remixicon-react/InformationLineIcon'
|
||||
import ExpandableList from '../../../components/ExpandableList'
|
||||
import ExpandableListItem from '../../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../../components/ExpandableListItemKey'
|
||||
import { SwarmButton } from '../../../components/SwarmButton'
|
||||
import { Context as IdentityContext, Identity } from '../../../providers/Feeds'
|
||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||
import { Identity, Context as IdentityContext } from '../../../providers/Feeds'
|
||||
import { ROUTES } from '../../../routes'
|
||||
import { formatEnum } from '../../../utils'
|
||||
import { persistIdentitiesWithoutUpdate } from '../../../utils/identity'
|
||||
@@ -19,8 +22,6 @@ import { ExportFeedDialog } from '../../feeds/ExportFeedDialog'
|
||||
import { ImportFeedDialog } from '../../feeds/ImportFeedDialog'
|
||||
import { AccountNavigation } from '../AccountNavigation'
|
||||
import { Header } from '../Header'
|
||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
||||
|
||||
export function AccountFeeds(): ReactElement {
|
||||
const { identities, setIdentities } = useContext(IdentityContext)
|
||||
@@ -98,7 +99,7 @@ export function AccountFeeds(): ReactElement {
|
||||
<ExpandableListItem label="Identity type" value={formatEnum(x.type)} />
|
||||
</ExpandableList>
|
||||
</Box>
|
||||
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
|
||||
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
|
||||
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
|
||||
<Box mt={0.75}>
|
||||
<ExpandableListItemActions>
|
||||
|
||||
@@ -5,16 +5,14 @@ import ExpandableListItemActions from '../../../components/ExpandableListItemAct
|
||||
import { Loading } from '../../../components/Loading'
|
||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||
import StakeModal from '../../../containers/StakeModal'
|
||||
import { CheckState, Context as BeeContext } from '../../../providers/Bee'
|
||||
import { Context as BalanceContext } from '../../../providers/WalletBalance'
|
||||
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||
import { AccountNavigation } from '../AccountNavigation'
|
||||
import { Header } from '../Header'
|
||||
|
||||
export function AccountStaking(): ReactElement {
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const { status, stake } = useContext(BeeContext)
|
||||
const { balance } = useContext(BalanceContext)
|
||||
const { status, stake, walletBalance } = useContext(BeeContext)
|
||||
|
||||
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
|
||||
|
||||
@@ -31,15 +29,15 @@ export function AccountStaking(): ReactElement {
|
||||
<Header />
|
||||
<AccountNavigation active="STAKING" />
|
||||
<div>
|
||||
{loading || stake?.toDecimal === undefined ? (
|
||||
{loading || !stake ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<ExpandableList label="Staking" defaultOpen>
|
||||
<ExpandableListItem label="Staked BZZ" value={`${stake?.toSignificantDigits()} xBZZ`} />
|
||||
{balance?.bzz ? (
|
||||
<ExpandableListItem label="Staked BZZ" value={`${stake?.toSignificantDigits(4)} xBZZ`} />
|
||||
{walletBalance?.bzzBalance ? (
|
||||
<ExpandableListItem
|
||||
label="Available xBZZ balance"
|
||||
value={`${balance?.bzz.toSignificantDigits(4)} xBZZ`}
|
||||
value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`}
|
||||
/>
|
||||
) : null}
|
||||
<ExpandableListItemActions>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BeeModes, Utils } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
@@ -13,15 +13,13 @@ import { SwarmButton } from '../../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext, CheckState } from '../../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../../providers/Settings'
|
||||
import { Context as BalanceProvider } from '../../../providers/WalletBalance'
|
||||
import { ROUTES } from '../../../routes'
|
||||
import { AccountNavigation } from '../AccountNavigation'
|
||||
import { Header } from '../Header'
|
||||
|
||||
export function AccountWallet(): ReactElement {
|
||||
const { nodeAddresses, nodeInfo, status } = useContext(BeeContext)
|
||||
const { nodeAddresses, nodeInfo, status, walletBalance } = useContext(BeeContext)
|
||||
const { isDesktop } = useContext(SettingsContext)
|
||||
const { balance } = useContext(BalanceProvider)
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
@@ -53,20 +51,22 @@ export function AccountWallet(): ReactElement {
|
||||
)}
|
||||
</Grid>
|
||||
</Box>
|
||||
{balance && nodeAddresses ? (
|
||||
{walletBalance && nodeAddresses ? (
|
||||
<>
|
||||
<Box mb={0.25}>
|
||||
<ExpandableListItemKey
|
||||
label="Node wallet address"
|
||||
value={Utils.capitalizeAddressERC55(nodeAddresses.ethereum)}
|
||||
expanded
|
||||
/>
|
||||
<ExpandableListItemKey label="Node wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded />
|
||||
</Box>
|
||||
<Box mb={0.25}>
|
||||
<ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
|
||||
<ExpandableListItem
|
||||
label="xDAI balance"
|
||||
value={`${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`}
|
||||
/>
|
||||
</Box>
|
||||
<Box mb={2}>
|
||||
<ExpandableListItem label="xBZZ balance" value={`${balance.bzz.toSignificantDigits(4)} xBZZ`} />
|
||||
<ExpandableListItem
|
||||
label="xBZZ balance"
|
||||
value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { BZZ } from '@ethersphere/bee-js'
|
||||
import type { ReactElement } from 'react'
|
||||
import CashoutModal from '../../components/CashoutModal'
|
||||
import ExpandableList from '../../components/ExpandableList'
|
||||
@@ -5,44 +6,43 @@ import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import { Accounting } from '../../hooks/accounting'
|
||||
import type { Token } from '../../models/Token'
|
||||
|
||||
interface Props {
|
||||
isLoadingUncashed: boolean
|
||||
totalUncashed: Token
|
||||
totalUncashed: BZZ
|
||||
accounting: Accounting[] | null
|
||||
}
|
||||
|
||||
export default function PeerBalances({ accounting, isLoadingUncashed, totalUncashed }: Props): ReactElement | null {
|
||||
const uncashedPeers = accounting?.filter(({ uncashedAmount }) => uncashedAmount.toBigNumber.isGreaterThan('0')) || []
|
||||
const uncashedPeers = accounting?.filter(({ uncashedAmount }) => uncashedAmount.gt(BZZ.fromPLUR('0'))) || []
|
||||
|
||||
return (
|
||||
<ExpandableList
|
||||
label={`Peers (${uncashedPeers.length})`}
|
||||
info={`${totalUncashed.toFixedDecimal()} xBZZ (uncashed)`}
|
||||
info={`${totalUncashed.toSignificantDigits(4)} xBZZ (uncashed)`}
|
||||
>
|
||||
<ExpandableListItem label="Uncashed Amount Total" value={`${totalUncashed.toFixedDecimal()} xBZZ`} />
|
||||
<ExpandableListItem label="Uncashed Amount Total" value={`${totalUncashed.toSignificantDigits(4)} xBZZ`} />
|
||||
{uncashedPeers.map(({ peer, balance, received, sent, uncashedAmount, total }) => (
|
||||
<ExpandableList
|
||||
key={peer}
|
||||
label={`Peer ${peer.slice(0, 8)}[…]`}
|
||||
level={1}
|
||||
info={`${uncashedAmount.toFixedDecimal()} xBZZ (uncashed)`}
|
||||
info={`${uncashedAmount.toSignificantDigits(4)} xBZZ (uncashed)`}
|
||||
>
|
||||
<ExpandableListItemKey label="Peer ID" value={peer} />
|
||||
<ExpandableListItem label="Outstanding Balance" value={`${balance.toFixedDecimal()} xBZZ`} />
|
||||
<ExpandableListItem label="Outstanding Balance" value={`${balance.toSignificantDigits(4)} xBZZ`} />
|
||||
<ExpandableListItem
|
||||
label="Settlements Sent / Received"
|
||||
value={`-${sent.toFixedDecimal()} / ${received.toFixedDecimal()} xBZZ`}
|
||||
value={`-${sent.toSignificantDigits(4)} / ${received.toSignificantDigits(4)} xBZZ`}
|
||||
/>
|
||||
<ExpandableListItem label="Total" value={`${total.toFixedDecimal()} xBZZ`} />
|
||||
<ExpandableListItem label="Total" value={`${total.toSignificantDigits(4)} xBZZ`} />
|
||||
<ExpandableListItem
|
||||
label="Uncashed Amount"
|
||||
value={isLoadingUncashed ? 'loading…' : `${uncashedAmount.toFixedDecimal()} xBZZ`}
|
||||
value={isLoadingUncashed ? 'loading…' : `${uncashedAmount.toSignificantDigits(4)} xBZZ`}
|
||||
/>
|
||||
{uncashedAmount.toBigNumber.isGreaterThan('0') && (
|
||||
{uncashedAmount.gt(BZZ.fromPLUR('0')) && (
|
||||
<ExpandableListItemActions>
|
||||
<CashoutModal uncashedAmount={uncashedAmount.toFixedDecimal()} peerId={peer} />
|
||||
<CashoutModal uncashedAmount={uncashedAmount.toSignificantDigits(4)} peerId={peer} />
|
||||
</ExpandableListItemActions>
|
||||
)}
|
||||
</ExpandableList>
|
||||
|
||||
+13
-7
@@ -1,14 +1,13 @@
|
||||
import { Bee } from '@ethersphere/bee-js'
|
||||
import { FdpStorage } from '@fairdatasociety/fdp-storage'
|
||||
import { Pod } from '@fairdatasociety/fdp-storage/dist/pod/types'
|
||||
import { CircularProgress, Typography } from '@material-ui/core'
|
||||
import { Bee, MantarayNode } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useEffect, useState } from 'react'
|
||||
import ImportIcon from 'remixicon-react/AddBoxLineIcon'
|
||||
import PlusCircle from 'remixicon-react/AddCircleLineIcon'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { joinUrl } from '../../react-fs/Utility'
|
||||
import { ManifestJs } from '../../utils/manifest'
|
||||
import { FdpLogin } from './FdpLogin'
|
||||
import { FdpPods } from './FdpPods'
|
||||
import { Horizontal } from './Horizontal'
|
||||
@@ -25,7 +24,9 @@ async function makeFdp(): Promise<FdpStorage | null> {
|
||||
return null
|
||||
}
|
||||
|
||||
return new FdpStorage('http://localhost:1633', highestCapacityBatch.batchID, {
|
||||
// TODO: FDS has bad types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return new FdpStorage('http://localhost:1633', highestCapacityBatch.batchID.toHex() as any, {
|
||||
ensOptions: {
|
||||
rpcUrl: sepolia,
|
||||
contractAddresses: {
|
||||
@@ -120,11 +121,16 @@ export default function FDP(): ReactElement {
|
||||
}
|
||||
setCreatingPod(true)
|
||||
const bee = new Bee('http://localhost:1633')
|
||||
const manifestJs = new ManifestJs(bee)
|
||||
const entries = await manifestJs.getHashes(importHash)
|
||||
const manifest = await MantarayNode.unmarshal(bee, importHash)
|
||||
await manifest.loadRecursively(bee)
|
||||
const nodes = manifest.collect()
|
||||
await fdp.personalStorage.create(name)
|
||||
for (const [path, hash] of Object.entries(entries)) {
|
||||
await fdp.file.uploadData(name, joinUrl('/', path), await bee.downloadData(hash))
|
||||
for (const node of nodes) {
|
||||
await fdp.file.uploadData(
|
||||
name,
|
||||
joinUrl('/', node.fullPathString),
|
||||
(await bee.downloadData(node.targetAddress)).toUint8Array(),
|
||||
)
|
||||
}
|
||||
const pods = await fdp.personalStorage.list()
|
||||
setPods(pods.pods)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||
import { Form, Formik } from 'formik'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
@@ -108,7 +109,7 @@ export default function CreateNewFeed(): ReactElement {
|
||||
</Box>
|
||||
{values.type === 'V3' && <SwarmTextInput name="password" label="Password" password formik />}
|
||||
<Box mt={2}>
|
||||
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
|
||||
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
|
||||
</Box>
|
||||
<Box mt={2} sx={{ bgcolor: '#fcf2e8' }} p={2}>
|
||||
<Grid container justifyContent="space-between">
|
||||
|
||||
@@ -122,7 +122,7 @@ export default function UpdateFeed(): ReactElement {
|
||||
<Grid container>
|
||||
{stamps ? (
|
||||
<SwarmSelect
|
||||
options={stamps.map(x => ({ value: x.batchID, label: x.batchID.slice(0, 8) }))}
|
||||
options={stamps.map(x => ({ value: x.batchID.toHex(), label: x.batchID.toHex().slice(0, 8) }))}
|
||||
onChange={onStampChange}
|
||||
label="Stamp"
|
||||
/>
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { NULL_TOPIC } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import Info from 'remixicon-react/InformationLineIcon'
|
||||
import Trash from 'remixicon-react/DeleteBin7LineIcon'
|
||||
import { useNavigate } from 'react-router'
|
||||
import PlusSquare from 'remixicon-react/AddBoxLineIcon'
|
||||
import Trash from 'remixicon-react/DeleteBin7LineIcon'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import Info from 'remixicon-react/InformationLineIcon'
|
||||
import ExpandableList from '../../components/ExpandableList'
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as IdentityContext, Identity } from '../../providers/Feeds'
|
||||
import { Context as BeeContext, CheckState } from '../../providers/Bee'
|
||||
import { Identity, Context as IdentityContext } from '../../providers/Feeds'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { formatEnum } from '../../utils'
|
||||
import { persistIdentitiesWithoutUpdate } from '../../utils/identity'
|
||||
@@ -97,7 +98,7 @@ export default function Feeds(): ReactElement {
|
||||
<ExpandableListItem label="Identity type" value={formatEnum(x.type)} />
|
||||
</ExpandableList>
|
||||
</Box>
|
||||
<ExpandableListItemKey label="Topic" value={'00'.repeat(32)} />
|
||||
<ExpandableListItemKey label="Topic" value={NULL_TOPIC.toHex()} />
|
||||
{x.feedHash && <ExpandableListItemKey label="Feed hash" value={x.feedHash} />}
|
||||
<Box mt={0.75}>
|
||||
<ExpandableListItemActions>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Utils } from '@ethersphere/bee-js'
|
||||
import { Box } from '@material-ui/core'
|
||||
import { Reference } from '@ethersphere/bee-js'
|
||||
import { ReactElement } from 'react'
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
@@ -11,7 +11,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export function AssetSummary({ isWebsite, reference }: Props): ReactElement {
|
||||
const isHash = Utils.isHexString(reference) && reference.length === 64
|
||||
const isHash = Reference.isValid(reference)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Box } from '@material-ui/core'
|
||||
import { Tag } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext, useEffect, useRef, useState } from 'react'
|
||||
import { DocumentationText } from '../../components/DocumentationText'
|
||||
import { LinearProgressWithLabel } from '../../components/ProgressBar'
|
||||
import { Tag } from '@ethersphere/bee-js'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
|
||||
interface Props {
|
||||
reference: string
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BeeModes, Utils } from '@ethersphere/bee-js'
|
||||
import { BeeModes, MantarayNode, NULL_ADDRESS, Reference } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
@@ -11,7 +11,6 @@ import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { recognizeEnsOrSwarmHash, regexpEns } from '../../utils'
|
||||
import { HISTORY_KEYS, determineHistoryName, putHistory } from '../../utils/local-storage'
|
||||
import { ManifestJs } from '../../utils/manifest'
|
||||
import { FileNavigation } from './FileNavigation'
|
||||
|
||||
export function Download(): ReactElement {
|
||||
@@ -26,41 +25,43 @@ export function Download(): ReactElement {
|
||||
const navigate = useNavigate()
|
||||
|
||||
const validateChange = (value: string) => {
|
||||
if (
|
||||
Utils.isHexString(value, 64) ||
|
||||
Utils.isHexString(value, 128) ||
|
||||
!value.trim().length ||
|
||||
regexpEns.test(value)
|
||||
) {
|
||||
if (Reference.isValid(value) || regexpEns.test(value)) {
|
||||
setReferenceError(undefined)
|
||||
} else {
|
||||
setReferenceError('Incorrect format of swarm hash. Expected 64 or 128 hexstring characters or ENS domain.')
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test this for feeds, bzz, and bytes
|
||||
async function onSwarmIdentifier(identifier: string) {
|
||||
setLoading(true)
|
||||
|
||||
if (!beeApi) {
|
||||
setLoading(false)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
|
||||
try {
|
||||
const manifestJs = new ManifestJs(beeApi)
|
||||
const feedIdentifier = await manifestJs.resolveFeedManifest(identifier)
|
||||
let manifest = await MantarayNode.unmarshal(beeApi, identifier)
|
||||
await manifest.loadRecursively(beeApi)
|
||||
|
||||
if (feedIdentifier) {
|
||||
identifier = feedIdentifier
|
||||
}
|
||||
const isManifest = await manifestJs.isManifest(identifier)
|
||||
// If the manifest is a feed, resolve it and overwrite the manifest
|
||||
await manifest.resolveFeed(beeApi).then(
|
||||
async feed =>
|
||||
await feed.ifPresentAsync(async feedUpdate => {
|
||||
manifest = MantarayNode.unmarshalFromData(feedUpdate.payload.toUint8Array(), NULL_ADDRESS)
|
||||
await manifest.loadRecursively(beeApi)
|
||||
}),
|
||||
)
|
||||
|
||||
if (!isManifest) {
|
||||
throw Error('The specified hash does not contain valid content.')
|
||||
}
|
||||
const indexDocument = await manifestJs.getIndexDocumentPath(identifier)
|
||||
putHistory(HISTORY_KEYS.DOWNLOAD_HISTORY, identifier, determineHistoryName(identifier, indexDocument))
|
||||
const rootMetadata = manifest.getDocsMetadata()
|
||||
|
||||
putHistory(
|
||||
HISTORY_KEYS.DOWNLOAD_HISTORY,
|
||||
identifier,
|
||||
determineHistoryName(identifier, rootMetadata.indexDocument),
|
||||
)
|
||||
setUploadOrigin(defaultUploadOrigin)
|
||||
navigate(ROUTES.HASH.replace(':hash', identifier))
|
||||
} catch (error: unknown) {
|
||||
|
||||
@@ -27,7 +27,7 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
|
||||
<Menu anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} onClose={handleClose}>
|
||||
{stamps.map(stamp => (
|
||||
<MenuItem
|
||||
key={stamp.batchID}
|
||||
key={stamp.batchID.toHex()}
|
||||
onClick={() => {
|
||||
setSelected(stamp)
|
||||
handleClose()
|
||||
@@ -35,7 +35,7 @@ export default function SimpleMenu({ stamps, selectedStamp, setSelected }: Props
|
||||
selected={stamp.batchID === selectedStamp?.batchID}
|
||||
>
|
||||
<ListItemIcon>{stamp.usageText}</ListItemIcon>
|
||||
<Typography variant="body2">{stamp.batchID.slice(0, 8)}[…]</Typography>
|
||||
<Typography variant="body2">{stamp.batchID.toHex().slice(0, 8)}[…]</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
|
||||
+48
-35
@@ -1,4 +1,5 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { MantarayNode, NULL_ADDRESS } from '@ethersphere/bee-js'
|
||||
import { saveAs } from 'file-saver'
|
||||
import JSZip from 'jszip'
|
||||
import { useSnackbar } from 'notistack'
|
||||
@@ -12,11 +13,10 @@ import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { determineHistoryName, HISTORY_KEYS, putHistory } from '../../utils/local-storage'
|
||||
import { ManifestJs } from '../../utils/manifest'
|
||||
import { AssetPreview } from './AssetPreview'
|
||||
import { AssetSummary } from './AssetSummary'
|
||||
import { DownloadActionBar } from './DownloadActionBar'
|
||||
import { AssetSyncing } from './AssetSyncing'
|
||||
import { DownloadActionBar } from './DownloadActionBar'
|
||||
|
||||
export function Share(): ReactElement {
|
||||
const { apiUrl, beeApi } = useContext(SettingsContext)
|
||||
@@ -41,44 +41,57 @@ export function Share(): ReactElement {
|
||||
return
|
||||
}
|
||||
|
||||
const manifestJs = new ManifestJs(beeApi)
|
||||
const isManifest = await manifestJs.isManifest(reference)
|
||||
try {
|
||||
let manifest = await MantarayNode.unmarshal(beeApi, reference)
|
||||
await manifest.loadRecursively(beeApi)
|
||||
|
||||
if (!isManifest) {
|
||||
// If the manifest is a feed, resolve it and overwrite the manifest
|
||||
await manifest.resolveFeed(beeApi).then(
|
||||
async feed =>
|
||||
await feed.ifPresentAsync(async feedUpdate => {
|
||||
manifest = MantarayNode.unmarshalFromData(feedUpdate.payload.toUint8Array(), NULL_ADDRESS)
|
||||
await manifest.loadRecursively(beeApi)
|
||||
}),
|
||||
)
|
||||
|
||||
const entries = manifest.collectAndMap()
|
||||
delete entries[META_FILE_NAME]
|
||||
setSwarmEntries(entries)
|
||||
|
||||
const docsMetadata = manifest.getDocsMetadata()
|
||||
|
||||
// needed in catch block, shadows the outer variable
|
||||
const indexDocument = docsMetadata.indexDocument
|
||||
setIndexDocument(indexDocument)
|
||||
|
||||
try {
|
||||
const remoteMetadata = await beeApi.downloadFile(reference, META_FILE_NAME)
|
||||
const formattedMetadata = remoteMetadata.data.toJSON() as Metadata
|
||||
|
||||
if (formattedMetadata.isVideo || formattedMetadata.isImage) {
|
||||
setPreview(`${apiUrl}/bzz/${reference}`)
|
||||
}
|
||||
setMetadata({ ...formattedMetadata, hash })
|
||||
} catch (e) {
|
||||
// if metadata is not available or invalid go with the default one
|
||||
const count = Object.keys(entries).length
|
||||
setMetadata({
|
||||
hash,
|
||||
type: count > 1 ? 'folder' : 'unknown',
|
||||
name: reference,
|
||||
count,
|
||||
isWebsite: Boolean(indexDocument && /.*\.html?$/i.test(indexDocument)),
|
||||
isVideo: Boolean(indexDocument && /.*\.(mp4|webm|ogg|mp3|ogg|wav)$/i.test(indexDocument)),
|
||||
isImage: Boolean(indexDocument && /.*\.(jpg|jpeg|png|gif|webp|svg)$/i.test(indexDocument)),
|
||||
// naive assumption based on indexDocument, we don't want to download the whole manifest
|
||||
})
|
||||
}
|
||||
} catch {
|
||||
setNotFound(true)
|
||||
enqueueSnackbar('The specified hash does not contain valid content.', { variant: 'error' })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const entries = await manifestJs.getHashes(reference, { exclude: [META_FILE_NAME] })
|
||||
setSwarmEntries(entries)
|
||||
|
||||
const indexDocument = await manifestJs.getIndexDocumentPath(reference)
|
||||
setIndexDocument(indexDocument)
|
||||
|
||||
try {
|
||||
const remoteMetadata = await beeApi.downloadFile(reference, META_FILE_NAME)
|
||||
const formattedMetadata = JSON.parse(remoteMetadata.data.text()) as Metadata
|
||||
|
||||
if (formattedMetadata.isVideo || formattedMetadata.isImage) {
|
||||
setPreview(`${apiUrl}/bzz/${reference}`)
|
||||
}
|
||||
setMetadata({ ...formattedMetadata, hash })
|
||||
} catch (e) {
|
||||
// if metadata is not available or invalid go with the default one
|
||||
const count = Object.keys(entries).length
|
||||
setMetadata({
|
||||
hash,
|
||||
type: count > 1 ? 'folder' : 'unknown',
|
||||
name: reference,
|
||||
count,
|
||||
isWebsite: Boolean(indexDocument && /.*\.html?$/i.test(indexDocument)),
|
||||
isVideo: Boolean(indexDocument && /.*\.(mp4|webm|ogg|mp3|ogg|wav)$/i.test(indexDocument)),
|
||||
isImage: Boolean(indexDocument && /.*\.(jpg|jpeg|png|gif|webp|svg)$/i.test(indexDocument)),
|
||||
// naive assumption based on indexDocument, we don't want to donwload the whole manifest
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function onOpen() {
|
||||
@@ -119,7 +132,7 @@ export function Share(): ReactElement {
|
||||
} else {
|
||||
const zip = new JSZip()
|
||||
for (const [path, hash] of Object.entries(swarmEntries)) {
|
||||
zip.file(path, await beeApi.downloadData(hash))
|
||||
zip.file(path, (await beeApi.downloadData(hash)).toUint8Array())
|
||||
}
|
||||
const content = await zip.generateAsync({ type: 'blob' })
|
||||
saveAs(content, reference + '.zip')
|
||||
|
||||
@@ -114,10 +114,10 @@ export function Upload(): ReactElement {
|
||||
beeApi
|
||||
.uploadFiles(stamp.batchID, fls, { indexDocument, deferred: true })
|
||||
.then(hash => {
|
||||
putHistory(HISTORY_KEYS.UPLOAD_HISTORY, hash.reference, getAssetNameFromFiles(files))
|
||||
putHistory(HISTORY_KEYS.UPLOAD_HISTORY, hash.reference.toHex(), getAssetNameFromFiles(files))
|
||||
|
||||
if (uploadOrigin.origin === 'UPLOAD') {
|
||||
navigate(ROUTES.HASH.replace(':hash', hash.reference), { replace: true })
|
||||
navigate(ROUTES.HASH.replace(':hash', hash.reference.toHex()), { replace: true })
|
||||
} else {
|
||||
updateFeed(beeApi, identity as Identity, hash.reference, stamp.batchID, password as string).then(() => {
|
||||
persistIdentity(identities, identity as Identity)
|
||||
@@ -164,7 +164,7 @@ export function Upload(): ReactElement {
|
||||
<>
|
||||
<Box mb={2}>
|
||||
{hasAnyStamps && stampMode === 'SELECT' ? (
|
||||
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID} />
|
||||
<PostageStampSelector onSelect={stamp => setStamp(stamp)} defaultValue={stamp?.batchID.toHex()} />
|
||||
) : (
|
||||
<PostageStampAdvancedCreation onFinished={() => setStampMode('SELECT')} />
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Box, Tooltip, Typography } from '@material-ui/core'
|
||||
import { BZZ, DAI } from '@ethersphere/bee-js'
|
||||
import { Wallet } from 'ethers'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
@@ -11,20 +12,19 @@ import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { Loading } from '../../components/Loading'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { Token } from '../../models/Token'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as TopUpContext } from '../../providers/TopUp'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { createGiftWallet } from '../../utils/desktop'
|
||||
import { ResolvedWallet } from '../../utils/wallet'
|
||||
|
||||
const GIFT_WALLET_FUND_DAI_AMOUNT = Token.fromDecimal('0.1', 18)
|
||||
const GIFT_WALLET_FUND_BZZ_AMOUNT = Token.fromDecimal('0.5', 16)
|
||||
const GIFT_WALLET_FUND_DAI_AMOUNT = DAI.fromDecimalString('0.1')
|
||||
const GIFT_WALLET_FUND_BZZ_AMOUNT = BZZ.fromDecimalString('0.5')
|
||||
|
||||
export default function Index(): ReactElement {
|
||||
const { giftWallets, addGiftWallet } = useContext(TopUpContext)
|
||||
const { rpcProvider, desktopUrl } = useContext(SettingsContext)
|
||||
const { balance } = useContext(BalanceProvider)
|
||||
const { walletBalance } = useContext(BeeContext)
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [balances, setBalances] = useState<ResolvedWallet[]>([])
|
||||
@@ -67,13 +67,13 @@ export default function Index(): ReactElement {
|
||||
navigate(-1)
|
||||
}
|
||||
|
||||
if (!balance) {
|
||||
if (!walletBalance) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
const notEnoughFundsCheck =
|
||||
balance.dai.toBigNumber.isLessThanOrEqualTo(GIFT_WALLET_FUND_DAI_AMOUNT.toBigNumber) ||
|
||||
balance.bzz.toBigNumber.isLessThan(GIFT_WALLET_FUND_BZZ_AMOUNT.toBigNumber)
|
||||
walletBalance.nativeTokenBalance.lte(GIFT_WALLET_FUND_DAI_AMOUNT) ||
|
||||
walletBalance.bzzBalance.lt(GIFT_WALLET_FUND_BZZ_AMOUNT)
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -86,10 +86,13 @@ export default function Index(): ReactElement {
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box mb={0.25}>
|
||||
<ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
|
||||
<ExpandableListItem
|
||||
label="xDAI balance"
|
||||
value={`${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`}
|
||||
/>
|
||||
</Box>
|
||||
<Box mb={2}>
|
||||
<ExpandableListItem label="xBZZ balance" value={`${balance.bzz.toSignificantDigits(4)} xBZZ`} />
|
||||
<ExpandableListItem label="xBZZ balance" value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`} />
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
{balances.map((x, i) => (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { BZZ } from '@ethersphere/bee-js'
|
||||
import { useContext } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import ExchangeFunds from 'remixicon-react/ExchangeFundsLineIcon'
|
||||
@@ -9,10 +10,7 @@ export function ChequebookInfoCard() {
|
||||
const { chequebookBalance } = useContext(BeeContext)
|
||||
const navigate = useNavigate()
|
||||
|
||||
if (
|
||||
chequebookBalance?.availableBalance !== undefined &&
|
||||
chequebookBalance?.availableBalance.toBigNumber.isGreaterThan(0)
|
||||
) {
|
||||
if (chequebookBalance?.availableBalance !== undefined && chequebookBalance?.availableBalance.gt(BZZ.fromPLUR('0'))) {
|
||||
return (
|
||||
<Card
|
||||
buttonProps={{
|
||||
|
||||
@@ -4,21 +4,18 @@ import Upload from 'remixicon-react/UploadLineIcon'
|
||||
import Wallet from 'remixicon-react/Wallet3LineIcon'
|
||||
import Card from '../../components/Card'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { ROUTES } from '../../routes'
|
||||
|
||||
export function WalletInfoCard() {
|
||||
const { nodeInfo } = useContext(BeeContext)
|
||||
const { balance, error } = useContext(BalanceProvider)
|
||||
const { nodeInfo, walletBalance } = useContext(BeeContext)
|
||||
const navigate = useNavigate()
|
||||
|
||||
let balanceText = 'Loading...'
|
||||
|
||||
if (error) {
|
||||
balanceText = 'Could not load...'
|
||||
console.error(error) // eslint-disable-line
|
||||
} else if (balance) {
|
||||
balanceText = `${balance.bzz.toSignificantDigits(4)} xBZZ | ${balance.dai.toSignificantDigits(4)} xDAI`
|
||||
if (walletBalance) {
|
||||
balanceText = `${walletBalance.bzzBalance.toSignificantDigits(
|
||||
4,
|
||||
)} xBZZ | ${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`
|
||||
}
|
||||
|
||||
if (nodeInfo?.beeMode && ['light', 'full', 'dev'].includes(nodeInfo.beeMode)) {
|
||||
|
||||
@@ -13,7 +13,7 @@ import NodeInfoCard from './NodeInfoCard'
|
||||
import { WalletInfoCard } from './WalletInfoCard'
|
||||
|
||||
export default function Status(): ReactElement {
|
||||
const { beeVersion, status, topology, nodeInfo, chainId } = useContext(BeeContext)
|
||||
const { beeVersion, status, topology, nodeInfo, walletBalance } = useContext(BeeContext)
|
||||
const { isDesktop, desktopUrl } = useContext(SettingsContext)
|
||||
const { beeDesktopVersion } = useBeeDesktop(isDesktop, desktopUrl)
|
||||
const { newBeeDesktopVersion } = useNewBeeDesktopVersion(isDesktop, desktopUrl, false)
|
||||
@@ -64,7 +64,9 @@ export default function Status(): ReactElement {
|
||||
)}
|
||||
<ExpandableListItem label="Bee version" value={beeVersion} />
|
||||
<ExpandableListItem label="Mode" value={nodeInfo?.beeMode} />
|
||||
{chainId !== null && <ExpandableListItem label="Blockchain network" value={chainIdToName(chainId)} />}
|
||||
{walletBalance !== null && (
|
||||
<ExpandableListItem label="Blockchain network" value={chainIdToName(walletBalance.chainID)} />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
|
||||
@@ -9,7 +9,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export function PostageStamp({ stamp, shorten }: Props): ReactElement {
|
||||
const batchId = shorten ? stamp.batchID.slice(0, 8) : stamp.batchID
|
||||
const batchId = shorten ? stamp.batchID.toHex().slice(0, 8) : stamp.batchID
|
||||
const label = `${batchId}${stamp.label ? ` - ${stamp.label}` : ''}`
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, IconButton, Typography, createStyles, makeStyles } from '@material-ui/core'
|
||||
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
@@ -13,7 +13,7 @@ import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as StampsContext } from '../../providers/Stamps'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { calculateStampPrice, convertAmountToSeconds, secondsToTimeString, waitUntilStampExists } from '../../utils'
|
||||
import { secondsToTimeString } from '../../utils'
|
||||
import { getHumanReadableFileSize } from '../../utils/file'
|
||||
|
||||
interface Props {
|
||||
@@ -61,18 +61,22 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
function getTtl(amount: number): string {
|
||||
function getTtl(amount: bigint): string {
|
||||
const isCurrentPriceAvailable = chainState && chainState.currentPrice
|
||||
|
||||
if (amount <= 0 || !isCurrentPriceAvailable) {
|
||||
return '-'
|
||||
}
|
||||
|
||||
const pricePerBlock = Number.parseInt(chainState.currentPrice, 10)
|
||||
const pricePerBlock = chainState.currentPrice
|
||||
|
||||
return `${secondsToTimeString(
|
||||
convertAmountToSeconds(amount, pricePerBlock),
|
||||
)} (with price of ${pricePerBlock.toFixed(0)} PLUR per block)`
|
||||
try {
|
||||
return `${secondsToTimeString(
|
||||
Utils.getStampDuration(amount, pricePerBlock, 5).toSeconds(),
|
||||
)} (with price of ${pricePerBlock} PLUR per block)`
|
||||
} catch {
|
||||
return `0 seconds (with price of ${pricePerBlock} PLUR per block)`
|
||||
}
|
||||
}
|
||||
|
||||
function getPrice(depth: number, amount: bigint): string {
|
||||
@@ -82,9 +86,9 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
return '-'
|
||||
}
|
||||
|
||||
const price = calculateStampPrice(depth, amount)
|
||||
const price = Utils.getStampCost(depth, amount)
|
||||
|
||||
return `${price.toSignificantDigits()} xBZZ`
|
||||
return `${price.toSignificantDigits(4)} xBZZ`
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@@ -107,8 +111,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
immutableFlag: immutable,
|
||||
}
|
||||
|
||||
const batchId = await beeApi.createPostageBatch(amount.toString(), depth, options)
|
||||
await waitUntilStampExists(batchId, beeApi)
|
||||
await beeApi.createPostageBatch(amount.toString(), depth, options)
|
||||
await refresh()
|
||||
onFinished()
|
||||
} catch (e) {
|
||||
@@ -173,7 +176,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
return '-'
|
||||
}
|
||||
|
||||
const theoreticalMaximumVolume = getHumanReadableFileSize(Utils.getStampMaximumCapacityBytes(depth))
|
||||
const theoreticalMaximumVolume = getHumanReadableFileSize(Utils.getStampTheoreticalBytes(depth))
|
||||
const effectiveVolume = getHumanReadableFileSize(Utils.getStampEffectiveBytes(depth))
|
||||
|
||||
return (
|
||||
@@ -227,7 +230,7 @@ export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElemen
|
||||
<Box mt={0.25} sx={{ bgcolor: '#f6f6f6' }} p={2}>
|
||||
<Grid container justifyContent="space-between">
|
||||
<Typography>Corresponding TTL (Time to live)</Typography>
|
||||
<Typography>{!amountError && amountInput ? getTtl(Number.parseInt(amountInput, 10)) : '-'}</Typography>
|
||||
<Typography>{!amountError && amountInput ? getTtl(BigInt(amountInput)) : '-'}</Typography>
|
||||
</Grid>
|
||||
</Box>
|
||||
{amountError && <Typography>{amountError}</Typography>}
|
||||
|
||||
@@ -14,7 +14,7 @@ export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactEl
|
||||
if (!stamps) {
|
||||
return
|
||||
}
|
||||
const stamp = stamps.find(x => x.batchID === stampId)
|
||||
const stamp = stamps.find(x => x.batchID.toHex() === stampId)
|
||||
|
||||
if (stamp) {
|
||||
onSelect(stamp)
|
||||
@@ -24,8 +24,8 @@ export function PostageStampSelector({ onSelect, defaultValue }: Props): ReactEl
|
||||
return (
|
||||
<SwarmSelect
|
||||
options={(stamps || []).map(x => ({
|
||||
label: x.label ? x.batchID.slice(0, 8) + ' - ' + x.label : x.batchID.slice(0, 8),
|
||||
value: x.batchID,
|
||||
label: x.label ? x.batchID.toHex().slice(0, 8) + ' - ' + x.label : x.batchID.toHex().slice(0, 8),
|
||||
value: x.batchID.toHex(),
|
||||
}))}
|
||||
onChange={event => onChange(event.target.value as string)}
|
||||
defaultValue={defaultValue}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PostageBatchOptions, Utils } from '@ethersphere/bee-js'
|
||||
import { Box, Button, Grid, Slider, Typography } from '@material-ui/core'
|
||||
import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'
|
||||
import { Duration, PostageBatchOptions, Size, Utils } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
@@ -10,7 +10,7 @@ import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as StampsContext } from '../../providers/Stamps'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { calculateStampPrice, convertAmountToSeconds, secondsToTimeString, waitUntilStampExists } from '../../utils'
|
||||
import { secondsToTimeString } from '../../utils'
|
||||
|
||||
interface Props {
|
||||
onFinished: () => void
|
||||
@@ -46,8 +46,8 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
const { refresh } = useContext(StampsContext)
|
||||
const { beeApi } = useContext(SettingsContext)
|
||||
|
||||
const [depthInput, setDepthInput] = useState<number>(Utils.getDepthForCapacity(4))
|
||||
const [amountInput, setAmountInput] = useState<string>(Utils.getAmountForTtl(30))
|
||||
const [depthInput, setDepthInput] = useState<number>(Utils.getDepthForSize(Size.fromGigabytes(4)))
|
||||
const [amountInput, setAmountInput] = useState<bigint>(Utils.getAmountForDuration(Duration.fromDays(30), 26500, 5))
|
||||
const [labelInput, setLabelInput] = useState('')
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [buttonValue, setButtonValue] = useState(4)
|
||||
@@ -56,24 +56,24 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
if (typeof newValue !== 'number') {
|
||||
return
|
||||
}
|
||||
const amountValue = Utils.getAmountForTtl(newValue)
|
||||
const amountValue = Utils.getAmountForDuration(Duration.fromDays(newValue), 26500, 5)
|
||||
setAmountInput(amountValue)
|
||||
}
|
||||
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
function getTtl(amount: string): string {
|
||||
function getTtl(amount: bigint): string {
|
||||
const pricePerBlock = 24000
|
||||
|
||||
return `${secondsToTimeString(
|
||||
convertAmountToSeconds(parseInt(amount, 10), pricePerBlock),
|
||||
)} (with price of ${pricePerBlock.toFixed(0)} PLUR per block)`
|
||||
Utils.getStampDuration(amount, pricePerBlock, 5).toSeconds(),
|
||||
)} (with price of ${pricePerBlock} PLUR per block)`
|
||||
}
|
||||
|
||||
function getPrice(depth: number, amount: bigint): string {
|
||||
const price = calculateStampPrice(depth, amount)
|
||||
const price = Utils.getStampCost(depth, amount)
|
||||
|
||||
return `${price.toSignificantDigits()} xBZZ`
|
||||
return `${price.toSignificantDigits(4)} xBZZ`
|
||||
}
|
||||
|
||||
async function submit() {
|
||||
@@ -96,8 +96,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
immutableFlag: true,
|
||||
}
|
||||
|
||||
const batchId = await beeApi.createPostageBatch(amount.toString(), depth, options)
|
||||
await waitUntilStampExists(batchId, beeApi)
|
||||
await beeApi.createPostageBatch(amount.toString(), depth, options)
|
||||
await refresh()
|
||||
onFinished()
|
||||
} catch (e) {
|
||||
@@ -109,7 +108,7 @@ export function PostageStampStandardCreation({ onFinished }: Props): ReactElemen
|
||||
|
||||
function handleBatchSize(gigabytes: number) {
|
||||
setButtonValue(gigabytes)
|
||||
const capacity = Utils.getDepthForCapacity(gigabytes)
|
||||
const capacity = Utils.getDepthForSize(Size.fromGigabytes(gigabytes))
|
||||
setDepthInput(capacity)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import Dialog from '@material-ui/core/Dialog'
|
||||
import DialogContent from '@material-ui/core/DialogContent'
|
||||
import DialogTitle from '@material-ui/core/DialogTitle'
|
||||
import { Check, Clear } from '@material-ui/icons'
|
||||
import React, { ReactElement, useState } from 'react'
|
||||
import { ReactElement, useState } from 'react'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { SwarmSelect } from '../../components/SwarmSelect'
|
||||
import { EnrichedPostageBatch } from '../../providers/Stamps'
|
||||
@@ -39,7 +39,7 @@ export function SelectPostageStampModal({ stamps, onSelect, onClose }: Props): R
|
||||
const classes = useStyles()
|
||||
|
||||
function onChange(stampId: string) {
|
||||
const stamp = stamps.find(x => x.batchID === stampId)
|
||||
const stamp = stamps.find(x => x.batchID.toHex() === stampId)
|
||||
|
||||
if (stamp) {
|
||||
setSelectedStamp(stamp)
|
||||
@@ -66,7 +66,7 @@ export function SelectPostageStampModal({ stamps, onSelect, onClose }: Props): R
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<SwarmSelect
|
||||
options={stamps.map(x => ({ label: x.batchID, value: x.batchID }))}
|
||||
options={stamps.map(x => ({ label: x.batchID.toHex(), value: x.batchID.toHex() }))}
|
||||
onChange={event => onChange(event.target.value as string)}
|
||||
/>
|
||||
</DialogContent>
|
||||
|
||||
@@ -28,10 +28,10 @@ function StampsTable({ postageStamps }: Props): ReactElement | null {
|
||||
<ExpandableList label="Postage Stamps" defaultOpen>
|
||||
{postageStamps.map(stamp => (
|
||||
<ExpandableElement
|
||||
key={stamp.batchID}
|
||||
key={stamp.batchID.toHex()}
|
||||
expandable={
|
||||
<>
|
||||
<ExpandableListItemKey label="Batch ID" value={stamp.batchID} />
|
||||
<ExpandableListItemKey label="Batch ID" value={stamp.batchID.toHex()} />
|
||||
<ExpandableListItem label="Depth" value={String(stamp.depth)} />
|
||||
<ExpandableListItem
|
||||
label="Capacity"
|
||||
@@ -40,13 +40,9 @@ function StampsTable({ postageStamps }: Props): ReactElement | null {
|
||||
)}`}
|
||||
/>
|
||||
<ExpandableListItem label="Amount" value={parseInt(stamp.amount, 10).toLocaleString()} />
|
||||
<ExpandableListItem
|
||||
label="Expires in"
|
||||
value={stamp.batchTTL === -1 ? 'does not expire' : `${secondsToTimeString(stamp.batchTTL)}`}
|
||||
/>
|
||||
<ExpandableListItem label="Expires in" value={secondsToTimeString(stamp.duration.toSeconds())} />
|
||||
<ExpandableListItem label="Label" value={stamp.label} />
|
||||
<ExpandableListItem label="Usable" value={stamp.usable ? 'yes' : 'no'} />
|
||||
<ExpandableListItem label="Exists" value={stamp.exists ? 'yes' : 'no'} />
|
||||
<ExpandableListItem label="Immutable" value={stamp.immutableFlag ? 'yes' : 'no'} />
|
||||
<ExpandableListItem label="Purchase Block Number" value={stamp.blockNumber} />
|
||||
<ExpandableListItemActions>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { DAI } from '@ethersphere/bee-js'
|
||||
import { ReactElement, useContext } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
@@ -9,10 +10,9 @@ import { Loading } from '../../components/Loading'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDivider } from '../../components/SwarmDivider'
|
||||
import { Context } from '../../providers/Bee'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
|
||||
|
||||
const MINIMUM_XDAI = '0.5'
|
||||
const MINIMUM_XDAI = DAI.fromDecimalString('0.5')
|
||||
|
||||
interface Props {
|
||||
header: string
|
||||
@@ -22,15 +22,14 @@ interface Props {
|
||||
}
|
||||
|
||||
export default function Index({ header, title, p, next }: Props): ReactElement {
|
||||
const { nodeAddresses } = useContext(Context)
|
||||
const { balance } = useContext(BalanceProvider)
|
||||
const { nodeAddresses, walletBalance } = useContext(Context)
|
||||
const navigate = useNavigate()
|
||||
|
||||
if (!balance || !nodeAddresses) {
|
||||
if (!walletBalance || !nodeAddresses) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
const disabled = balance.dai.toDecimal.lt(MINIMUM_XDAI)
|
||||
const disabled = walletBalance.nativeTokenBalance.lt(MINIMUM_XDAI)
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -44,17 +43,19 @@ export default function Index({ header, title, p, next }: Props): ReactElement {
|
||||
<Box mb={4}>{p}</Box>
|
||||
<SwarmDivider mb={4} />
|
||||
<Box mb={0.25}>
|
||||
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum} expanded />
|
||||
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded />
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
<ExpandableListItem label="xDAI balance" value={balance.dai.toSignificantDigits(4)} />
|
||||
<ExpandableListItem label="xDAI balance" value={walletBalance.nativeTokenBalance.toSignificantDigits(4)} />
|
||||
</Box>
|
||||
<Grid container direction="row" justifyContent="space-between">
|
||||
<SwarmButton iconType={Check} onClick={() => navigate(next)} disabled={disabled}>
|
||||
Proceed
|
||||
</SwarmButton>
|
||||
{disabled ? (
|
||||
<Typography>Please deposit at least {MINIMUM_XDAI} xDAI to the address above in order to proceed.</Typography>
|
||||
<Typography>
|
||||
Please deposit at least {MINIMUM_XDAI.toSignificantDigits(4)} xDAI to the address above in order to proceed.
|
||||
</Typography>
|
||||
) : null}
|
||||
</Grid>
|
||||
</>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
@@ -14,16 +14,14 @@ import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDivider } from '../../components/SwarmDivider'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { sleepMs } from '../../utils'
|
||||
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
||||
import { ResolvedWallet } from '../../utils/wallet'
|
||||
|
||||
export function GiftCardFund(): ReactElement {
|
||||
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
|
||||
const { nodeAddresses, nodeInfo, walletBalance } = useContext(BeeContext)
|
||||
const { isDesktop, desktopUrl, rpcProvider, rpcProviderUrl } = useContext(SettingsContext)
|
||||
const { balance } = useContext(BalanceProvider)
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [wallet, setWallet] = useState<ResolvedWallet | null>(null)
|
||||
@@ -41,7 +39,7 @@ export function GiftCardFund(): ReactElement {
|
||||
ResolvedWallet.make(privateKeyString, rpcProvider).then(setWallet)
|
||||
}, [privateKeyString, rpcProvider])
|
||||
|
||||
if (!wallet || !balance) {
|
||||
if (!wallet || !walletBalance) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
@@ -108,13 +106,20 @@ export function GiftCardFund(): ReactElement {
|
||||
<ArrowDown size={24} color="#aaaaaa" />
|
||||
</Box>
|
||||
<Box mb={0.25}>
|
||||
<ExpandableListItemKey label="Node wallet address" value={nodeAddresses?.ethereum || 'N/A'} expanded />
|
||||
<ExpandableListItemKey
|
||||
label="Node wallet address"
|
||||
value={nodeAddresses?.ethereum.toChecksum() || 'N/A'}
|
||||
expanded
|
||||
/>
|
||||
</Box>
|
||||
<Box mb={0.25}>
|
||||
<ExpandableListItem label="xDAI balance" value={`${balance.dai.toSignificantDigits(4)} xDAI`} />
|
||||
<ExpandableListItem
|
||||
label="xDAI balance"
|
||||
value={`${walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI`}
|
||||
/>
|
||||
</Box>
|
||||
<Box mb={2}>
|
||||
<ExpandableListItem label="xBZZ balance" value={`${balance.bzz.toSignificantDigits(4)} xBZZ`} />
|
||||
<ExpandableListItem label="xBZZ balance" value={`${walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ`} />
|
||||
</Box>
|
||||
<SwarmButton iconType={Check} onClick={onFund} disabled={loading} loading={loading}>
|
||||
{canUpgradeToLightNode ? 'Send all funds to your node and Upgrade' : 'Send all funds to your node'}
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { BZZ, DAI } from '@ethersphere/bee-js'
|
||||
import { Wallet } from 'ethers'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { ProgressIndicator } from '../../components/ProgressIndicator'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDivider } from '../../components/SwarmDivider'
|
||||
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||
import { BzzToken } from '../../models/BzzToken'
|
||||
import { DaiToken } from '../../models/DaiToken'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { Rpc } from '../../utils/rpc'
|
||||
|
||||
@@ -29,10 +28,10 @@ export function GiftCardTopUpIndex(): ReactElement {
|
||||
setLoading(true)
|
||||
try {
|
||||
const wallet = new Wallet(giftCode, rpcProvider)
|
||||
const dai = new DaiToken(await Rpc._eth_getBalance(wallet.address, rpcProvider))
|
||||
const bzz = new BzzToken(await Rpc._eth_getBalanceERC20(wallet.address, rpcProvider))
|
||||
const dai = await Rpc._eth_getBalance(wallet.address, rpcProvider)
|
||||
const bzz = await Rpc._eth_getBalanceERC20(wallet.address, rpcProvider)
|
||||
|
||||
if (dai.toDecimal.lt(0.001) || bzz.toDecimal.lt(0.001)) {
|
||||
if (dai.lt(DAI.fromDecimalString('0.001')) || bzz.lt(BZZ.fromDecimalString('0.001'))) {
|
||||
throw Error('Gift wallet does not have enough funds')
|
||||
}
|
||||
enqueueSnackbar('Successfully verified gift wallet', { variant: 'success' })
|
||||
|
||||
+38
-43
@@ -1,5 +1,5 @@
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
@@ -13,14 +13,11 @@ import { Loading } from '../../components/Loading'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { SwarmDivider } from '../../components/SwarmDivider'
|
||||
import { SwarmTextInput } from '../../components/SwarmTextInput'
|
||||
import { BZZ_DECIMAL_PLACES, BzzToken } from '../../models/BzzToken'
|
||||
import { DaiToken } from '../../models/DaiToken'
|
||||
import { Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { sleepMs } from '../../utils'
|
||||
import { SwapError, isSwapError, wrapWithSwapError } from '../../utils/SwapError'
|
||||
import { isSwapError, SwapError, wrapWithSwapError } from '../../utils/SwapError'
|
||||
import {
|
||||
getBzzPriceAsDai,
|
||||
getDesktopConfiguration,
|
||||
@@ -31,8 +28,8 @@ import {
|
||||
import { Rpc } from '../../utils/rpc'
|
||||
import { TopUpProgressIndicator } from './TopUpProgressIndicator'
|
||||
|
||||
const MINIMUM_XDAI = '0.1'
|
||||
const MINIMUM_XBZZ = '0.1'
|
||||
const MINIMUM_XDAI = DAI.fromDecimalString('0.1')
|
||||
const MINIMUM_XBZZ = BZZ.fromDecimalString('0.1')
|
||||
|
||||
const GENERIC_SWAP_FAILED_ERROR_MESSAGE = 'Failed to swap. The full error is printed to the console.'
|
||||
|
||||
@@ -44,15 +41,14 @@ export function Swap({ header }: Props): ReactElement {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [hasSwapped, setSwapped] = useState(false)
|
||||
const [userInputSwap, setUserInputSwap] = useState<string | null>(null)
|
||||
const [price, setPrice] = useState(DaiToken.fromDecimal('0.6'))
|
||||
const [price, setPrice] = useState(DAI.fromDecimalString('0.3'))
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [daiToSwap, setDaiToSwap] = useState<DaiToken | null>(null)
|
||||
const [bzzAfterSwap, setBzzAfterSwap] = useState<BzzToken | null>(null)
|
||||
const [daiAfterSwap, setDaiAfterSwap] = useState<DaiToken | null>(null)
|
||||
const [daiToSwap, setDaiToSwap] = useState<DAI | null>(null)
|
||||
const [bzzAfterSwap, setBzzAfterSwap] = useState<BZZ | null>(null)
|
||||
const [daiAfterSwap, setDaiAfterSwap] = useState<DAI | null>(null)
|
||||
|
||||
const { rpcProviderUrl, isDesktop, desktopUrl } = useContext(SettingsContext)
|
||||
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
|
||||
const { balance } = useContext(BalanceProvider)
|
||||
const { nodeAddresses, nodeInfo, walletBalance } = useContext(BeeContext)
|
||||
|
||||
const navigate = useNavigate()
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
@@ -65,30 +61,30 @@ export function Swap({ header }: Props): ReactElement {
|
||||
|
||||
// Set the initial xDAI to swap
|
||||
useEffect(() => {
|
||||
if (!balance || userInputSwap) {
|
||||
if (!walletBalance || userInputSwap) {
|
||||
return
|
||||
}
|
||||
|
||||
const minimumOptimalValue = DaiToken.fromDecimal('1').plusBaseUnits(MINIMUM_XDAI).toDecimal
|
||||
const minimumOptimalValue = DAI.fromDecimalString('1').plus(MINIMUM_XDAI)
|
||||
|
||||
if (balance.dai.toDecimal.isGreaterThanOrEqualTo(minimumOptimalValue)) {
|
||||
if (walletBalance.nativeTokenBalance.gte(minimumOptimalValue)) {
|
||||
// Balance has at least 1 + MINIMUM_XDAI xDai
|
||||
setDaiToSwap(balance.dai.minusBaseUnits('1'))
|
||||
setDaiToSwap(walletBalance.nativeTokenBalance.minus(DAI.fromDecimalString('1')))
|
||||
} else {
|
||||
// Balance is low, halve the amount
|
||||
setDaiToSwap(new DaiToken(balance.dai.toBigNumber.dividedToIntegerBy(2)))
|
||||
setDaiToSwap(walletBalance.nativeTokenBalance.divide(BigInt(2)))
|
||||
}
|
||||
}, [balance, userInputSwap])
|
||||
}, [walletBalance, userInputSwap])
|
||||
|
||||
// Set the xDAI to swap based on user input
|
||||
useEffect(() => {
|
||||
setError(null)
|
||||
try {
|
||||
if (userInputSwap) {
|
||||
const dai = DaiToken.fromDecimal(userInputSwap)
|
||||
const dai = DAI.fromDecimalString(userInputSwap)
|
||||
setDaiToSwap(dai)
|
||||
|
||||
if (dai.toDecimal.lte(0)) {
|
||||
if (dai.lte(DAI.fromDecimalString('0'))) {
|
||||
setError('xDAI to swap must be a positive number')
|
||||
}
|
||||
}
|
||||
@@ -99,25 +95,23 @@ export function Swap({ header }: Props): ReactElement {
|
||||
|
||||
// Calculate the amount of tokens after swap
|
||||
useEffect(() => {
|
||||
if (!balance || !daiToSwap || error) {
|
||||
if (!walletBalance || !daiToSwap || error) {
|
||||
return
|
||||
}
|
||||
const daiAfterSwap = new DaiToken(balance.dai.toBigNumber.minus(daiToSwap.toBigNumber))
|
||||
const daiAfterSwap = walletBalance.nativeTokenBalance.minus(daiToSwap)
|
||||
setDaiAfterSwap(daiAfterSwap)
|
||||
const tokensConverted = BzzToken.fromDecimal(
|
||||
daiToSwap.toBigNumber.dividedBy(price.toBigNumber).decimalPlaces(BZZ_DECIMAL_PLACES),
|
||||
)
|
||||
const bzzAfterSwap = new BzzToken(tokensConverted.toBigNumber.plus(balance.bzz.toBigNumber))
|
||||
const tokensConverted = daiToSwap.exchangeToBZZ(price)
|
||||
const bzzAfterSwap = tokensConverted.plus(walletBalance.bzzBalance)
|
||||
setBzzAfterSwap(bzzAfterSwap)
|
||||
|
||||
if (daiAfterSwap.toDecimal.lt(MINIMUM_XDAI)) {
|
||||
setError(`Must keep at least ${MINIMUM_XDAI} xDAI after swap!`)
|
||||
} else if (bzzAfterSwap.toDecimal.lt(MINIMUM_XBZZ)) {
|
||||
setError(`Must have at least ${MINIMUM_XBZZ} xBZZ after swap!`)
|
||||
if (daiAfterSwap.lt(MINIMUM_XDAI)) {
|
||||
setError(`Must keep at least ${MINIMUM_XDAI.toSignificantDigits(4)} xDAI after swap!`)
|
||||
} else if (bzzAfterSwap.lt(MINIMUM_XBZZ)) {
|
||||
setError(`Must have at least ${MINIMUM_XBZZ.toSignificantDigits(4)} xBZZ after swap!`)
|
||||
}
|
||||
}, [error, balance, daiToSwap, price])
|
||||
}, [error, walletBalance, daiToSwap, price])
|
||||
|
||||
if (!balance || !nodeAddresses || !daiToSwap || !bzzAfterSwap || !daiAfterSwap) {
|
||||
if (!walletBalance || !nodeAddresses || !daiToSwap || !bzzAfterSwap || !daiAfterSwap) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
@@ -135,9 +129,9 @@ export function Swap({ header }: Props): ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
async function sendSwapRequest(daiToSwap: DaiToken) {
|
||||
async function sendSwapRequest(daiToSwap: DAI) {
|
||||
try {
|
||||
await performSwap(desktopUrl, daiToSwap.toString)
|
||||
await performSwap(desktopUrl, daiToSwap)
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
@@ -145,7 +139,7 @@ export function Swap({ header }: Props): ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
async function performSwapWithChecks(daiToSwap: DaiToken) {
|
||||
async function performSwapWithChecks(daiToSwap: DAI) {
|
||||
if (!localStorage.getItem('apiKey')) {
|
||||
throw new SwapError('API key is not set, reopen dashboard through Swarm Desktop')
|
||||
}
|
||||
@@ -186,7 +180,9 @@ export function Swap({ header }: Props): ReactElement {
|
||||
: 'Successfully swapped. Balances will refresh soon. You may now navigate away.'
|
||||
enqueueSnackbar(message, { variant: 'success' })
|
||||
|
||||
if (canUpgradeToLightNode) await restart()
|
||||
if (canUpgradeToLightNode) {
|
||||
await restart()
|
||||
}
|
||||
} catch (error) {
|
||||
if (isSwapError(error)) {
|
||||
// we have a custom and user friendly error message
|
||||
@@ -201,7 +197,6 @@ export function Swap({ header }: Props): ReactElement {
|
||||
console.error(error) // eslint-disable-line
|
||||
}
|
||||
} finally {
|
||||
balance?.refresh()
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
@@ -217,15 +212,15 @@ export function Swap({ header }: Props): ReactElement {
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
<Typography>
|
||||
You need to swap xDAI to xBZZ in order to use Swarm. Make sure to keep at least {MINIMUM_XDAI} xDAI in order
|
||||
to pay for transaction costs on the network.
|
||||
You need to swap xDAI to xBZZ in order to use Swarm. Make sure to keep at least{' '}
|
||||
{MINIMUM_XDAI.toSignificantDigits(4)} xDAI in order to pay for transaction costs on the network.
|
||||
</Typography>
|
||||
</Box>
|
||||
<SwarmDivider mb={4} />
|
||||
<Box mb={4}>
|
||||
<Typography>
|
||||
Your current balance is {balance.dai.toSignificantDigits(4)} xDAI and {balance.bzz.toSignificantDigits(4)}{' '}
|
||||
xBZZ.
|
||||
Your current balance is {walletBalance.nativeTokenBalance.toSignificantDigits(4)} xDAI and{' '}
|
||||
{walletBalance.bzzBalance.toSignificantDigits(4)} xBZZ.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box mb={4}>
|
||||
@@ -242,7 +237,7 @@ export function Swap({ header }: Props): ReactElement {
|
||||
<ArrowDown size={24} color="#aaaaaa" />
|
||||
</Box>
|
||||
<Box mb={0.25}>
|
||||
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum} expanded />
|
||||
<ExpandableListItemKey label="Funding wallet address" value={nodeAddresses.ethereum.toChecksum()} expanded />
|
||||
</Box>
|
||||
<Box mb={0.25}>
|
||||
<ExpandableListItem
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
||||
import { BeeModes, BZZ, DAI } from '@ethersphere/bee-js'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
@@ -15,7 +15,6 @@ import { SwarmButton } from '../../components/SwarmButton'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { Context as BeeContext, CheckState } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
||||
|
||||
@@ -33,15 +32,14 @@ const useStyles = makeStyles(() =>
|
||||
}),
|
||||
)
|
||||
|
||||
const MINIMUM_XDAI = '0.05'
|
||||
const MINIMUM_XBZZ = '0.1'
|
||||
const MINIMUM_XDAI = DAI.fromDecimalString('0.05')
|
||||
const MINIMUM_XBZZ = BZZ.fromDecimalString('0.1')
|
||||
|
||||
export default function TopUp(): ReactElement {
|
||||
const navigate = useNavigate()
|
||||
const styles = useStyles()
|
||||
const { isDesktop, desktopUrl } = useContext(SettingsContext)
|
||||
const { nodeInfo, status } = useContext(BeeContext)
|
||||
const { balance } = useContext(BalanceProvider)
|
||||
const { nodeInfo, status, walletBalance } = useContext(BeeContext)
|
||||
const { rpcProviderUrl } = useContext(SettingsContext)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
@@ -49,8 +47,8 @@ export default function TopUp(): ReactElement {
|
||||
const canUpgradeToLightNode =
|
||||
isDesktop &&
|
||||
nodeInfo?.beeMode === BeeModes.ULTRA_LIGHT &&
|
||||
balance?.dai.toDecimal.gte(MINIMUM_XDAI) &&
|
||||
balance?.bzz.toDecimal.gte(MINIMUM_XBZZ)
|
||||
walletBalance?.nativeTokenBalance.gte(MINIMUM_XDAI) &&
|
||||
walletBalance?.bzzBalance.gte(MINIMUM_XBZZ)
|
||||
|
||||
async function restart() {
|
||||
setLoading(true)
|
||||
@@ -67,7 +65,7 @@ export default function TopUp(): ReactElement {
|
||||
|
||||
if (status.all === CheckState.ERROR) return <TroubleshootConnectionCard />
|
||||
|
||||
if (!balance) {
|
||||
if (!walletBalance) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
|
||||
+29
-56
@@ -1,18 +1,20 @@
|
||||
import {
|
||||
AllSettlements,
|
||||
BeeModes,
|
||||
BZZ,
|
||||
ChainState,
|
||||
ChequebookAddressResponse,
|
||||
ChequebookBalanceResponse,
|
||||
LastChequesResponse,
|
||||
NodeAddresses,
|
||||
NodeInfo,
|
||||
Peer,
|
||||
PeerBalance,
|
||||
Topology,
|
||||
WalletBalance,
|
||||
} 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 { BzzToken } from '../models/BzzToken'
|
||||
import { Token } from '../models/Token'
|
||||
import type { Balance, ChequebookBalance, Settlements } from '../types'
|
||||
import { Context as SettingsContext } from './Settings'
|
||||
|
||||
const LAUNCH_GRACE_PERIOD = 15_000
|
||||
@@ -50,13 +52,13 @@ interface ContextInterface {
|
||||
topology: Topology | null
|
||||
chequebookAddress: ChequebookAddressResponse | null
|
||||
peers: Peer[] | null
|
||||
chequebookBalance: ChequebookBalance | null
|
||||
stake: BzzToken | null
|
||||
peerBalances: Balance[] | null
|
||||
chequebookBalance: ChequebookBalanceResponse | null
|
||||
stake: BZZ | null
|
||||
peerBalances: PeerBalance[] | null
|
||||
peerCheques: LastChequesResponse | null
|
||||
settlements: Settlements | null
|
||||
settlements: AllSettlements | null
|
||||
chainState: ChainState | null
|
||||
chainId: number | null
|
||||
walletBalance: WalletBalance | null
|
||||
latestBeeRelease: LatestBeeRelease | null
|
||||
isLoading: boolean
|
||||
lastUpdate: number | null
|
||||
@@ -86,7 +88,7 @@ const initialValues: ContextInterface = {
|
||||
peerCheques: null,
|
||||
settlements: null,
|
||||
chainState: null,
|
||||
chainId: null,
|
||||
walletBalance: null,
|
||||
latestBeeRelease: null,
|
||||
isLoading: true,
|
||||
lastUpdate: null,
|
||||
@@ -107,7 +109,7 @@ function getStatus(
|
||||
apiHealth: boolean,
|
||||
topology: Topology | null,
|
||||
chequebookAddress: ChequebookAddressResponse | null,
|
||||
chequebookBalance: ChequebookBalance | null,
|
||||
chequebookBalance: ChequebookBalanceResponse | null,
|
||||
error: Error | null,
|
||||
startedAt: number,
|
||||
): Status {
|
||||
@@ -173,13 +175,13 @@ export function Provider({ children }: Props): ReactElement {
|
||||
const [topology, setNodeTopology] = useState<Topology | null>(null)
|
||||
const [chequebookAddress, setChequebookAddress] = useState<ChequebookAddressResponse | null>(null)
|
||||
const [peers, setPeers] = useState<Peer[] | null>(null)
|
||||
const [chequebookBalance, setChequebookBalance] = useState<ChequebookBalance | null>(null)
|
||||
const [stake, setStake] = useState<BzzToken | null>(null)
|
||||
const [peerBalances, setPeerBalances] = useState<Balance[] | null>(null)
|
||||
const [chequebookBalance, setChequebookBalance] = useState<ChequebookBalanceResponse | null>(null)
|
||||
const [stake, setStake] = useState<BZZ | null>(null)
|
||||
const [peerBalances, setPeerBalances] = useState<PeerBalance[] | 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 [chainId, setChainId] = useState<number | null>(null)
|
||||
const [walletBalance, setWalletBalance] = useState<WalletBalance | null>(null)
|
||||
const [startedAt] = useState(Date.now())
|
||||
|
||||
const { latestBeeRelease } = useLatestBeeRelease()
|
||||
@@ -232,38 +234,6 @@ export function Provider({ children }: Props): ReactElement {
|
||||
isRefreshing = true
|
||||
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 = [
|
||||
// API health
|
||||
beeApi
|
||||
@@ -320,26 +290,29 @@ export function Provider({ children }: Props): ReactElement {
|
||||
// Wallet
|
||||
beeApi
|
||||
.getWalletBalance({ timeout: TIMEOUT })
|
||||
.then(({ chainID }) => setChainId(chainID))
|
||||
.catch(() => setChainId(null)),
|
||||
.then(setWalletBalance)
|
||||
.catch(() => setWalletBalance(null)),
|
||||
|
||||
// Chequebook balance
|
||||
chequeBalanceWrapper()
|
||||
beeApi
|
||||
.getChequebookBalance({ timeout: TIMEOUT })
|
||||
.then(setChequebookBalance)
|
||||
.catch(() => setChequebookBalance(null)),
|
||||
|
||||
beeApi
|
||||
.getStake({ timeout: TIMEOUT })
|
||||
.then(stake => setStake(new BzzToken(stake)))
|
||||
.then(stake => setStake(stake))
|
||||
.catch(() => setStake(null)),
|
||||
|
||||
// Peer balances
|
||||
peerBalanceWrapper()
|
||||
.then(setPeerBalances)
|
||||
beeApi
|
||||
.getAllBalances({ timeout: TIMEOUT })
|
||||
.then(x => setPeerBalances(x.balances))
|
||||
.catch(() => setPeerBalances(null)),
|
||||
|
||||
// Settlements
|
||||
settlementsWrapper()
|
||||
beeApi
|
||||
.getAllSettlements()
|
||||
.then(setSettlements)
|
||||
.catch(() => setSettlements(null)),
|
||||
]
|
||||
@@ -398,7 +371,7 @@ export function Provider({ children }: Props): ReactElement {
|
||||
peerCheques,
|
||||
settlements,
|
||||
chainState,
|
||||
chainId,
|
||||
walletBalance,
|
||||
latestBeeRelease,
|
||||
isLoading,
|
||||
lastUpdate,
|
||||
|
||||
@@ -68,7 +68,7 @@ export function Provider({ children }: Props): ReactElement {
|
||||
setIsLoading(true)
|
||||
const stamps = await beeApi.getAllPostageBatch()
|
||||
|
||||
setStamps(stamps.filter(x => x.exists).map(enrichStamp))
|
||||
setStamps(stamps.map(enrichStamp))
|
||||
setLastUpdate(Date.now())
|
||||
setError(null)
|
||||
} 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)
|
||||
// learn more: https://github.com/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 { Token } from './models/Token'
|
||||
import { CheckState } from './providers/Bee'
|
||||
|
||||
export interface StatusHookCommon {
|
||||
@@ -12,31 +11,11 @@ export interface StatusNodeVersionHook extends StatusHookCommon {
|
||||
latestUrl: string
|
||||
isLatestBeeVersion: boolean
|
||||
}
|
||||
|
||||
export interface StatusEthereumConnectionHook extends StatusHookCommon {
|
||||
nodeAddresses: NodeAddresses | null
|
||||
}
|
||||
|
||||
export interface StatusTopologyHook extends StatusHookCommon {
|
||||
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 { BEE_DESKTOP_LATEST_RELEASE_PAGE_API } from '../constants'
|
||||
import { DaiToken } from '../models/DaiToken'
|
||||
import { Token } from '../models/Token'
|
||||
import { getJson, postJson } from './net'
|
||||
|
||||
export interface BeeConfig {
|
||||
@@ -18,10 +17,10 @@ export interface BeeConfig {
|
||||
'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`)
|
||||
|
||||
return DaiToken.fromDecimal(response.data)
|
||||
return DAI.fromDecimalString(response.data)
|
||||
}
|
||||
|
||||
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}`)
|
||||
}
|
||||
|
||||
export async function performSwap(desktopUrl: string, daiAmount: string): Promise<void> {
|
||||
await postJson(`${desktopUrl}/swap`, { dai: daiAmount })
|
||||
export async function performSwap(desktopUrl: string, daiAmount: DAI): Promise<void> {
|
||||
await postJson(`${desktopUrl}/swap`, { dai: daiAmount.toWeiString() })
|
||||
}
|
||||
|
||||
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 { uuidV4, waitUntilStampUsable } from '.'
|
||||
import { Identity, IdentityType } from '../providers/Feeds'
|
||||
@@ -80,18 +80,18 @@ async function getWallet(type: IdentityType, data: string, password?: string): P
|
||||
export async function updateFeed(
|
||||
beeApi: Bee,
|
||||
identity: Identity,
|
||||
hash: string,
|
||||
stamp: string,
|
||||
hash: Reference | string,
|
||||
stamp: BatchId | string,
|
||||
password?: string,
|
||||
): Promise<void> {
|
||||
const wallet = await getWalletFromIdentity(identity, password)
|
||||
|
||||
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 writer.upload(stamp, hash as Reference)
|
||||
await waitUntilStampUsable(stamp, beeApi)
|
||||
await writer.upload(stamp, hash)
|
||||
}
|
||||
|
||||
+20
-47
@@ -1,8 +1,6 @@
|
||||
import { BatchId, Bee, PostageBatch } from '@ethersphere/bee-js'
|
||||
import { decodeCid } from '@ethersphere/swarm-cid'
|
||||
import { BatchId, Bee, PostageBatch, Reference } from '@ethersphere/bee-js'
|
||||
import { BigNumber } from 'bignumber.js'
|
||||
import { BZZ_LINK_DOMAIN } from '../constants'
|
||||
import { Token } from '../models/Token'
|
||||
|
||||
/**
|
||||
* Test if value is an integer
|
||||
@@ -131,13 +129,7 @@ export function extractSwarmCid(s: string): string | undefined {
|
||||
|
||||
const cid = matches[1]
|
||||
try {
|
||||
const decodeResult = decodeCid(cid)
|
||||
|
||||
if (!decodeResult.type) {
|
||||
return
|
||||
}
|
||||
|
||||
return decodeResult.reference
|
||||
return new Reference(cid).toHex()
|
||||
} catch (e) {
|
||||
return
|
||||
}
|
||||
@@ -171,48 +163,36 @@ export function formatEnum(string: string): string {
|
||||
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
|
||||
|
||||
if (unit < 120) {
|
||||
return `${seconds} seconds`
|
||||
}
|
||||
unit /= 60
|
||||
unit /= BigInt(60)
|
||||
|
||||
if (unit < 120) {
|
||||
return `${Math.round(unit)} minutes`
|
||||
return `${unit} minutes`
|
||||
}
|
||||
unit /= 60
|
||||
unit /= BigInt(60)
|
||||
|
||||
if (unit < 48) {
|
||||
return `${Math.round(unit)} hours`
|
||||
return `${unit} hours`
|
||||
}
|
||||
unit /= 24
|
||||
unit /= BigInt(24)
|
||||
|
||||
if (unit < 14) {
|
||||
return `${Math.round(unit)} days`
|
||||
return `${unit} days`
|
||||
}
|
||||
unit /= 7
|
||||
unit /= BigInt(7)
|
||||
|
||||
if (unit < 52) {
|
||||
return `${Math.round(unit)} weeks`
|
||||
return `${unit} weeks`
|
||||
}
|
||||
unit /= 52
|
||||
unit /= BigInt(52)
|
||||
|
||||
return `${unit.toFixed(1)} 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
|
||||
return `${unit} years`
|
||||
}
|
||||
|
||||
export function shortenText(text: string, length = 20, separator = '[…]'): string {
|
||||
@@ -231,20 +211,11 @@ interface Options {
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
export function waitUntilStampUsable(batchId: BatchId, bee: Bee, options?: Options): Promise<PostageBatch> {
|
||||
return waitForStamp(batchId, bee, 'usable', options)
|
||||
export function waitUntilStampUsable(batchId: BatchId | string, bee: Bee, options?: Options): Promise<PostageBatch> {
|
||||
return waitForStamp(batchId, bee, options)
|
||||
}
|
||||
|
||||
export function waitUntilStampExists(batchId: BatchId, 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> {
|
||||
async function waitForStamp(batchId: BatchId | string, bee: Bee, options?: Options): Promise<PostageBatch> {
|
||||
const timeout = options?.timeout || DEFAULT_STAMP_USABLE_TIMEOUT
|
||||
const pollingFrequency = options?.pollingFrequency || DEFAULT_POLLING_FREQUENCY
|
||||
|
||||
@@ -252,7 +223,9 @@ async function waitForStamp(
|
||||
try {
|
||||
const stamp = await bee.getPostageBatch(batchId)
|
||||
|
||||
if (stamp[field]) return stamp
|
||||
if (stamp.usable) {
|
||||
return stamp
|
||||
}
|
||||
} catch {
|
||||
// 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 { Contract, providers, Wallet, BigNumber as BN } from 'ethers'
|
||||
import { bzzABI, BZZ_TOKEN_ADDRESS } from './bzz-abi'
|
||||
import { BZZ, DAI, EthAddress, PrivateKey } from '@ethersphere/bee-js'
|
||||
import { BigNumber as BN, Contract, providers, Wallet } from 'ethers'
|
||||
import { BZZ_TOKEN_ADDRESS, bzzABI } from './bzz-abi'
|
||||
|
||||
const NETWORK_ID = 100
|
||||
|
||||
@@ -12,13 +13,12 @@ async function getNetworkChainId(url: string): Promise<number> {
|
||||
return network.chainId
|
||||
}
|
||||
|
||||
async function eth_getBalance(address: string, provider: providers.JsonRpcProvider): Promise<string> {
|
||||
if (!address.startsWith('0x')) {
|
||||
address = `0x${address}`
|
||||
}
|
||||
const balance = await provider.getBalance(address)
|
||||
async function eth_getBalance(address: EthAddress | string, provider: providers.JsonRpcProvider): Promise<DAI> {
|
||||
address = new EthAddress(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> {
|
||||
@@ -28,17 +28,16 @@ async function eth_getBlockByNumber(provider: providers.JsonRpcProvider): Promis
|
||||
}
|
||||
|
||||
async function eth_getBalanceERC20(
|
||||
address: string,
|
||||
address: EthAddress | string,
|
||||
provider: providers.JsonRpcProvider,
|
||||
tokenAddress = BZZ_TOKEN_ADDRESS,
|
||||
): Promise<string> {
|
||||
if (!address.startsWith('0x')) {
|
||||
address = `0x${address}`
|
||||
}
|
||||
const contract = new Contract(tokenAddress, bzzABI, provider)
|
||||
const balance = await contract.balanceOf(address)
|
||||
): Promise<BZZ> {
|
||||
address = new EthAddress(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 {
|
||||
@@ -47,29 +46,34 @@ interface TransferResponse {
|
||||
}
|
||||
|
||||
export async function estimateNativeTransferTransactionCost(
|
||||
privateKey: string,
|
||||
privateKey: PrivateKey | string,
|
||||
jsonRpcProvider: string,
|
||||
): Promise<{ gasPrice: BN; totalCost: BN }> {
|
||||
): Promise<{ gasPrice: DAI; totalCost: DAI }> {
|
||||
privateKey = new PrivateKey(privateKey)
|
||||
|
||||
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
|
||||
const gasLimit = '21000'
|
||||
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(
|
||||
privateKey: string,
|
||||
to: string,
|
||||
value: string,
|
||||
privateKey: PrivateKey | string,
|
||||
to: EthAddress | string,
|
||||
value: DAI,
|
||||
jsonRpcProvider: string,
|
||||
externalGasPrice?: BN,
|
||||
externalGasPrice?: DAI,
|
||||
): Promise<TransferResponse> {
|
||||
privateKey = new PrivateKey(privateKey)
|
||||
to = new EthAddress(to)
|
||||
|
||||
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({
|
||||
to,
|
||||
value: BN.from(value),
|
||||
gasPrice,
|
||||
to: to.toHex(),
|
||||
value: BN.from(value.toWeiString()),
|
||||
gasPrice: BN.from(gasPrice.toWeiString()),
|
||||
gasLimit: BN.from(21000),
|
||||
type: 0,
|
||||
})
|
||||
@@ -79,11 +83,14 @@ export async function sendNativeTransaction(
|
||||
}
|
||||
|
||||
export async function sendBzzTransaction(
|
||||
privateKey: string,
|
||||
to: string,
|
||||
value: string,
|
||||
privateKey: PrivateKey | string,
|
||||
to: EthAddress | string,
|
||||
value: BZZ,
|
||||
jsonRpcProvider: string,
|
||||
): Promise<TransferResponse> {
|
||||
privateKey = new PrivateKey(privateKey)
|
||||
to = new EthAddress(to)
|
||||
|
||||
const signer = await makeReadySigner(privateKey, jsonRpcProvider)
|
||||
const gasPrice = await signer.getGasPrice()
|
||||
const bzz = new Contract(BZZ_TOKEN_ADDRESS, bzzABI, signer)
|
||||
@@ -93,10 +100,10 @@ export async function sendBzzTransaction(
|
||||
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)
|
||||
await provider.ready
|
||||
const signer = new Wallet(privateKey, provider)
|
||||
const signer = new Wallet(privateKey.toUint8Array(), provider)
|
||||
|
||||
return signer
|
||||
}
|
||||
|
||||
+12
-36
@@ -1,39 +1,15 @@
|
||||
import { BZZ, DAI, EthAddress } from '@ethersphere/bee-js'
|
||||
import { providers, Wallet } from 'ethers'
|
||||
import { BzzToken } from '../models/BzzToken'
|
||||
import { DaiToken } from '../models/DaiToken'
|
||||
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 {
|
||||
public address: string
|
||||
public privateKey: string
|
||||
|
||||
private constructor(
|
||||
public wallet: Wallet,
|
||||
public bzz: BzzToken,
|
||||
public dai: DaiToken,
|
||||
public bzz: BZZ,
|
||||
public dai: DAI,
|
||||
public provider: providers.JsonRpcProvider,
|
||||
) {
|
||||
this.address = wallet.address
|
||||
@@ -44,32 +20,32 @@ export class ResolvedWallet {
|
||||
const wallet =
|
||||
typeof privateKeyOrWallet === 'string' ? new Wallet(privateKeyOrWallet, provider) : privateKeyOrWallet
|
||||
const address = wallet.address
|
||||
const bzz = new BzzToken(await Rpc._eth_getBalanceERC20(address, provider))
|
||||
const dai = new DaiToken(await Rpc._eth_getBalance(address, provider))
|
||||
const bzz = await Rpc._eth_getBalanceERC20(address, provider)
|
||||
const dai = await Rpc._eth_getBalance(address, provider)
|
||||
|
||||
return new ResolvedWallet(wallet, bzz, dai, provider)
|
||||
}
|
||||
|
||||
public async refresh(): Promise<ResolvedWallet> {
|
||||
this.bzz = new BzzToken(await Rpc._eth_getBalanceERC20(this.address, this.provider))
|
||||
this.dai = new DaiToken(await Rpc._eth_getBalance(this.address, this.provider))
|
||||
this.bzz = await Rpc._eth_getBalanceERC20(this.address, this.provider)
|
||||
this.dai = await Rpc._eth_getBalance(this.address, this.provider)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async transfer(destination: string, jsonRpcProvider: string): Promise<void> {
|
||||
if (this.bzz.toDecimal.gt(0.05)) {
|
||||
await Rpc.sendBzzTransaction(this.privateKey, destination, this.bzz.toString, jsonRpcProvider)
|
||||
public async transfer(destination: EthAddress | string, jsonRpcProvider: string): Promise<void> {
|
||||
if (this.bzz.gt(BZZ.fromDecimalString('0.05'))) {
|
||||
await Rpc.sendBzzTransaction(this.privateKey, destination, this.bzz, jsonRpcProvider)
|
||||
await this.refresh()
|
||||
}
|
||||
|
||||
const { gasPrice, totalCost } = await estimateNativeTransferTransactionCost(this.privateKey, jsonRpcProvider)
|
||||
|
||||
if (this.dai.toBigNumber.gt(totalCost.toString())) {
|
||||
if (this.dai.gt(totalCost)) {
|
||||
await Rpc.sendNativeTransaction(
|
||||
this.privateKey,
|
||||
destination,
|
||||
this.dai.toBigNumber.minus(totalCost.toString()).toString(),
|
||||
this.dai.minus(totalCost),
|
||||
jsonRpcProvider,
|
||||
gasPrice,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user