diff --git a/package-lock.json b/package-lock.json
index e87c86e..44f1a35 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "0.24.1",
"license": "BSD-3-Clause",
"dependencies": {
- "@ethersphere/bee-js": "^6.2.0",
+ "@ethersphere/bee-js": "^6.6.0",
"@ethersphere/swarm-cid": "^0.1.0",
"@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2",
@@ -2440,13 +2440,13 @@
}
},
"node_modules/@ethersphere/bee-js": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.2.0.tgz",
- "integrity": "sha512-QWuVvW+c9z+AFWuRL+YBZECDyV3G5qeD9L0J7Tx7HYMugoDjMAjeRyNHjkf0a5np57q3i9MAE2P678rjSJ1Yaw==",
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.6.0.tgz",
+ "integrity": "sha512-f39yEbkCX7mnKSn0x9cV2TAlnMiemiJCiTVLhS6+g7nMbud1r269gzEHopElDaP5VJIsJy1uwD0VN4+HxIp3bg==",
"dependencies": {
"@ethersphere/swarm-cid": "^0.1.0",
"@types/readable-stream": "^2.3.13",
- "axios": "^1.3.4",
+ "axios": "^0.27.2",
"cafe-utility": "^10.8.1",
"elliptic": "^6.5.4",
"fetch-blob": "2.1.2",
@@ -2466,13 +2466,12 @@
}
},
"node_modules/@ethersphere/bee-js/node_modules/axios": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
- "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dependencies": {
- "follow-redirects": "^1.15.0",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
}
},
"node_modules/@ethersphere/bee-js/node_modules/form-data": {
@@ -16491,7 +16490,8 @@
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "dev": true
},
"node_modules/psl": {
"version": "1.8.0",
@@ -22305,13 +22305,13 @@
}
},
"@ethersphere/bee-js": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.2.0.tgz",
- "integrity": "sha512-QWuVvW+c9z+AFWuRL+YBZECDyV3G5qeD9L0J7Tx7HYMugoDjMAjeRyNHjkf0a5np57q3i9MAE2P678rjSJ1Yaw==",
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/@ethersphere/bee-js/-/bee-js-6.6.0.tgz",
+ "integrity": "sha512-f39yEbkCX7mnKSn0x9cV2TAlnMiemiJCiTVLhS6+g7nMbud1r269gzEHopElDaP5VJIsJy1uwD0VN4+HxIp3bg==",
"requires": {
"@ethersphere/swarm-cid": "^0.1.0",
"@types/readable-stream": "^2.3.13",
- "axios": "^1.3.4",
+ "axios": "^0.27.2",
"cafe-utility": "^10.8.1",
"elliptic": "^6.5.4",
"fetch-blob": "2.1.2",
@@ -22324,13 +22324,12 @@
},
"dependencies": {
"axios": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
- "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
+ "version": "0.27.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+ "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"requires": {
- "follow-redirects": "^1.15.0",
- "form-data": "^4.0.0",
- "proxy-from-env": "^1.1.0"
+ "follow-redirects": "^1.14.9",
+ "form-data": "^4.0.0"
}
},
"form-data": {
@@ -32620,7 +32619,8 @@
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
- "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+ "dev": true
},
"psl": {
"version": "1.8.0",
diff --git a/package.json b/package.json
index 6b310a3..2e1cdd2 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
"url": "https://github.com/ethersphere/bee-dashboard.git"
},
"dependencies": {
- "@ethersphere/bee-js": "^6.2.0",
+ "@ethersphere/bee-js": "^6.6.0",
"@ethersphere/swarm-cid": "^0.1.0",
"@material-ui/core": "4.12.3",
"@material-ui/icons": "4.11.2",
diff --git a/src/pages/account/stamps/AccountStamps.tsx b/src/pages/account/stamps/AccountStamps.tsx
index 658d8fa..b29167f 100644
--- a/src/pages/account/stamps/AccountStamps.tsx
+++ b/src/pages/account/stamps/AccountStamps.tsx
@@ -46,7 +46,7 @@ export function AccountStamps(): ReactElement {
if (status.all === CheckState.ERROR) return
function navigateToNewStamp() {
- navigate(ROUTES.ACCOUNT_STAMPS_NEW)
+ navigate(ROUTES.ACCOUNT_STAMPS_NEW_STANDARD)
}
return (
diff --git a/src/pages/files/Upload.tsx b/src/pages/files/Upload.tsx
index 94a84a2..16a1594 100644
--- a/src/pages/files/Upload.tsx
+++ b/src/pages/files/Upload.tsx
@@ -18,7 +18,7 @@ import { detectIndexHtml, getAssetNameFromFiles, packageFile } from '../../utils
import { persistIdentity, updateFeed } from '../../utils/identity'
import { HISTORY_KEYS, putHistory } from '../../utils/local-storage'
import { FeedPasswordDialog } from '../feeds/FeedPasswordDialog'
-import { PostageStampCreation } from '../stamps/PostageStampCreation'
+import { PostageStampAdvancedCreation } from '../stamps/PostageStampAdvancedCreation'
import { PostageStampSelector } from '../stamps/PostageStampSelector'
import { AssetPreview } from './AssetPreview'
import { StampPreview } from './StampPreview'
@@ -186,7 +186,7 @@ export function Upload(): ReactElement {
{hasAnyStamps && stampMode === 'SELECT' ? (
setStamp(stamp)} defaultValue={stamp?.batchID} />
) : (
- setStampMode('SELECT')} />
+ setStampMode('SELECT')} />
)}
diff --git a/src/pages/stamps/CreatePostageStampPage.tsx b/src/pages/stamps/CreatePostageStampAdvancedPage.tsx
similarity index 64%
rename from src/pages/stamps/CreatePostageStampPage.tsx
rename to src/pages/stamps/CreatePostageStampAdvancedPage.tsx
index 2888824..31f860d 100644
--- a/src/pages/stamps/CreatePostageStampPage.tsx
+++ b/src/pages/stamps/CreatePostageStampAdvancedPage.tsx
@@ -2,7 +2,7 @@ import { ReactElement } from 'react'
import { useNavigate } from 'react-router'
import { HistoryHeader } from '../../components/HistoryHeader'
import { ROUTES } from '../../routes'
-import { PostageStampCreation } from './PostageStampCreation'
+import { PostageStampAdvancedCreation } from './PostageStampAdvancedCreation'
export function CreatePostageStampPage(): ReactElement {
const navigate = useNavigate()
@@ -13,8 +13,8 @@ export function CreatePostageStampPage(): ReactElement {
return (
-
Buy new postage stamp
-
+
Buy new postage stamp batch
+
)
}
diff --git a/src/pages/stamps/CreatePostageStampStandardPage.tsx b/src/pages/stamps/CreatePostageStampStandardPage.tsx
new file mode 100644
index 0000000..92a4083
--- /dev/null
+++ b/src/pages/stamps/CreatePostageStampStandardPage.tsx
@@ -0,0 +1,20 @@
+import { ReactElement } from 'react'
+import { useNavigate } from 'react-router'
+import { HistoryHeader } from '../../components/HistoryHeader'
+import { ROUTES } from '../../routes'
+import { PostageStampStandardCreation } from './PostageStampStandardCreation'
+
+export function CreatePostageStampBasicPage(): ReactElement {
+ const navigate = useNavigate()
+
+ function onFinished() {
+ navigate(ROUTES.ACCOUNT_STAMPS)
+ }
+
+ return (
+
+
Buy new postage stamp batch
+
+
+ )
+}
diff --git a/src/pages/stamps/PostageStampCreation.tsx b/src/pages/stamps/PostageStampAdvancedCreation.tsx
similarity index 79%
rename from src/pages/stamps/PostageStampCreation.tsx
rename to src/pages/stamps/PostageStampAdvancedCreation.tsx
index 0089f4b..971ed15 100644
--- a/src/pages/stamps/PostageStampCreation.tsx
+++ b/src/pages/stamps/PostageStampAdvancedCreation.tsx
@@ -1,5 +1,6 @@
import { PostageBatchOptions } from '@ethersphere/bee-js'
import { Box, Grid, Typography } from '@material-ui/core'
+import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
import BigNumber from 'bignumber.js'
import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
@@ -18,12 +19,32 @@ import {
waitUntilStampExists,
} from '../../utils'
import { getHumanReadableFileSize } from '../../utils/file'
+import { Link } from 'react-router-dom'
+import { ROUTES } from '../../routes'
interface Props {
onFinished: () => void
}
-export function PostageStampCreation({ onFinished }: Props): ReactElement {
+const useStyles = makeStyles((theme: Theme) =>
+ createStyles({
+ link: {
+ color: '#dd7700',
+ textDecoration: 'underline',
+ '&:hover': {
+ textDecoration: 'none',
+
+ // https://github.com/mui-org/material-ui/issues/22543
+ '@media (hover: none)': {
+ textDecoration: 'none',
+ },
+ },
+ },
+ }),
+)
+
+export function PostageStampAdvancedCreation({ onFinished }: Props): ReactElement {
+ const classes = useStyles()
const { chainState } = useContext(BeeContext)
const { refresh } = useContext(StampsContext)
const { beeDebugApi } = useContext(SettingsContext)
@@ -153,22 +174,25 @@ export function PostageStampCreation({ onFinished }: Props): ReactElement {
return (
<>
-
-
- To upload data to Swarm network, you will need to purchase a postage stamp. If you're not familiar with
- this, please read{' '}
-
- this guide
-
- .
-
+
+ Batch name
- validateDepthInput(event.target.value)} />
+ setLabelInput(event.target.value)} />
+
+
+ setImmutable(event.target.value === 'Yes')}
+ options={[
+ { value: 'Yes', label: 'Yes' },
+ { value: 'No', label: 'No' },
+ ]}
+ />
+
+
+ validateDepthInput(event.target.value)} />
Corresponding file size
@@ -185,38 +209,14 @@ export function PostageStampCreation({ onFinished }: Props): ReactElement {
{!amountError && amountInput ? getTtl(Number.parseInt(amountInput, 10)) : '-'}
+
+
+ Current price of 24000 per block
+
+
{amountError && {amountError}}
-
- setLabelInput(event.target.value)} />
-
-
- setImmutable(event.target.value === 'Yes')}
- options={[
- { value: 'Yes', label: 'Yes' },
- { value: 'No', label: 'No' },
- ]}
- />
-
-
- {immutable && (
-
- Once an immutable stamp is maxed out, it disallows further content uploads, thereby safeguarding your
- previously uploaded content from unintentional overwriting.
-
- )}
- {!immutable && (
-
- When a mutable stamp reaches full capacity, it still permits new content uploads. However, this comes
- with the caveat of overwriting previously uploaded content associated with the same stamp.
-
- )}
-
-
-
+
Indicative Price
@@ -227,14 +227,24 @@ export function PostageStampCreation({ onFinished }: Props): ReactElement {
-
- Buy New Stamp
-
+
+
+
+
+ Buy New Stamp
+
+
+
+
+ Standard mode
+
+
+
>
)
}
diff --git a/src/pages/stamps/PostageStampStandardCreation.tsx b/src/pages/stamps/PostageStampStandardCreation.tsx
new file mode 100644
index 0000000..ed68333
--- /dev/null
+++ b/src/pages/stamps/PostageStampStandardCreation.tsx
@@ -0,0 +1,230 @@
+import { PostageBatchOptions } from '@ethersphere/bee-js'
+import { Utils } from '@ethersphere/bee-js'
+import { Box, Button, Grid, Slider, Typography } from '@material-ui/core'
+import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'
+import { useSnackbar } from 'notistack'
+import { ReactElement, useContext, useState } from 'react'
+import Check from 'remixicon-react/CheckLineIcon'
+import { SwarmButton } from '../../components/SwarmButton'
+import { SwarmTextInput } from '../../components/SwarmTextInput'
+import { Context as SettingsContext } from '../../providers/Settings'
+import { Context as StampsContext } from '../../providers/Stamps'
+import { Link } from 'react-router-dom'
+import { ROUTES } from '../../routes'
+import { calculateStampPrice, convertAmountToSeconds, secondsToTimeString, waitUntilStampExists } from '../../utils'
+
+interface Props {
+ onFinished: () => void
+}
+const useStyles = makeStyles((theme: Theme) =>
+ createStyles({
+ link: {
+ color: '#dd7700',
+ textDecoration: 'underline',
+ '&:hover': {
+ textDecoration: 'none',
+
+ // https://github.com/mui-org/material-ui/issues/22543
+ '@media (hover: none)': {
+ textDecoration: 'none',
+ },
+ },
+ },
+ buttonSelected: {
+ color: 'white',
+ backgroundColor: theme.palette.primary.main,
+ },
+ }),
+)
+
+const marks = [
+ { value: 1, label: '1 day' },
+ { value: 365, label: '365 days' },
+]
+
+export function PostageStampStandardCreation({ onFinished }: Props): ReactElement {
+ const getDepthForCapacity = Utils.getDepthForCapacity
+ const getAmountForTtl = Utils.getAmountForTtl
+ const classes = useStyles()
+ const { refresh } = useContext(StampsContext)
+ const { beeDebugApi } = useContext(SettingsContext)
+
+ const [depthInput, setDepthInput] = useState(getDepthForCapacity(4))
+ const [amountInput, setAmountInput] = useState(Number.parseInt(getAmountForTtl(30)))
+ const [labelInput, setLabelInput] = useState('')
+ const [submitting, setSubmitting] = useState(false)
+ const [buttonValue, setButtonValue] = useState(4)
+
+ function sliderValueChange(event: any, newValue: any) {
+ const amountValue = Number.parseInt(getAmountForTtl(newValue))
+ setAmountInput(amountValue)
+ }
+
+ const { enqueueSnackbar } = useSnackbar()
+
+ function getTtl(amount: number): string {
+ const pricePerBlock = 24000
+
+ return `${secondsToTimeString(
+ convertAmountToSeconds(amount, pricePerBlock),
+ )} (with price of ${pricePerBlock.toFixed(0)} per block)`
+ }
+
+ function getPrice(depth: number, amount: bigint): string {
+ const price = calculateStampPrice(depth, amount)
+
+ return `${price.toSignificantDigits()} xBZZ`
+ }
+
+ async function submit() {
+ try {
+ // This is really just a typeguard, the validation pretty much guarantees these will have the right values
+ if (!depthInput || !amountInput) {
+ return
+ }
+
+ if (!beeDebugApi) {
+ return
+ }
+
+ setSubmitting(true)
+ const amount = BigInt(amountInput)
+ const depth = depthInput
+ const options: PostageBatchOptions = {
+ waitForUsable: false,
+ label: labelInput || undefined,
+ immutableFlag: false,
+ }
+
+ const batchId = await beeDebugApi.createPostageBatch(amount.toString(), depth, options)
+ await waitUntilStampExists(batchId, beeDebugApi)
+ await refresh()
+ onFinished()
+ } catch (e) {
+ console.error(e) // eslint-disable-line
+ enqueueSnackbar(`Error: ${(e as Error).message}`, { variant: 'error' })
+ }
+ setSubmitting(false)
+ }
+
+ function handleBatchSize(event: any) {
+ let value = event.target.innerText
+ value = Number(value.substring(0, value.length - 3))
+ setButtonValue(value)
+ const capacity = getDepthForCapacity(value)
+ setDepthInput(capacity)
+ }
+
+ return (
+ <>
+
+
+ A postage stamp batch containes postage stamps that will give you the right to upload data to the Swarm
+ network. If you're not familiar with this, please read
+
+ this guide
+
+ .
+
+
+
+ Batch name
+
+
+ setLabelInput(e.target.value)} />
+
+
+ Batch size
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Data persistence
+
+
+
+
+
+
+
+ Corresponding TTL (Time to live)
+ {amountInput ? getTtl(amountInput) : '-'}
+
+
+
+
+ Current price of 24000 per block
+
+
+
+
+
+ Indicative Price
+ {getPrice(depthInput, BigInt(amountInput))}
+
+
+
+
+
+ Buy New Stamp
+
+
+
+
+ Advanced mode
+
+
+
+ >
+ )
+}
diff --git a/src/pages/stamps/index.tsx b/src/pages/stamps/index.tsx
index 2a1bae6..79e7796 100644
--- a/src/pages/stamps/index.tsx
+++ b/src/pages/stamps/index.tsx
@@ -44,7 +44,7 @@ export default function Stamp(): ReactElement {
if (status.all === CheckState.ERROR) return
function navigateToNewStamp() {
- navigate(ROUTES.ACCOUNT_STAMPS_NEW)
+ navigate(ROUTES.ACCOUNT_STAMPS_NEW_STANDARD)
}
return (
diff --git a/src/routes.tsx b/src/routes.tsx
index 0093ee4..f72b0c8 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -16,7 +16,7 @@ import GiftCards from './pages/gift-code'
import Info from './pages/info'
import LightModeRestart from './pages/restart/LightModeRestart'
import Settings from './pages/settings'
-import { CreatePostageStampPage } from './pages/stamps/CreatePostageStampPage'
+import { CreatePostageStampPage } from './pages/stamps/CreatePostageStampAdvancedPage'
import Status from './pages/status'
import TopUp from './pages/top-up'
import { BankCardTopUpIndex } from './pages/top-up/BankCardTopUpIndex'
@@ -25,6 +25,7 @@ import { GiftCardFund } from './pages/top-up/GiftCardFund'
import { GiftCardTopUpIndex } from './pages/top-up/GiftCardTopUpIndex'
import { Swap } from './pages/top-up/Swap'
import { Context as SettingsContext } from './providers/Settings'
+import { CreatePostageStampBasicPage } from './pages/stamps/CreatePostageStampStandardPage'
export enum ROUTES {
INFO = '/',
@@ -46,7 +47,8 @@ export enum ROUTES {
ACCOUNT_WALLET = '/account/wallet',
ACCOUNT_CHEQUEBOOK = '/account/chequebook',
ACCOUNT_STAMPS = '/account/stamps',
- ACCOUNT_STAMPS_NEW = '/account/stamps/new',
+ ACCOUNT_STAMPS_NEW_STANDARD = '/account/stamps/new',
+ ACCOUNT_STAMPS_NEW_ADVANCED = '/account/stamps/new/advanced',
ACCOUNT_FEEDS = '/account/feeds',
ACCOUNT_FEEDS_NEW = '/account/feeds/new',
ACCOUNT_FEEDS_UPDATE = '/account/feeds/update/:hash',
@@ -86,7 +88,8 @@ const BaseRouter = (): ReactElement => {
} />
} />
} />
- } />
+ } />
+ } />
} />
} />
} />
diff --git a/src/theme.tsx b/src/theme.tsx
index 98a88c5..d78ae12 100644
--- a/src/theme.tsx
+++ b/src/theme.tsx
@@ -159,6 +159,23 @@ const componentsOverrides = (theme: Theme) => ({
backgroundColor: 'transparent',
},
},
+ MuiSlider: {
+ root: {
+ '& .MuiSlider-valueLabel': {
+ top: '-27px',
+ '& span': {
+ height: '20px',
+ borderRadius: '0px',
+ transform: 'none',
+ '& span': {
+ display: 'flex',
+ alignItems: 'center',
+ transform: 'none',
+ },
+ },
+ },
+ },
+ },
})
const propsOverrides = {
diff --git a/src/utils/stamps.ts b/src/utils/stamps.ts
new file mode 100644
index 0000000..47d20bf
--- /dev/null
+++ b/src/utils/stamps.ts
@@ -0,0 +1,25 @@
+export function getStampUsage(utilization: number, depth: number, bucketDepth: number): number {
+ return utilization / Math.pow(2, depth - bucketDepth)
+}
+
+export function getStampMaximumCapacityBytes(depth: number): number {
+ return 2 ** depth * 4096
+}
+
+export function getStampCostInPlur(depth: number, amount: number): number {
+ return 2 ** depth * amount
+}
+
+export function getStampCostInBzz(depth: number, amount: number): number {
+ const BZZ_UNIT = 10 ** 16
+
+ return getStampCostInPlur(depth, amount) / BZZ_UNIT
+}
+
+// export function getStampTtlSeconds(amount: number, pricePerBlock = 24_000, blockTime = 5): number {
+// return (amount * blockTime) / pricePerBlock
+// }
+
+export function getStampTtlSeconds(amount: number, pricePerBlock = 24_000, blockTime = 5): number {
+ return (amount * blockTime) / pricePerBlock
+}