diff --git a/src/app/components/Modal/index.tsx b/src/app/components/Modal/index.tsx
index baa01a8fdc..af23b6c02d 100644
--- a/src/app/components/Modal/index.tsx
+++ b/src/app/components/Modal/index.tsx
@@ -1,13 +1,30 @@
-import { createContext, useCallback, useContext, useState } from 'react'
+import React from 'react'
+import { createContext, useCallback, useContext, useEffect, useState } from 'react'
import { Box, Button, Layer, Heading, Paragraph } from 'grommet'
import { useTranslation } from 'react-i18next'
import { Alert, Checkmark, Close } from 'grommet-icons/icons'
+import { AlertBox } from '../AlertBox'
+import { selectAllowDangerousSetting } from '../SettingsDialog/slice/selectors'
+import { useSelector } from 'react-redux'
interface Modal {
title: string
description: string
handleConfirm: () => void
+
+ /**
+ * Is this a dangerous operation?
+ *
+ * If marked as such, it will only be possible to execute it if the wallet is configured to run in dangerous mode.
+ *
+ * It also automatically implies a mandatory waiting time of 10 sec, unless specified otherwise.
+ */
isDangerous: boolean
+
+ /**
+ * How long does the user have to wait before he can actually confirm the action?
+ */
+ mustWaitSecs?: number
}
interface ModalContainerProps {
@@ -32,12 +49,55 @@ const ModalContainer = ({ modal, closeModal }: ModalContainerProps) => {
modal.handleConfirm()
closeModal()
}, [closeModal, modal])
+ const { isDangerous, mustWaitSecs } = modal
+ const allowDangerous = useSelector(selectAllowDangerousSetting)
+ const forbidden = isDangerous && !allowDangerous
+ const waitingTime = forbidden
+ ? 0 // If the action is forbidden, there is nothing to wait for
+ : isDangerous
+ ? mustWaitSecs ?? 10 // For dangerous actions, we require 10 seconds of waiting, unless specified otherwise.
+ : mustWaitSecs ?? 0 // For normal, non-dangerous operations, just use what was specified
+
+ const [secsLeft, setSecsLeft] = useState(0)
+
+ useEffect(() => {
+ if (waitingTime) {
+ setSecsLeft(waitingTime)
+ const stopCounting = () => window.clearInterval(interval)
+ const interval = window.setInterval(
+ () =>
+ setSecsLeft(seconds => {
+ const remains = seconds - 1
+ if (!remains) stopCounting()
+ return remains
+ }),
+ 1000,
+ )
+ return stopCounting
+ }
+ }, [waitingTime])
return (
{modal.title}
{modal.description}
+ {forbidden && (
+
+ {t(
+ 'dangerMode.youDontWantThis',
+ "You most probably don't want to do this, so I won't allow it. If you really do, then please enable the 'dangerous mode' in wallet settings, and try again.",
+ )}
+
+ )}
+ {isDangerous && allowDangerous && (
+
+ {t(
+ 'dangerMode.youCanButDoYouWant',
+ "You most probably shouldn't do this, but since you have specifically enabled 'dangerous mode' in wallet settings, we won't stop you.",
+ )}
+
+ )}
diff --git a/src/app/components/SettingsButton/index.tsx b/src/app/components/SettingsButton/index.tsx
new file mode 100644
index 0000000000..81248d8fde
--- /dev/null
+++ b/src/app/components/SettingsButton/index.tsx
@@ -0,0 +1,22 @@
+import React, { useState } from 'react'
+import { SidebarButton } from '../Sidebar'
+import { Configure } from 'grommet-icons/icons'
+import { useTranslation } from 'react-i18next'
+import { SettingsDialog } from '../SettingsDialog'
+
+export const SettingsButton = () => {
+ const [layerVisibility, setLayerVisibility] = useState(false)
+ const { t } = useTranslation()
+ return (
+ <>
+ }
+ label={t('menu.settings', 'Settings')}
+ onClick={() => {
+ setLayerVisibility(true)
+ }}
+ />
+ {layerVisibility && setLayerVisibility(false)} />}
+ >
+ )
+}
diff --git a/src/app/components/SettingsDialog/index.tsx b/src/app/components/SettingsDialog/index.tsx
new file mode 100644
index 0000000000..dc5c48017c
--- /dev/null
+++ b/src/app/components/SettingsDialog/index.tsx
@@ -0,0 +1,80 @@
+import React, { useContext } from 'react'
+import { useTranslation } from 'react-i18next'
+import { ResponsiveLayer } from '../ResponsiveLayer'
+import { Box, Button, Heading, Paragraph, RadioButtonGroup, ResponsiveContext } from 'grommet'
+import { useDispatch, useSelector } from 'react-redux'
+import { selectAllowDangerousSetting } from './slice/selectors'
+import { Threats } from 'grommet-icons'
+import { settingsActions } from './slice'
+
+interface SettingsDialogProps {
+ closeHandler: () => void
+}
+
+export const SettingsDialog = (props: SettingsDialogProps) => {
+ const { t } = useTranslation()
+ const size = useContext(ResponsiveContext)
+
+ const dispatch = useDispatch()
+ const dangerousMode = useSelector(selectAllowDangerousSetting)
+
+ return (
+
+
+
+ {t('settings.dialog.title', 'Wallet settings')}
+
+
+ {t(
+ 'settings.dialog.description',
+ 'This is where you can configure the behavior of the Oasis Wallet.',
+ )}
+
+
+
+
+ {t(
+ 'dangerMode.description',
+ 'Dangerous mode: should the wallet let the user shoot himself in the foot?',
+ )}
+
+
+
+ {t('dangerMode.on', "On - Allow executing nonsensical actions. Don't blame Oasis!")}{' '}
+
+
+ ),
+ },
+ ]}
+ value={dangerousMode}
+ onChange={event => dispatch(settingsActions.setAllowDangerous(event.target.value === 'true'))}
+ />
+
+
+
+
+
+
+ )
+}
diff --git a/src/app/components/SettingsDialog/slice/index.ts b/src/app/components/SettingsDialog/slice/index.ts
new file mode 100644
index 0000000000..9e1d0a08bd
--- /dev/null
+++ b/src/app/components/SettingsDialog/slice/index.ts
@@ -0,0 +1,22 @@
+import { PayloadAction } from '@reduxjs/toolkit'
+import { createSlice } from 'utils/@reduxjs/toolkit'
+
+import { SettingsState } from './types'
+
+export const initialState: SettingsState = {
+ allowDangerous: false,
+}
+
+const slice = createSlice({
+ name: 'settings',
+ initialState,
+ reducers: {
+ setAllowDangerous(state, action: PayloadAction) {
+ state.allowDangerous = action.payload
+ },
+ },
+})
+
+export const { actions: settingsActions } = slice
+
+export default slice.reducer
diff --git a/src/app/components/SettingsDialog/slice/selectors.ts b/src/app/components/SettingsDialog/slice/selectors.ts
new file mode 100644
index 0000000000..d60ea31546
--- /dev/null
+++ b/src/app/components/SettingsDialog/slice/selectors.ts
@@ -0,0 +1,8 @@
+import { createSelector } from '@reduxjs/toolkit'
+
+import { RootState } from 'types'
+import { initialState } from '.'
+
+const selectSlice = (state: RootState) => state.settings || initialState
+
+export const selectAllowDangerousSetting = createSelector([selectSlice], settings => settings.allowDangerous)
diff --git a/src/app/components/SettingsDialog/slice/types.ts b/src/app/components/SettingsDialog/slice/types.ts
new file mode 100644
index 0000000000..d8dd1a2c99
--- /dev/null
+++ b/src/app/components/SettingsDialog/slice/types.ts
@@ -0,0 +1,3 @@
+export interface SettingsState {
+ allowDangerous: boolean
+}
diff --git a/src/app/components/Sidebar/__tests__/__snapshots__/index.test.tsx.snap b/src/app/components/Sidebar/__tests__/__snapshots__/index.test.tsx.snap
index fb52c86d38..b9bd19297f 100644
--- a/src/app/components/Sidebar/__tests__/__snapshots__/index.test.tsx.snap
+++ b/src/app/components/Sidebar/__tests__/__snapshots__/index.test.tsx.snap
@@ -672,6 +672,39 @@ exports[` should match snapshot 1`] = `
+
+
+
+
+
+ menu.settings
+
+
+
+
{
const size = useContext(ResponsiveContext)
@@ -211,6 +212,7 @@ const SidebarFooter = (props: SidebarFooterProps) => {
+
}
label="GitHub"
diff --git a/src/app/components/Transaction/index.tsx b/src/app/components/Transaction/index.tsx
index d46b8b2140..4f8590af5a 100644
--- a/src/app/components/Transaction/index.tsx
+++ b/src/app/components/Transaction/index.tsx
@@ -18,6 +18,7 @@ import {
LinkNext,
Atm,
Alert,
+ Trash,
} from 'grommet-icons/icons'
import type { Icon } from 'grommet-icons/icons'
import * as React from 'react'
@@ -99,6 +100,19 @@ export function Transaction(props: TransactionProps) {
),
}
+ const burnTransaction: TransactionDictionary[transactionTypes.TransactionType][TransactionSide] = {
+ destination: '',
+ icon: Trash,
+ header: () => (
+
+ ),
+ }
+
const unrecognizedTransaction: TransactionDictionary[transactionTypes.TransactionType][TransactionSide] = {
destination: t('account.transaction.unrecognizedTransaction.destination', 'Other address'),
icon: Alert,
@@ -286,6 +300,10 @@ export function Transaction(props: TransactionProps) {
[TransactionSide.Received]: genericTransaction,
[TransactionSide.Sent]: genericTransaction,
},
+ [transactionTypes.TransactionType.StakingBurn]: {
+ [TransactionSide.Received]: burnTransaction,
+ [TransactionSide.Sent]: burnTransaction,
+ },
[transactionTypes.TransactionType.RoothashExecutorCommit]: {
[TransactionSide.Received]: genericTransaction,
[TransactionSide.Sent]: genericTransaction,
@@ -349,6 +367,7 @@ export function Transaction(props: TransactionProps) {
const Icon = matchingConfiguration.icon
const header = matchingConfiguration.header()
+ const hasDestination = transaction.type !== transactionTypes.TransactionType.StakingBurn
const destination = matchingConfiguration.destination
const backendLinks = config[props.network][backend()]
const externalExplorerLink = transaction.runtimeId
@@ -392,13 +411,15 @@ export function Transaction(props: TransactionProps) {
{!isMobile && (
-
+ {hasDestination && (
+
+ )}
{
addEscrow: t('transaction.types.addEscrow', 'Delegating your tokens to a validator and generate rewards'),
reclaimEscrow: t('transaction.types.reclaimEscrow', 'Reclaiming your tokens delegated to a validator'),
transfer: t('transaction.types.transfer', 'Transferring tokens from your account to another'),
+ burn: t('transaction.types.burn', 'Burn tokens in your account'),
}
const typeMessage = typeMap[type]
diff --git a/src/app/lib/transaction.ts b/src/app/lib/transaction.ts
index 02252f36a9..53b2f07303 100644
--- a/src/app/lib/transaction.ts
+++ b/src/app/lib/transaction.ts
@@ -79,6 +79,25 @@ export class OasisTransaction {
return tw
}
+ public static async buildBurn(
+ nic: OasisClient,
+ signer: Signer,
+ amount: bigint,
+ ): Promise> {
+ const tw = oasis.staking.burnWrapper()
+ const nonce = await OasisTransaction.getNonce(nic, signer)
+ tw.setNonce(nonce)
+ tw.setFeeAmount(oasis.quantity.fromBigInt(0n))
+ tw.setBody({
+ amount: oasis.quantity.fromBigInt(amount),
+ })
+
+ const gas = await tw.estimateGas(nic, signer.public())
+ tw.setFeeGas(gas)
+
+ return tw
+ }
+
public static async signUsingLedger(
chainContext: string,
signer: ContextSigner,
diff --git a/src/app/pages/AccountPage/Features/AccountTokenBurning/index.tsx b/src/app/pages/AccountPage/Features/AccountTokenBurning/index.tsx
new file mode 100644
index 0000000000..7b50ecf6f6
--- /dev/null
+++ b/src/app/pages/AccountPage/Features/AccountTokenBurning/index.tsx
@@ -0,0 +1,30 @@
+/**
+ *
+ * AccountDetails
+ *
+ */
+import { Box } from 'grommet'
+import React, { memo } from 'react'
+import { useSelector } from 'react-redux'
+import { TransactionHistory } from '../TransactionHistory'
+import { selectIsAddressInWallet } from 'app/state/selectIsAddressInWallet'
+import { SendBurn } from '../SendBurn'
+
+interface Props {}
+
+export const AccountTokenBurning = memo((props: Props) => {
+ const isAddressInWallet = useSelector(selectIsAddressInWallet)
+
+ return (
+
+ {isAddressInWallet && (
+
+
+
+ )}
+
+
+
+
+ )
+})
diff --git a/src/app/pages/AccountPage/Features/SendBurn/__tests__/index.test.tsx b/src/app/pages/AccountPage/Features/SendBurn/__tests__/index.test.tsx
new file mode 100644
index 0000000000..bf1c2f3c23
--- /dev/null
+++ b/src/app/pages/AccountPage/Features/SendBurn/__tests__/index.test.tsx
@@ -0,0 +1,38 @@
+import { render, screen } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import { transactionActions } from 'app/state/transaction'
+import * as React from 'react'
+import { Provider } from 'react-redux'
+import { configureAppStore } from 'store/configureStore'
+import { SendBurn } from '..'
+
+const renderComponent = (store: any) =>
+ render(
+
+
+ ,
+ )
+
+describe('', () => {
+ let store: ReturnType
+
+ beforeEach(() => {
+ store = configureAppStore()
+ })
+
+ it('should dispatch sendBurn action on submit', () => {
+ const spy = jest.spyOn(store, 'dispatch')
+ renderComponent(store)
+
+ userEvent.type(screen.getByPlaceholderText('0'), '10')
+ userEvent.click(screen.getByRole('button'))
+
+ expect(spy).toHaveBeenCalledWith({
+ payload: {
+ amount: '10000000000',
+ type: 'burn',
+ },
+ type: 'transaction/sendBurn',
+ } as ReturnType)
+ })
+})
diff --git a/src/app/pages/AccountPage/Features/SendBurn/index.tsx b/src/app/pages/AccountPage/Features/SendBurn/index.tsx
new file mode 100644
index 0000000000..bdc166e6a9
--- /dev/null
+++ b/src/app/pages/AccountPage/Features/SendBurn/index.tsx
@@ -0,0 +1,83 @@
+import { TransactionStatus } from 'app/components/TransactionStatus'
+import { transactionActions } from 'app/state/transaction'
+import { selectTransaction } from 'app/state/transaction/selectors'
+import { Box, Button, Form, FormField, TextInput } from 'grommet'
+import * as React from 'react'
+import { useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { useDispatch, useSelector } from 'react-redux'
+import { parseRoseStringToBaseUnitString } from 'app/lib/helpers'
+import { useModal } from '../../../../components/Modal'
+
+export interface SendBurnProps {
+ isAddressInWallet: boolean
+}
+
+export function SendBurn(props: SendBurnProps) {
+ if (!props.isAddressInWallet) {
+ throw new Error('SendTransaction component should only appear on your accounts')
+ }
+
+ const { t } = useTranslation()
+ const dispatch = useDispatch()
+ const { launchModal } = useModal()
+ const { error, success } = useSelector(selectTransaction)
+ const [amount, setAmount] = useState('')
+ const sendBurn = () =>
+ dispatch(
+ transactionActions.sendBurn({
+ type: 'burn',
+ amount: parseRoseStringToBaseUnitString(amount),
+ }),
+ )
+ const onSubmit = () =>
+ launchModal({
+ title: t('account.sendBurn.confirmBurning.title', 'Are you sure you want to continue?'),
+ description: t(
+ 'account.sendBurn.confirmBurning.description',
+ 'You are about to burn these tokens. You are not sending them anywhere, but destroying them. They will completely cease to exist, and there is no way to get them back.',
+ ),
+ handleConfirm: sendBurn,
+ isDangerous: true,
+ })
+
+ // On successful transaction, clear the fields
+ useEffect(() => {
+ if (success) {
+ setAmount('')
+ }
+ }, [success])
+
+ // Cleanup effect - clear the transaction when the component unmounts
+ useEffect(() => {
+ return function cleanup() {
+ dispatch(transactionActions.clearTransaction())
+ }
+ }, [dispatch])
+
+ return (
+
+
+
+
+ )
+}
diff --git a/src/app/pages/AccountPage/__tests__/__snapshots__/index.test.tsx.snap b/src/app/pages/AccountPage/__tests__/__snapshots__/index.test.tsx.snap
index cc99e9d1fe..b63d1adba6 100644
--- a/src/app/pages/AccountPage/__tests__/__snapshots__/index.test.tsx.snap
+++ b/src/app/pages/AccountPage/__tests__/__snapshots__/index.test.tsx.snap
@@ -1015,6 +1015,16 @@ exports[` should match snapshot 1`] = `
Debonding
+
+
+ Burning tokens
+
+
should match snapshot 1`] = `
exports[` with missing delegations 1`] = `"Total-Available100.0 Staked-Debonding-"`;
-exports[` with missing delegations 2`] = `"TransactionsDelegationsDebonding"`;
+exports[` with missing delegations 2`] = `"TransactionsDelegationsDebondingBurning tokens"`;
diff --git a/src/app/pages/AccountPage/index.tsx b/src/app/pages/AccountPage/index.tsx
index 5f075649fe..e2cff4e756 100644
--- a/src/app/pages/AccountPage/index.tsx
+++ b/src/app/pages/AccountPage/index.tsx
@@ -35,6 +35,7 @@ import { mobileHeaderZIndex } from '../../components/Sidebar'
import { ValidatorList } from '../StakingPage/Features/ValidatorList'
import { AccountDetails } from './Features/AccountDetails'
import { AccountSummary } from './Features/AccountSummary'
+import { AccountTokenBurning } from './Features/AccountTokenBurning'
const StyledNavItem = styled(NavLink)`
display: flex;
@@ -201,12 +202,18 @@ export function AccountPage(props: Props) {
}
route="debonding-delegations"
/>
+
+
} />
} />
} />
} />
+ } />
>
)}
diff --git a/src/app/state/account/saga.ts b/src/app/state/account/saga.ts
index a38a29a1a7..f2c738679a 100644
--- a/src/app/state/account/saga.ts
+++ b/src/app/state/account/saga.ts
@@ -91,10 +91,22 @@ export function* refreshAccountOnTransaction() {
const from = yield* select(selectAddress)
let otherAddress: string
- if (payload.type === 'transfer') {
- otherAddress = payload.to
- } else {
- otherAddress = payload.validator
+ switch (payload.type) {
+ case 'transfer':
+ otherAddress = payload.to
+ break
+ case 'addEscrow':
+ otherAddress = payload.validator
+ break
+ case 'reclaimEscrow':
+ otherAddress = payload.validator
+ break
+ case 'burn':
+ otherAddress = 'none'
+ break
+ default:
+ console.warn('Unknown payload type', (payload as any).type)
+ otherAddress = 'none'
}
const currentAccount = yield* select(selectAccountAddress)
diff --git a/src/app/state/transaction/index.ts b/src/app/state/transaction/index.ts
index ffe8a3f3e9..af1a0d996d 100644
--- a/src/app/state/transaction/index.ts
+++ b/src/app/state/transaction/index.ts
@@ -9,6 +9,7 @@ import {
TransactionStep,
TransactionPayload,
ReclaimEscrowPayload,
+ BurnPayload,
} from './types'
export const initialState: TransactionState = {
@@ -46,6 +47,11 @@ const slice = createSlice({
state.success = false
state.active = true
},
+ sendBurn(state, action: PayloadAction) {
+ state.error = undefined
+ state.success = false
+ state.active = true
+ },
updateTransactionPreview(state, action: PayloadAction) {
state.preview = Object.assign({}, action.payload)
},
diff --git a/src/app/state/transaction/saga.ts b/src/app/state/transaction/saga.ts
index 70d71e20e8..b880795b8a 100644
--- a/src/app/state/transaction/saga.ts
+++ b/src/app/state/transaction/saga.ts
@@ -18,6 +18,7 @@ export function* transactionSaga() {
yield* takeEvery(transactionActions.sendTransaction, doTransaction)
yield* takeEvery(transactionActions.addEscrow, doTransaction)
yield* takeEvery(transactionActions.reclaimEscrow, doTransaction)
+ yield* takeEvery(transactionActions.sendBurn, doTransaction)
}
function* setStep(step: TransactionStep) {
@@ -95,6 +96,15 @@ function* prepareReclaimEscrow(signer: Signer, shares: bigint, validator: string
return yield* call(OasisTransaction.buildReclaimEscrow, nic, signer as Signer, validator, shares)
}
+function* prepareBurn(signer: Signer, amount: bigint) {
+ const nic = yield* call(getOasisNic)
+
+ yield* call(assertWalletIsOpen)
+ yield* call(assertSufficientBalance, amount)
+
+ return yield* call(OasisTransaction.buildBurn, nic, signer as Signer, amount)
+}
+
/**
* Generate transaction, sign, push to node
*
@@ -137,6 +147,10 @@ export function* doTransaction(action: PayloadAction) {
)
break
+ case 'burn':
+ tw = yield* call(prepareBurn, signer as Signer, BigInt(action.payload.amount))
+ break
+
default:
throw new ExhaustedTypeError('Unsupported transaction type', action.payload)
}
diff --git a/src/app/state/transaction/types.ts b/src/app/state/transaction/types.ts
index c27a5515d0..48023127ef 100644
--- a/src/app/state/transaction/types.ts
+++ b/src/app/state/transaction/types.ts
@@ -8,6 +8,7 @@ export enum TransactionType {
StakingAmendCommissionSchedule = 'staking.AmendCommissionSchedule',
StakingAllow = 'staking.Allow',
StakingWithdraw = 'staking.Withdraw',
+ StakingBurn = 'staking.Burn',
RoothashExecutorCommit = 'roothash.ExecutorCommit',
RoothashExecutorProposerTimeout = 'roothash.ExecutorProposerTimeout',
RegistryRegisterEntity = 'registry.RegisterEntity',
@@ -51,8 +52,8 @@ export interface TransactionState {
preview?: TransactionPreview
}
-export type NewTransactionType = 'transfer' | 'addEscrow' | 'reclaimEscrow'
-export type TransactionPayload = TransferPayload | AddEscrowPayload | ReclaimEscrowPayload
+export type NewTransactionType = 'transfer' | 'addEscrow' | 'reclaimEscrow' | 'burn'
+export type TransactionPayload = TransferPayload | AddEscrowPayload | ReclaimEscrowPayload | BurnPayload
export interface TransactionPreview {
transaction: TransactionPayload
@@ -103,3 +104,10 @@ export interface ReclaimEscrowPayload {
/* Displayed token equivalent */
amount: StringifiedBigInt
}
+
+export interface BurnPayload {
+ type: 'burn'
+
+ /* Token amount */
+ amount: StringifiedBigInt
+}
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index c7af45d1d7..3a45c636a8 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -18,6 +18,13 @@
"reclaim": "Reclaim",
"reclaimAll": "Reclaim all"
},
+ "sendBurn": {
+ "burn": "Burn",
+ "confirmBurning": {
+ "description": "You are about to burn these tokens. You are not sending them anywhere, but destroying them. They will completely cease to exist, and there is no way to get them back.",
+ "title": "Are you sure you want to continue?"
+ }
+ },
"sendTransaction": {
"confirmSendingToValidator": {
"description": "This is a validator address. Transfers to this address do not stake your funds with the validator.",
@@ -30,6 +37,7 @@
},
"subnavigation": {
"activeDelegations": "Active delegations",
+ "burning": "Burning tokens",
"debondingDelegations": "Debonding delegations",
"mobileActiveDelegations": "Delegations",
"mobileDebondingDelegations": "Debonding",
@@ -52,6 +60,9 @@
"received": "Received delegation in escrow",
"sent": "Delegated to validator"
},
+ "burnTransaction": {
+ "header": "Burned "
+ },
"consensusDeposit": {
"received": "Received deposit into {{runtimeName}} ParaTime",
"sent": "Deposited into {{runtimeName}} ParaTime"
@@ -96,6 +107,9 @@
"oasismonitor": "Oasis Monitor API",
"oasisscan": "Oasis Scan API"
},
+ "burn": {
+ "amount": "Amount to burn"
+ },
"common": {
"amount": "Amount",
"block": "Block",
@@ -119,6 +133,13 @@
"newMnemonic": "Generate a new mnemonic",
"thisIsYourPhrase": "This is your mnemonic"
},
+ "dangerMode": {
+ "description": "Dangerous mode: should the wallet let the user shoot himself in the foot?",
+ "off": "Off - Refuse to execute nonsensical actions",
+ "on": "On - Allow executing nonsensical actions. Don't blame Oasis!",
+ "youCanButDoYouWant": "You most probably shouldn't do this, but since you have specifically enabled 'dangerous mode' in wallet settings, we won't stop you.",
+ "youDontWantThis": "You most probably don't want to do this, so I won't allow it. If you really do, then please enable the 'dangerous mode' in wallet settings, and try again."
+ },
"delegations": {
"activeDelegations": "Active delegations",
"debondingDelegations": "Debonding delegations",
@@ -193,6 +214,7 @@
"menu": {
"closeWallet": "Close wallet",
"home": "Home",
+ "settings": "Settings",
"stake": "Stake",
"wallet": "Wallet"
},
@@ -229,6 +251,13 @@
"showPrivateKey": "Show private key"
}
},
+ "settings": {
+ "dialog": {
+ "close": "Close",
+ "description": "This is where you can configure the behavior of the Oasis Wallet.",
+ "title": "Wallet settings"
+ }
+ },
"theme": {
"darkMode": "Dark mode",
"lightMode": "Light mode"
@@ -272,6 +301,7 @@
},
"types": {
"addEscrow": "Delegating your tokens to a validator and generate rewards",
+ "burn": "Burn tokens in your account",
"reclaimEscrow": "Reclaiming your tokens delegated to a validator",
"transfer": "Transferring tokens from your account to another"
}
diff --git a/src/store/reducers.ts b/src/store/reducers.ts
index eb5435e3cb..9111fd363f 100644
--- a/src/store/reducers.ts
+++ b/src/store/reducers.ts
@@ -12,6 +12,7 @@ import stakingReducer from 'app/state/staking'
import transactionReducer from 'app/state/transaction'
import walletReducer from 'app/state/wallet'
import themeReducer from 'styles/theme/slice'
+import settingReducer from 'app/components/SettingsDialog/slice'
export function createReducer() {
const rootReducer = combineReducers({
@@ -24,6 +25,7 @@ export function createReducer() {
theme: themeReducer,
transaction: transactionReducer,
wallet: walletReducer,
+ settings: settingReducer,
})
return rootReducer
diff --git a/src/types/RootState.ts b/src/types/RootState.ts
index f3e356a3f7..7c554adcb4 100644
--- a/src/types/RootState.ts
+++ b/src/types/RootState.ts
@@ -8,6 +8,7 @@ import { TransactionState } from 'app/state/transaction/types'
import { ImportAccountsState } from 'app/state/importaccounts/types'
import { StakingState } from 'app/state/staking/types'
import { FatalErrorState } from 'app/state/fatalerror/types'
+import { SettingsState } from '../app/components/SettingsDialog/slice/types'
// [IMPORT NEW CONTAINERSTATE ABOVE] < Needed for generating containers seamlessly
export interface RootState {
@@ -20,6 +21,7 @@ export interface RootState {
importAccounts: ImportAccountsState
staking: StakingState
fatalError: FatalErrorState
+ settings: SettingsState
// [INSERT NEW REDUCER KEY ABOVE] < Needed for generating containers seamlessly
}
diff --git a/src/vendors/explorer/models/OperationsRow.ts b/src/vendors/explorer/models/OperationsRow.ts
index 77aac749ef..2cb60734ab 100644
--- a/src/vendors/explorer/models/OperationsRow.ts
+++ b/src/vendors/explorer/models/OperationsRow.ts
@@ -5,7 +5,7 @@
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* The version of the OpenAPI document: 1
- *
+ *
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
@@ -14,97 +14,97 @@
import { exists, mapValues } from '../runtime';
/**
- *
+ *
* @export
* @interface OperationsRow
*/
export interface OperationsRow {
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
level?: number;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
hash?: string;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
nonce?: number;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
timestamp?: number;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
type?: OperationsRowTypeEnum;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
from?: string;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
to?: string;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
amount?: number;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
escrow_amount?: number;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
reclaim_escrow_amount?: number;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
fee?: number;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
gas_used?: number;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
gas_price?: number;
/**
- *
+ *
* @type {boolean}
* @memberof OperationsRow
*/
status?: boolean;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
@@ -131,7 +131,8 @@ export enum OperationsRowTypeEnum {
Submitproposal = 'submitproposal',
Pvsscommit = 'pvsscommit',
Pvssreveal = 'pvssreveal',
- Vrfprove = 'vrfprove'
+ Vrfprove = 'vrfprove',
+ Burn = "burn"
}
export function OperationsRowFromJSON(json: any): OperationsRow {
@@ -143,7 +144,7 @@ export function OperationsRowFromJSONTyped(json: any, ignoreDiscriminator: boole
return json;
}
return {
-
+
'level': !exists(json, 'level') ? undefined : json['level'],
'hash': !exists(json, 'hash') ? undefined : json['hash'],
'nonce': !exists(json, 'nonce') ? undefined : json['nonce'],
@@ -170,7 +171,7 @@ export function OperationsRowToJSON(value?: OperationsRow | null): any {
return null;
}
return {
-
+
'level': value.level,
'hash': value.hash,
'nonce': value.nonce,
diff --git a/src/vendors/monitor.ts b/src/vendors/monitor.ts
index 9fe89b866e..8c4af011a8 100644
--- a/src/vendors/monitor.ts
+++ b/src/vendors/monitor.ts
@@ -141,6 +141,7 @@ const transactionMethodMap: { [k in OperationsRowTypeEnum]: TransactionType } =
[OperationsRowTypeEnum.Pvsscommit]: TransactionType.BeaconPvssCommit,
[OperationsRowTypeEnum.Pvssreveal]: TransactionType.BeaconPvssReveal,
[OperationsRowTypeEnum.Vrfprove]: TransactionType.BeaconVrfProve,
+ [OperationsRowTypeEnum.Burn]: TransactionType.StakingBurn,
}
export function parseTransactionsList(transactionsList: OperationsRow[]): Transaction[] {
diff --git a/src/vendors/oasisscan.ts b/src/vendors/oasisscan.ts
index 460a82f4cf..584952b85a 100644
--- a/src/vendors/oasisscan.ts
+++ b/src/vendors/oasisscan.ts
@@ -126,6 +126,7 @@ export const transactionMethodMap: {
[OperationsRowMethodEnum.StakingAmendCommissionSchedule]: TransactionType.StakingAmendCommissionSchedule,
[OperationsRowMethodEnum.StakingAllow]: TransactionType.StakingAllow,
[OperationsRowMethodEnum.StakingWithdraw]: TransactionType.StakingWithdraw,
+ [OperationsRowMethodEnum.StakingBurn]: TransactionType.StakingBurn,
[OperationsRowMethodEnum.RoothashExecutorCommit]: TransactionType.RoothashExecutorCommit,
[OperationsRowMethodEnum.RoothashExecutorProposerTimeout]: TransactionType.RoothashExecutorProposerTimeout,
[OperationsRowMethodEnum.RegistryRegisterEntity]: TransactionType.RegistryRegisterEntity,
diff --git a/src/vendors/oasisscan/models/OperationsRow.ts b/src/vendors/oasisscan/models/OperationsRow.ts
index 456908e963..50d3826934 100644
--- a/src/vendors/oasisscan/models/OperationsRow.ts
+++ b/src/vendors/oasisscan/models/OperationsRow.ts
@@ -5,7 +5,7 @@
* https://github.com/bitcat365/oasisscan-backend#readme
*
* The version of the OpenAPI document: 1
- *
+ *
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
@@ -14,79 +14,79 @@
import { exists, mapValues } from '../runtime';
/**
- *
+ *
* @export
* @interface OperationsRow
*/
export interface OperationsRow {
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
txHash: string;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
height: number;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
method: OperationsRowMethodEnum;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
fee: string;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
amount: string | null;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
shares?: string | null;
/**
- *
+ *
* @type {boolean}
* @memberof OperationsRow
*/
add: boolean;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
timestamp: number;
/**
- *
+ *
* @type {number}
* @memberof OperationsRow
*/
time: number;
/**
- *
+ *
* @type {boolean}
* @memberof OperationsRow
*/
status: boolean;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
from: string;
/**
- *
+ *
* @type {string}
* @memberof OperationsRow
*/
@@ -110,6 +110,7 @@ export enum OperationsRowMethodEnum {
StakingAmendCommissionSchedule = 'staking.AmendCommissionSchedule',
StakingAllow = 'staking.Allow',
StakingWithdraw = 'staking.Withdraw',
+ StakingBurn = 'staking.Burn',
RoothashExecutorCommit = 'roothash.ExecutorCommit',
RoothashExecutorProposerTimeout = 'roothash.ExecutorProposerTimeout',
RegistryRegisterEntity = 'registry.RegisterEntity',
@@ -131,7 +132,7 @@ export function OperationsRowFromJSONTyped(json: any, ignoreDiscriminator: boole
return json;
}
return {
-
+
'txHash': json['txHash'],
'height': json['height'],
'method': json['method'],
@@ -156,7 +157,7 @@ export function OperationsRowToJSON(value?: OperationsRow | null): any {
return null;
}
return {
-
+
'txHash': value.txHash,
'height': value.height,
'method': value.method,