diff --git a/package-lock.json b/package-lock.json
index 0c0e53c..e3c43ff 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"@types/react-router": "^5.1.13",
"@types/react-router-dom": "^5.1.7",
"axios": "^0.21.1",
+ "bignumber.js": "^9.0.1",
"feather-icons": "^4.28.0",
"material-ui-dropzone": "^3.5.0",
"qrcode.react": "^1.0.1",
@@ -4252,6 +4253,14 @@
"node": "*"
}
},
+ "node_modules/bignumber.js": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+ "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -25127,6 +25136,11 @@
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true
},
+ "bignumber.js": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
+ "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
+ },
"binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
diff --git a/package.json b/package.json
index 8c2c70c..191659f 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
"@types/react-router": "^5.1.13",
"@types/react-router-dom": "^5.1.7",
"axios": "^0.21.1",
+ "bignumber.js": "^9.0.1",
"feather-icons": "^4.28.0",
"material-ui-dropzone": "^3.5.0",
"qrcode.react": "^1.0.1",
diff --git a/src/components/DepositModal.tsx b/src/components/DepositModal.tsx
deleted file mode 100644
index ed12007..0000000
--- a/src/components/DepositModal.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React, { ReactElement } 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 { Snackbar } from '@material-ui/core'
-
-import { beeDebugApi } from '../services/bee'
-
-export default function DepositModal(): ReactElement {
- const [open, setOpen] = React.useState(false)
- const [amount, setAmount] = React.useState(BigInt(0))
- const [showToast, setToastVisibility] = React.useState(false)
- const [toastContent, setToastContent] = React.useState('')
-
- const handleClickOpen = () => {
- setOpen(true)
- }
-
- const handleClose = () => {
- setOpen(false)
- }
-
- const handleWithdraw = () => {
- if (amount > 0) {
- beeDebugApi.chequebook
- .deposit(amount)
- .then(res => {
- setOpen(false)
- handleToast(`Successful Deposit. Transaction ${res.transactionHash}`)
- })
- .catch(() => {
- handleToast('Error with Deposit')
- })
- } else {
- handleToast('Must be amount of greater than 0')
- }
- }
-
- const handleToast = (text: string) => {
- setToastContent(text)
- setToastVisibility(true)
- setTimeout(() => setToastVisibility(false), 7000)
- }
-
- return (
-
-
-
-
-
- )
-}
diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
deleted file mode 100644
index 5851bd3..0000000
--- a/src/components/SearchBar.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import React from 'react'
-
-import { makeStyles, Theme, createStyles } from '@material-ui/core/styles'
-import { Paper, InputBase, IconButton } from '@material-ui/core'
-import { Search } from '@material-ui/icons'
-
-const useStyles = makeStyles((theme: Theme) =>
- createStyles({
- root: {
- padding: '2px 4px',
- display: 'flex',
- alignItems: 'center',
- width: 400,
- },
- input: {
- marginLeft: theme.spacing(1),
- flex: 1,
- },
- iconButton: {
- padding: 10,
- },
- divider: {
- height: 28,
- margin: 4,
- },
- }),
-)
-
-export default function SearchBar() {
- const classes = useStyles()
-
- return (
-
- )
-}
diff --git a/src/components/WDModal.tsx b/src/components/WDModal.tsx
new file mode 100644
index 0000000..fda8948
--- /dev/null
+++ b/src/components/WDModal.tsx
@@ -0,0 +1,118 @@
+import { ReactElement, 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, Snackbar } from '@material-ui/core'
+import { Token } from '../models/Token'
+import type { BigNumber } from 'bignumber.js'
+
+interface Props {
+ successMessage: string
+ errorMessage: string
+ dialogMessage: string
+ label: string
+ max?: BigNumber
+ min?: BigNumber
+ action: (amount: bigint) => Promise<{ transactionHash: string }>
+}
+
+export default function WithdrawModal({
+ successMessage,
+ errorMessage,
+ dialogMessage,
+ min,
+ max,
+ label,
+ action,
+}: Props): ReactElement {
+ const [open, setOpen] = useState(false)
+ const [amount, setAmount] = useState('')
+ const [amountToken, setAmountToken] = useState(null)
+ const [amountError, setAmountError] = useState(null)
+ const [showToast, setToastVisibility] = useState(false)
+ const [toastContent, setToastContent] = useState('')
+
+ const handleClickOpen = () => {
+ setOpen(true)
+ }
+
+ const handleClose = () => {
+ setOpen(false)
+ }
+
+ const handleAction = async () => {
+ if (amountToken === null) return
+
+ try {
+ const { transactionHash } = await action(amountToken.toBigInt)
+ setOpen(false)
+ handleToast(`${successMessage} Transaction ${transactionHash}`)
+ } catch (e) {
+ handleToast(`${errorMessage} Error: ${e.message}`)
+ }
+ }
+
+ const handleToast = (text: string) => {
+ setToastContent(text)
+ setToastVisibility(true)
+ setTimeout(() => setToastVisibility(false), 7000)
+ }
+
+ const handleChange = (e: React.ChangeEvent) => {
+ const value = e.target.value
+ setAmount(value)
+ setAmountError(null)
+ try {
+ const t = Token.fromDecimal(value)
+ setAmountToken(t)
+
+ if (min && t.toDecimal.isLessThan(min)) setAmountError(new Error(`Needs to be more than ${min}`))
+
+ if (max && t.toDecimal.isGreaterThan(max)) setAmountError(new Error(`Needs to be less than ${max}`))
+ } catch (e) {
+ setAmountError(e)
+ }
+ }
+
+ return (
+
+
+
+
+
+ )
+}
diff --git a/src/components/WithdrawlModal.tsx b/src/components/WithdrawlModal.tsx
deleted file mode 100644
index c65a97d..0000000
--- a/src/components/WithdrawlModal.tsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import { ReactElement, 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 { Snackbar } from '@material-ui/core'
-
-import { beeDebugApi } from '../services/bee'
-
-export default function WithdrawlModal(): ReactElement {
- const [open, setOpen] = useState(false)
- const [amount, setAmount] = useState(BigInt(0))
- const [showToast, setToastVisibility] = useState(false)
- const [toastContent, setToastContent] = useState('')
-
- const handleClickOpen = () => {
- setOpen(true)
- }
-
- const handleClose = () => {
- setOpen(false)
- }
-
- const handleWithdraw = () => {
- if (amount > 0) {
- beeDebugApi.chequebook
- .withdraw(amount)
- .then(res => {
- setOpen(false)
- handleToast(`Successful withdrawl. Transaction ${res.transactionHash}`)
- })
- .catch(() => {
- // FIXME: should probably detail the error
- handleToast('Error with withdrawing')
- })
- } else {
- handleToast('Must be amount of greater than 0')
- }
- }
-
- const handleToast = (text: string) => {
- setToastContent(text)
- setToastVisibility(true)
- setTimeout(() => setToastVisibility(false), 7000)
- }
-
- return (
-
-
-
-
-
- )
-}
diff --git a/src/containers/DepositModal.tsx b/src/containers/DepositModal.tsx
new file mode 100644
index 0000000..5d73b93
--- /dev/null
+++ b/src/containers/DepositModal.tsx
@@ -0,0 +1,18 @@
+import type { ReactElement } from 'react'
+import { beeDebugApi } from '../services/bee'
+
+import WDModal from '../components/WDModal'
+import { BigNumber } from 'bignumber.js'
+
+export default function DepositModal(): ReactElement {
+ return (
+
+ )
+}
diff --git a/src/containers/WithdrawModal.tsx b/src/containers/WithdrawModal.tsx
new file mode 100644
index 0000000..b606b86
--- /dev/null
+++ b/src/containers/WithdrawModal.tsx
@@ -0,0 +1,18 @@
+import type { ReactElement } from 'react'
+import { beeDebugApi } from '../services/bee'
+
+import WDModal from '../components/WDModal'
+import { BigNumber } from 'bignumber.js'
+
+export default function WithdrawModal(): ReactElement {
+ return (
+
+ )
+}
diff --git a/src/models/Token.test.ts b/src/models/Token.test.ts
new file mode 100644
index 0000000..55abe41
--- /dev/null
+++ b/src/models/Token.test.ts
@@ -0,0 +1,39 @@
+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)
+ })
+ })
+ })
+})
diff --git a/src/models/Token.ts b/src/models/Token.ts
new file mode 100644
index 0000000..3afbdd1
--- /dev/null
+++ b/src/models/Token.ts
@@ -0,0 +1,56 @@
+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')
+
+ 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))
+ }
+}
diff --git a/src/pages/accounting/AccountCard.tsx b/src/pages/accounting/AccountCard.tsx
index 3a92811..99f56bb 100644
--- a/src/pages/accounting/AccountCard.tsx
+++ b/src/pages/accounting/AccountCard.tsx
@@ -3,11 +3,11 @@ import { ReactElement } from 'react'
import { createStyles, makeStyles } from '@material-ui/core/styles'
import { Card, CardContent, Typography, Grid } from '@material-ui/core/'
import { Skeleton } from '@material-ui/lab'
-import WithdrawlModal from '../../components/WithdrawlModal'
-import DepositModal from '../../components/DepositModal'
+import WithdrawModal from '../../containers/WithdrawModal'
+import DepositModal from '../../containers/DepositModal'
import CashoutModal from '../../components/CashoutModal'
-import { ConvertBalanceToBZZ } from '../../utils/common'
+import { fromBZZbaseUnit } from '../../utils'
import type { AllSettlements, ChequebookAddressResponse } from '@ethersphere/bee-js'
@@ -55,7 +55,7 @@ function AccountCard(props: Props): ReactElement {
Accounting
-
+
@@ -68,28 +68,28 @@ function AccountCard(props: Props): ReactElement {
- Total Balance
+ Total Balance (BZZ)
- {ConvertBalanceToBZZ(props.chequebookBalance.totalBalance)}
+ {fromBZZbaseUnit(props.chequebookBalance.totalBalance)}
- Available Balance
+ Available Balance (BZZ)
- {ConvertBalanceToBZZ(props.chequebookBalance.availableBalance)}
+ {fromBZZbaseUnit(props.chequebookBalance.availableBalance)}
- Total Sent / Received
+ Total Sent / Received (BZZ)
- {ConvertBalanceToBZZ(props.settlements?.totalsent || 0)} /{' '}
- {ConvertBalanceToBZZ(props.settlements?.totalreceived || 0)}
+ {fromBZZbaseUnit(props.settlements?.totalsent || 0)} /{' '}
+ {fromBZZbaseUnit(props.settlements?.totalreceived || 0)}
(
- {ConvertBalanceToBZZ(
+ {fromBZZbaseUnit(
(props.settlements && props.settlements?.totalsent - props.settlements?.totalreceived) || 0,
)}
)
diff --git a/src/pages/accounting/BalancesTable.tsx b/src/pages/accounting/BalancesTable.tsx
index ac4b0c5..c1ebbcd 100644
--- a/src/pages/accounting/BalancesTable.tsx
+++ b/src/pages/accounting/BalancesTable.tsx
@@ -12,7 +12,7 @@ import {
CircularProgress,
} from '@material-ui/core'
-import { ConvertBalanceToBZZ } from '../../utils/common'
+import { fromBZZbaseUnit } from '../../utils'
const useStyles = makeStyles({
table: {
@@ -59,12 +59,12 @@ function BalancesTable(props: Props): ReactElement {
{peerBalance.peer}
0 ? '#32c48d' : '#c9201f',
+ color: fromBZZbaseUnit(peerBalance.balance) > 0 ? '#32c48d' : '#c9201f',
textAlign: 'right',
fontFamily: 'monospace, monospace',
}}
>
- {ConvertBalanceToBZZ(peerBalance.balance).toFixed(7).toLocaleString()}
+ {fromBZZbaseUnit(peerBalance.balance).toFixed(7).toLocaleString()}
diff --git a/src/pages/accounting/ChequebookTable.tsx b/src/pages/accounting/ChequebookTable.tsx
index 3e35926..4c81982 100644
--- a/src/pages/accounting/ChequebookTable.tsx
+++ b/src/pages/accounting/ChequebookTable.tsx
@@ -1,4 +1,4 @@
-import React, { ReactElement } from 'react'
+import type { ReactElement } from 'react'
import { makeStyles } from '@material-ui/core/styles'
import {
Table,
@@ -12,7 +12,7 @@ import {
CircularProgress,
} from '@material-ui/core'
-import { ConvertBalanceToBZZ } from '../../utils/common'
+import { fromBZZbaseUnit } from '../../utils'
import EthereumAddress from '../../components/EthereumAddress'
import ClipboardCopy from '../../components/ClipboardCopy'
import PeerDetailDrawer from './PeerDetailDrawer'
@@ -80,7 +80,7 @@ function ChequebookTable(props: Props): ReactElement {
{peerCheque.lastreceived?.payout
- ? `${ConvertBalanceToBZZ(peerCheque.lastreceived?.payout).toFixed(7).toLocaleString()} from`
+ ? `${fromBZZbaseUnit(peerCheque.lastreceived?.payout).toFixed(7).toLocaleString()} from`
: '-'}
{peerCheque.lastreceived ? (
@@ -97,7 +97,7 @@ function ChequebookTable(props: Props): ReactElement {
{peerCheque.lastsent?.payout
- ? `${ConvertBalanceToBZZ(peerCheque.lastsent?.payout).toFixed(7).toLocaleString()} to`
+ ? `${fromBZZbaseUnit(peerCheque.lastsent?.payout).toFixed(7).toLocaleString()} to`
: '-'}
{peerCheque.lastsent ? (
diff --git a/src/pages/accounting/PeerDetailDrawer.tsx b/src/pages/accounting/PeerDetailDrawer.tsx
index c2c405d..babcefd 100644
--- a/src/pages/accounting/PeerDetailDrawer.tsx
+++ b/src/pages/accounting/PeerDetailDrawer.tsx
@@ -3,7 +3,7 @@ import { Paper, Container, Drawer, Button, Typography, CircularProgress, Grid }
import ClipboardCopy from '../../components/ClipboardCopy'
import { beeDebugApi } from '../../services/bee'
import EthereumAddress from '../../components/EthereumAddress'
-import { ConvertBalanceToBZZ } from '../../utils/common'
+import { fromBZZbaseUnit } from '../../utils'
import { LastCashoutActionResponse, LastChequesForPeerResponse } from '@ethersphere/bee-js'
function truncStringPortion(str: string, firstCharCount = 10, endCharCount = 10) {
@@ -87,7 +87,7 @@ export default function Index(props: Props): ReactElement {
Payout:
{' '}
- {peerCheque?.lastsent?.payout ? ConvertBalanceToBZZ(peerCheque?.lastsent?.payout) : '-'}
+ {peerCheque?.lastsent?.payout ? fromBZZbaseUnit(peerCheque?.lastsent?.payout) : '-'}
@@ -105,7 +105,7 @@ export default function Index(props: Props): ReactElement {
Payout:
{' '}
- {peerCheque?.lastreceived?.payout ? ConvertBalanceToBZZ(peerCheque?.lastreceived?.payout) : '-'}
+ {peerCheque?.lastreceived?.payout ? fromBZZbaseUnit(peerCheque?.lastreceived?.payout) : '-'}
@@ -124,14 +124,14 @@ export default function Index(props: Props): ReactElement {
Cumulative Payout:
- {peerCashout?.cumulativePayout ? ConvertBalanceToBZZ(peerCashout?.cumulativePayout) : '-'}
+ {peerCashout?.cumulativePayout ? fromBZZbaseUnit(peerCashout?.cumulativePayout) : '-'}
Last Payout:
{' '}
- {peerCashout?.result.lastPayout ? ConvertBalanceToBZZ(peerCashout?.result.lastPayout) : '-'}
+ {peerCashout?.result.lastPayout ? fromBZZbaseUnit(peerCashout?.result.lastPayout) : '-'}
{peerCashout?.result.bounced ? 'Bounced' : ''}
diff --git a/src/pages/accounting/SettlementsTable.tsx b/src/pages/accounting/SettlementsTable.tsx
index 1718334..b12f479 100644
--- a/src/pages/accounting/SettlementsTable.tsx
+++ b/src/pages/accounting/SettlementsTable.tsx
@@ -12,7 +12,7 @@ import {
CircularProgress,
} from '@material-ui/core'
-import { ConvertBalanceToBZZ } from '../../utils/common'
+import { fromBZZbaseUnit } from '../../utils'
import type { AllSettlements, Settlements } from '@ethersphere/bee-js'
@@ -51,10 +51,10 @@ function SettlementsTable(props: Props): ReactElement {
{item.peer}
- {item.received > 0 ? ConvertBalanceToBZZ(item.received).toFixed(7).toLocaleString() : item.received}
+ {item.received > 0 ? fromBZZbaseUnit(item.received).toFixed(7).toLocaleString() : item.received}
- {item.sent > 0 ? ConvertBalanceToBZZ(item.sent).toFixed(7).toLocaleString() : item.sent}
+ {item.sent > 0 ? fromBZZbaseUnit(item.sent).toFixed(7).toLocaleString() : item.sent}
))}
diff --git a/src/pages/status/SetupSteps/ChequebookDeployFund.tsx b/src/pages/status/SetupSteps/ChequebookDeployFund.tsx
index 4b0455c..ef28b58 100644
--- a/src/pages/status/SetupSteps/ChequebookDeployFund.tsx
+++ b/src/pages/status/SetupSteps/ChequebookDeployFund.tsx
@@ -1,6 +1,6 @@
import { Typography } from '@material-ui/core/'
import EthereumAddress from '../../../components/EthereumAddress'
-import DepositModal from '../../../components/DepositModal'
+import DepositModal from '../../../containers/DepositModal'
import CodeBlockTabs from '../../../components/CodeBlockTabs'
import type { ReactElement } from 'react'
diff --git a/src/utils/common.tsx b/src/utils/common.tsx
deleted file mode 100644
index 0bf8696..0000000
--- a/src/utils/common.tsx
+++ /dev/null
@@ -1,3 +0,0 @@
-export const ConvertBalanceToBZZ = (amount: number): number => {
- return amount / 10 ** 16
-}
diff --git a/src/utils/index.test.ts b/src/utils/index.test.ts
new file mode 100644
index 0000000..bf9dc45
--- /dev/null
+++ b/src/utils/index.test.ts
@@ -0,0 +1,71 @@
+import BigNumber from 'bignumber.js'
+import { fromBZZbaseUnit, isInteger, makeBigNumber } from './index'
+
+describe('utils', () => {
+ describe('fromBZZbaseUnit', () => {
+ const values = [
+ { bzz: 0, baseUnits: 0 },
+ { bzz: 0.1, baseUnits: 1e15 },
+ { bzz: 0.9, baseUnits: 9e15 },
+ ]
+
+ values.forEach(({ bzz, baseUnits }) => {
+ test(`converting ${bzz} => ${baseUnits}`, () => {
+ expect(fromBZZbaseUnit(baseUnits)).toBe(bzz)
+ })
+ })
+ })
+
+ describe('isInteger', () => {
+ const correctValues = [
+ BigInt(0),
+ BigInt(1),
+ BigInt(-1),
+ new BigNumber('1'),
+ new BigNumber('0'),
+ new BigNumber('-1'),
+ ]
+ const wrongValues = ['1', new BigNumber('-0.1'), new BigNumber(NaN), new BigNumber(Infinity)]
+
+ correctValues.forEach(v => {
+ test(`testing ${v}`, () => {
+ expect(isInteger(v)).toBe(true)
+ })
+ })
+
+ wrongValues.forEach(v => {
+ test(`testing ${v}`, () => {
+ expect(isInteger(v)).toBe(false)
+ })
+ })
+ })
+
+ describe('makeBigNumber', () => {
+ const correctValues = [
+ BigInt(0),
+ BigInt(1),
+ BigInt(-1),
+ '1',
+ '0',
+ '-1',
+ '0.1',
+ new BigNumber('1'),
+ new BigNumber('0'),
+ new BigNumber('-1'),
+ ]
+ const wrongValues = [new Function(), 0, 1]
+
+ correctValues.forEach(v => {
+ test(`testing ${v}`, () => {
+ expect(BigNumber.isBigNumber(makeBigNumber(v))).toBe(true)
+ })
+ })
+
+ wrongValues.forEach(v => {
+ test(`testing ${v}`, () => {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ expect(() => makeBigNumber((v as unknown) as any)).toThrow()
+ })
+ })
+ })
+})
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 0000000..5ea2dca
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1,48 @@
+import { BigNumber } from 'bignumber.js'
+
+/**
+ * @deprecated Should be removed in favour of Token class
+ */
+const BZZ_BASE_UNIT = 1e16
+
+/**
+ * Convert from base units of BZZ token to BZZ
+ * @deprecated This should only be used for displaying values, it's unsafe and should be replaced with Token class
+ *
+ * @param amount Amount in base units of BZZ token
+ *
+ * @returns amount in BZZ
+ */
+export const fromBZZbaseUnit = (amount: number): number => {
+ return amount / BZZ_BASE_UNIT
+}
+
+/**
+ * Test if value is an integer
+ *
+ * @param value Value to be tested if it is an integer
+ *
+ * @returns True if the passed in value is integer
+ */
+export function isInteger(value: unknown): value is BigNumber | bigint {
+ return (BigNumber.isBigNumber(value) && value.isInteger()) || typeof value === 'bigint'
+}
+
+/**
+ *Convert value into a BigNumber if not already
+ *
+ * @param value Value to be converted
+ *
+ * @throws {TypeError} if the value is not convertible to a BigNumber
+ *
+ * @returns BigNumber - but it may still be NaN or Infinite
+ */
+export function makeBigNumber(value: BigNumber | bigint | string): BigNumber | never {
+ if (BigNumber.isBigNumber(value)) return value
+
+ if (typeof value === 'string') return new BigNumber(value)
+
+ if (typeof value === 'bigint') return new BigNumber(value.toString())
+
+ throw new TypeError('Not a BigNumber or BigNumber convertible value')
+}