fix: handle auth and server error during swap (#593)
* fix: change execution order for light node upgrade * refactor: grab new configuration from post config request * fix: only print successful light node upgrade when it really happens * fix: log full desktop side swap error (#596) * refactor: try to make the auth error in swap nicer * refactor: make error instruction consistent * fix: avoid overwriting daiToSwap when it is set manually
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, Grid, Typography } from '@material-ui/core'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import { Waiting } from '../../components/Waiting'
|
||||
@@ -12,6 +13,7 @@ export default function LightModeRestart(): ReactElement {
|
||||
const [startedAt] = useState(Number.parseInt(localStorage.getItem(STARTED_UPGRADE_AT) ?? Date.now().toFixed()))
|
||||
const { apiHealth, nodeInfo } = useContext(Context)
|
||||
const navigate = useNavigate()
|
||||
const { enqueueSnackbar } = useSnackbar()
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem(STARTED_UPGRADE_AT, startedAt.toFixed())
|
||||
@@ -23,10 +25,11 @@ export default function LightModeRestart(): ReactElement {
|
||||
}
|
||||
|
||||
if (apiHealth && nodeInfo?.beeMode === BeeModes.LIGHT) {
|
||||
enqueueSnackbar('Upgraded to light node', { variant: 'success' })
|
||||
localStorage.removeItem(STARTED_UPGRADE_AT)
|
||||
navigate(ROUTES.INFO)
|
||||
}
|
||||
}, [startedAt, navigate, nodeInfo, apiHealth])
|
||||
}, [startedAt, navigate, nodeInfo, apiHealth, enqueueSnackbar])
|
||||
|
||||
return (
|
||||
<Grid container direction="column" justifyContent="center" alignItems="center">
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, Typography } from '@material-ui/core'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useEffect, useState } from 'react'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import ArrowDown from 'remixicon-react/ArrowDownLineIcon'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
import ArrowDown from 'remixicon-react/ArrowDownLineIcon'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import ExpandableListItem from '../../components/ExpandableListItem'
|
||||
import ExpandableListItemKey from '../../components/ExpandableListItemKey'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
@@ -18,7 +19,6 @@ import { ROUTES } from '../../routes'
|
||||
import { sleepMs } from '../../utils'
|
||||
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
||||
import { ResolvedWallet } from '../../utils/wallet'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
|
||||
export function GiftCardFund(): ReactElement {
|
||||
const { nodeAddresses, nodeInfo } = useContext(BeeContext)
|
||||
@@ -52,7 +52,6 @@ export function GiftCardFund(): ReactElement {
|
||||
await sleepMs(5_000)
|
||||
await upgradeToLightNode(desktopUrl, rpcProviderUrl)
|
||||
await restartBeeNode(desktopUrl)
|
||||
enqueueSnackbar('Upgraded to light node', { variant: 'success' })
|
||||
navigate(ROUTES.RESTART_LIGHT)
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
|
||||
@@ -65,7 +65,7 @@ export function Swap({ header }: Props): ReactElement {
|
||||
|
||||
// Set the initial xDAI to swap
|
||||
useEffect(() => {
|
||||
if (!balance) {
|
||||
if (!balance || userInputSwap) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export function Swap({ header }: Props): ReactElement {
|
||||
// Balance is low, halve the amount
|
||||
setDaiToSwap(new DaiToken(balance.dai.toBigNumber.dividedToIntegerBy(2)))
|
||||
}
|
||||
}, [balance])
|
||||
}, [balance, userInputSwap])
|
||||
|
||||
// Set the xDAI to swap based on user input
|
||||
useEffect(() => {
|
||||
@@ -126,10 +126,8 @@ export function Swap({ header }: Props): ReactElement {
|
||||
async function restart() {
|
||||
try {
|
||||
await sleepMs(5_000)
|
||||
await upgradeToLightNode(desktopUrl, rpcProviderUrl)
|
||||
await restartBeeNode(desktopUrl)
|
||||
|
||||
enqueueSnackbar('Upgraded to light node', { variant: 'success' })
|
||||
navigate(ROUTES.RESTART_LIGHT)
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
@@ -137,12 +135,33 @@ export function Swap({ header }: Props): ReactElement {
|
||||
}
|
||||
}
|
||||
|
||||
async function sendSwapRequest(daiToSwap: DaiToken) {
|
||||
try {
|
||||
await performSwap(desktopUrl, daiToSwap.toString)
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function performSwapWithChecks(daiToSwap: DaiToken) {
|
||||
const desktopConfiguration = await wrapWithSwapError(
|
||||
if (!localStorage.getItem('apiKey')) {
|
||||
throw new SwapError('API key is not set, reopen dashboard through Swarm Desktop')
|
||||
}
|
||||
|
||||
let desktopConfiguration = await wrapWithSwapError(
|
||||
getDesktopConfiguration(desktopUrl),
|
||||
'Unable to reach Desktop API. Is Swarm Desktop running?',
|
||||
'Unable to reach Desktop API, Swarm Desktop may not be running',
|
||||
)
|
||||
|
||||
if (canUpgradeToLightNode) {
|
||||
desktopConfiguration = await wrapWithSwapError(
|
||||
upgradeToLightNode(desktopUrl, rpcProviderUrl),
|
||||
'Failed to update the configuration file with the new swap values using the Desktop API',
|
||||
)
|
||||
}
|
||||
|
||||
if (!desktopConfiguration['swap-endpoint']) {
|
||||
throw new SwapError('Swap endpoint is not configured in Swarm Desktop')
|
||||
}
|
||||
@@ -150,7 +169,7 @@ export function Swap({ header }: Props): ReactElement {
|
||||
Rpc.getNetworkChainId(desktopConfiguration['swap-endpoint']),
|
||||
`Swap endpoint not reachable at ${desktopConfiguration['swap-endpoint']}`,
|
||||
)
|
||||
await wrapWithSwapError(performSwap(desktopUrl, daiToSwap.toString), GENERIC_SWAP_FAILED_ERROR_MESSAGE)
|
||||
await wrapWithSwapError(sendSwapRequest(daiToSwap), GENERIC_SWAP_FAILED_ERROR_MESSAGE)
|
||||
}
|
||||
|
||||
async function onSwap() {
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { Box, createStyles, Grid, makeStyles, Typography } from '@material-ui/core'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import { ReactElement, useContext, useState } from 'react'
|
||||
import { useNavigate } from 'react-router'
|
||||
import BankCard from 'remixicon-react/BankCard2LineIcon'
|
||||
import Check from 'remixicon-react/CheckLineIcon'
|
||||
import Download from 'remixicon-react/DownloadLineIcon'
|
||||
import BankCard from 'remixicon-react/BankCard2LineIcon'
|
||||
import MoneyDollarCircle from 'remixicon-react/MoneyDollarCircleLineIcon'
|
||||
import Gift from 'remixicon-react/GiftLineIcon'
|
||||
import { useNavigate } from 'react-router'
|
||||
import MoneyDollarCircle from 'remixicon-react/MoneyDollarCircleLineIcon'
|
||||
import ExpandableListItemActions from '../../components/ExpandableListItemActions'
|
||||
import { HistoryHeader } from '../../components/HistoryHeader'
|
||||
import { Loading } from '../../components/Loading'
|
||||
import { SwarmButton } from '../../components/SwarmButton'
|
||||
import { ROUTES } from '../../routes'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
import { CheckState, Context as BeeContext } from '../../providers/Bee'
|
||||
import { Context as SettingsContext } from '../../providers/Settings'
|
||||
import { Context as BalanceProvider } from '../../providers/WalletBalance'
|
||||
import { BeeModes } from '@ethersphere/bee-js'
|
||||
import { ROUTES } from '../../routes'
|
||||
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
|
||||
import { Loading } from '../../components/Loading'
|
||||
import { useSnackbar } from 'notistack'
|
||||
import TroubleshootConnectionCard from '../../components/TroubleshootConnectionCard'
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
@@ -57,7 +57,6 @@ export default function TopUp(): ReactElement {
|
||||
try {
|
||||
await upgradeToLightNode(desktopUrl, rpcProviderUrl)
|
||||
await restartBeeNode(desktopUrl)
|
||||
enqueueSnackbar('Upgraded to light node', { variant: 'success' })
|
||||
navigate(ROUTES.RESTART_LIGHT)
|
||||
} catch (error) {
|
||||
console.error(error) // eslint-disable-line
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
export class AuthError extends Error {
|
||||
constructor() {
|
||||
super('Bad API key')
|
||||
this.name = 'AuthError'
|
||||
}
|
||||
}
|
||||
|
||||
export function isAuthError(error: unknown): error is AuthError {
|
||||
return error instanceof Error && error.name === 'AuthError'
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isAuthError } from './AuthError'
|
||||
|
||||
export class SwapError extends Error {
|
||||
snackbarMessage: string
|
||||
originalError?: Error
|
||||
@@ -16,6 +18,9 @@ export function isSwapError(error: unknown): error is SwapError {
|
||||
|
||||
export function wrapWithSwapError<T>(promise: Promise<T>, snackbarMessage: string): Promise<T> {
|
||||
return promise.catch((error: Error) => {
|
||||
if (isAuthError(error)) {
|
||||
throw new SwapError('Bad API key, reopen dashboard through Swarm Desktop', error)
|
||||
}
|
||||
throw new SwapError(snackbarMessage, error)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ export async function getBzzPriceAsDai(desktopUrl: string): Promise<Token> {
|
||||
return DaiToken.fromDecimal(response.data)
|
||||
}
|
||||
|
||||
export async function upgradeToLightNode(desktopUrl: string, rpcProvider: string): Promise<void> {
|
||||
await updateDesktopConfiguration(desktopUrl, {
|
||||
export function upgradeToLightNode(desktopUrl: string, rpcProvider: string): Promise<BeeConfig> {
|
||||
return updateDesktopConfiguration(desktopUrl, {
|
||||
'swap-enable': true,
|
||||
'swap-endpoint': rpcProvider,
|
||||
})
|
||||
@@ -43,8 +43,8 @@ export function getDesktopConfiguration(desktopUrl: string): Promise<BeeConfig>
|
||||
return getJson(`${desktopUrl}/config`)
|
||||
}
|
||||
|
||||
async function updateDesktopConfiguration(desktopUrl: string, values: Record<string, unknown>): Promise<void> {
|
||||
await postJson(`${desktopUrl}/config`, values)
|
||||
function updateDesktopConfiguration(desktopUrl: string, values: Record<string, unknown>): Promise<BeeConfig> {
|
||||
return postJson(`${desktopUrl}/config`, values)
|
||||
}
|
||||
|
||||
export async function restartBeeNode(desktopUrl: string): Promise<void> {
|
||||
|
||||
+11
-1
@@ -1,12 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import axios from 'axios'
|
||||
import { AuthError } from './AuthError'
|
||||
|
||||
export function getJson<T extends Record<string, any>>(url: string): Promise<T> {
|
||||
return sendRequest(url, 'GET') as Promise<T>
|
||||
}
|
||||
|
||||
export function postJson<T extends Record<string, any>>(url: string, data?: T): Promise<T> {
|
||||
export function postJson<T extends Record<string, any>>(url: string, data?: Record<string, any>): Promise<T> {
|
||||
return sendRequest(url, 'POST', data) as Promise<T>
|
||||
}
|
||||
|
||||
@@ -27,6 +28,15 @@ export async function sendRequest(
|
||||
method,
|
||||
headers,
|
||||
data,
|
||||
}).catch(error => {
|
||||
if (error?.response?.status === 401) {
|
||||
throw new AuthError()
|
||||
}
|
||||
|
||||
if (error?.response?.data) {
|
||||
throw Error(`Request ${method} ${url} failed: ${JSON.stringify(error.response.data)}`)
|
||||
}
|
||||
throw error
|
||||
})
|
||||
|
||||
return response.data
|
||||
|
||||
Reference in New Issue
Block a user