diff --git a/src/renderer/components/settings/AppSettings.stories.tsx b/src/renderer/components/settings/AppSettings.stories.tsx
index 87eb4972c..5ac618e69 100644
--- a/src/renderer/components/settings/AppSettings.stories.tsx
+++ b/src/renderer/components/settings/AppSettings.stories.tsx
@@ -16,6 +16,8 @@ type StoryArgs = {
goToReleasePage: (version: string) => void
changeLocale: (locale: Locale) => void
onChangeMidgardUrl: (url: string) => void
+ onChangeThornodeNodeUrl: (url: string) => void
+ onChangeThornodeRpcUrl: (url: string) => void
changeNetwork: ChangeNetworkHandler
collapsed: boolean
}
@@ -26,6 +28,8 @@ const Template = ({
checkForUpdates,
goToReleasePage,
onChangeMidgardUrl,
+ onChangeThornodeRpcUrl,
+ onChangeThornodeNodeUrl,
changeLocale,
collapsed
}: StoryArgs) => {
@@ -47,8 +51,14 @@ const Template = ({
collapsed={collapsed}
toggleCollapse={() => console.log('toggle')}
midgardUrl={RD.pending}
+ thornodeNodeUrl="thornode-node-url"
+ thornodeRpcUrl="thornode-rpc-url"
onChangeMidgardUrl={onChangeMidgardUrl}
+ onChangeThornodeRpcUrl={onChangeThornodeRpcUrl}
+ onChangeThornodeNodeUrl={onChangeThornodeNodeUrl}
checkMidgardUrl$={(url, _) => Rx.of(RD.success(url))}
+ checkThornodeNodeUrl$={(url, _) => Rx.of(RD.success(url))}
+ checkThornodeRpcUrl$={(url, _) => Rx.of(RD.success(url))}
/>
)
}
@@ -76,6 +86,12 @@ const meta: ComponentMeta = {
},
onChangeMidgardUrl: {
action: 'onChangeMidgardUrl'
+ },
+ onChangeThornodeNodeUrl: {
+ action: 'onChangeThornodeNodeUrl'
+ },
+ onChangeThornodeRpcUrl: {
+ action: 'onChangeThornodeRpcUrl'
}
},
args: { onlineStatus: OnlineStatus.ON, updateDataRD: 'initial', collapsed: false }
diff --git a/src/renderer/components/settings/AppSettings.tsx b/src/renderer/components/settings/AppSettings.tsx
index 787965e29..8fb0b3318 100644
--- a/src/renderer/components/settings/AppSettings.tsx
+++ b/src/renderer/components/settings/AppSettings.tsx
@@ -12,6 +12,7 @@ import { Locale } from '../../../shared/i18n/types'
import { LOCALES } from '../../i18n'
import { AVAILABLE_NETWORKS } from '../../services/const'
import { CheckMidgardUrlHandler, MidgardUrlRD } from '../../services/midgard/types'
+import { CheckThornodeNodeUrlHandler, CheckThornodeRpcUrlHandler } from '../../services/thorchain/types'
import { DownIcon } from '../icons'
import { Menu } from '../shared/menu'
import { BorderButton, TextButton } from '../uielements/button'
@@ -34,6 +35,12 @@ export type Props = {
midgardUrl: MidgardUrlRD
onChangeMidgardUrl: (url: string) => void
checkMidgardUrl$: CheckMidgardUrlHandler
+ checkThornodeNodeUrl$: CheckThornodeNodeUrlHandler
+ onChangeThornodeNodeUrl: (url: string) => void
+ checkThornodeRpcUrl$: CheckThornodeRpcUrlHandler
+ thornodeRpcUrl: string
+ thornodeNodeUrl: string
+ onChangeThornodeRpcUrl: (url: string) => void
}
type SectionProps = {
@@ -63,7 +70,13 @@ export const AppSettings: React.FC = (props): JSX.Element => {
toggleCollapse,
midgardUrl: midgardUrlRD,
onChangeMidgardUrl,
- checkMidgardUrl$
+ checkMidgardUrl$,
+ onChangeThornodeNodeUrl,
+ checkThornodeNodeUrl$,
+ checkThornodeRpcUrl$,
+ onChangeThornodeRpcUrl,
+ thornodeRpcUrl,
+ thornodeNodeUrl
} = props
const intl = useIntl()
@@ -244,15 +257,36 @@ export const AppSettings: React.FC = (props): JSX.Element => {
{advancedActive && (
-
+ <>
+
+
+
+ >
)}
diff --git a/src/renderer/components/settings/EditableUrl.tsx b/src/renderer/components/settings/EditableUrl.tsx
index 42bf3be3e..daf9e1ccf 100644
--- a/src/renderer/components/settings/EditableUrl.tsx
+++ b/src/renderer/components/settings/EditableUrl.tsx
@@ -23,13 +23,14 @@ type TestUrlHandler = (url: string, intl: IntlShape) => TestUrlLD
type Props = {
url: string
checkUrl$: TestUrlHandler
+ successMsg: string
loading?: boolean
onChange?: (url: string) => void
className?: string
}
const EditableUrl: React.FC = (props): JSX.Element => {
- const { url: initialUrl, className, loading = false, onChange = FP.constVoid, checkUrl$ } = props
+ const { url: initialUrl, className, successMsg, loading = false, onChange = FP.constVoid, checkUrl$ } = props
const [editableUrl, setEditableUrl] = useState>(O.none)
const [url, setUrl] = useState(initialUrl)
@@ -86,14 +87,10 @@ const EditableUrl: React.FC = (props): JSX.Element => {
{error?.message ?? error.toString()}
),
- (_) => (
-
- {intl.formatMessage({ id: 'midgard.url.valid' })}
-
- )
+ (_) => {successMsg}
)
),
- [intl, testUrlState]
+ [successMsg, testUrlState]
)
const renderUrl = useCallback(() => {
diff --git a/src/renderer/hooks/useThorchainClientUrl.ts b/src/renderer/hooks/useThorchainClientUrl.ts
index 73b7c10db..4dc7b0fdc 100644
--- a/src/renderer/hooks/useThorchainClientUrl.ts
+++ b/src/renderer/hooks/useThorchainClientUrl.ts
@@ -1,19 +1,24 @@
import { useEffect } from 'react'
+import * as RD from '@devexperts/remote-data-ts'
import { NodeUrl } from '@xchainjs/xchain-thorchain'
import * as FP from 'fp-ts/lib/function'
import { useObservableState } from 'observable-hooks'
+import { useIntl } from 'react-intl'
import * as Rx from 'rxjs'
+import * as RxAjax from 'rxjs/ajax'
import * as RxOp from 'rxjs/operators'
import { Network } from '../../shared/api/types'
import { useThorchainContext } from '../contexts/ThorchainContext'
import { INITIAL_CLIENT_URL } from '../services/thorchain/const'
+import { Configuration, HealthApi } from '../types/generated/thornode'
import { useNetwork } from './useNetwork'
export const useThorchainClientUrl = () => {
const { clientUrl$, setClientUrl } = useThorchainContext()
const { network } = useNetwork()
+ const intl = useIntl()
const [nodeUrl, networkUpdated] = useObservableState(
(network$) =>
@@ -32,5 +37,42 @@ export const useThorchainClientUrl = () => {
const setRpc = (url: string) => setClientUrl({ url, network, type: 'rpc' })
const setNode = (url: string) => setClientUrl({ url, network, type: 'node' })
- return { rpc: nodeUrl.rpc, node: nodeUrl.node, setRpc, setNode }
+ const checkNode$ = (url: string) =>
+ FP.pipe(
+ // Check `ping` endpoint
+ new HealthApi(new Configuration({ basePath: url })).ping(),
+ RxOp.map((result) => {
+ const { ping } = result
+ if (ping) return RD.success(url)
+
+ return RD.failure(
+ Error(intl.formatMessage({ id: 'setting.thornode.node.error.unhealthy' }, { endpoint: '/ping' }))
+ )
+ }),
+ RxOp.catchError((_: Error) =>
+ Rx.of(RD.failure(Error(`${intl.formatMessage({ id: 'setting.thornode.node.error.url' })}`)))
+ )
+ )
+
+ const checkRpc$ = (url: string) =>
+ FP.pipe(
+ // Check `health` endpoint
+ // https://docs.tendermint.com/v0.34/rpc/#/Info/health
+ RxAjax.ajax(`${url}/health`),
+ RxOp.map(({ response }) => {
+ // Empty result object means no error
+ if (response.result && typeof response.result === 'object' && Object.keys(response.result).length === 0)
+ return RD.success(url)
+
+ return RD.failure(
+ Error(intl.formatMessage({ id: 'setting.thornode.rpc.error.unhealthy' }, { endpoint: '/ping' }))
+ )
+ }),
+
+ RxOp.catchError((_: Error) =>
+ Rx.of(RD.failure(Error(`${intl.formatMessage({ id: 'thornode.url.error.invalid' })}`)))
+ )
+ )
+
+ return { rpc: nodeUrl.rpc, node: nodeUrl.node, setRpc, setNode, checkNode$, checkRpc$ }
}
diff --git a/src/renderer/i18n/de/settings.ts b/src/renderer/i18n/de/settings.ts
index 245be6f38..55c072942 100644
--- a/src/renderer/i18n/de/settings.ts
+++ b/src/renderer/i18n/de/settings.ts
@@ -18,7 +18,14 @@ const settings: SettingMessages = {
'setting.wallet.index': 'Index',
'setting.wallet.index.info': 'Trage die Index Nummer der Ledger Addresse ein, die Du verwenden möchtest',
'setting.wallet.hdpath.legacy.info': 'Veralteter Derivation Pfad {path}',
- 'setting.wallet.hdpath.ledgerlive.info': 'Ledger Live Derivation Pfad {path}'
+ 'setting.wallet.hdpath.ledgerlive.info': 'Ledger Live Derivation Pfad {path}',
+ 'setting.thornode.node.error.unhealthy':
+ 'THORNode API URL scheint "unhealthy" zu sein beim Überprüfen von "{endpoint}"',
+ 'setting.thornode.node.error.url': 'Ungültige THORNode API URL. Bitte überprüfe diese und versuche es erneut.',
+ 'setting.thornode.rpc.error.url': 'Ungültige THORNode RPC URL. Bitte überprüfe diese und versuche es erneut.',
+ 'setting.thornode.rpc.error.unhealthy': 'THORNode RPC scheint "unhealthy" zu sein beim Überprüfen von "{endpoint"',
+ 'setting.thornode.node.valid': 'Gültige THORNode API URL',
+ 'setting.thornode.rpc.valid': 'Gültige THORNode RPC URL'
}
export default settings
diff --git a/src/renderer/i18n/en/settings.ts b/src/renderer/i18n/en/settings.ts
index 9dd32314e..fe86a757e 100644
--- a/src/renderer/i18n/en/settings.ts
+++ b/src/renderer/i18n/en/settings.ts
@@ -18,7 +18,13 @@ const settings: SettingMessages = {
'setting.wallet.index': 'Index',
'setting.wallet.index.info': 'Enter the index number of the Ledger address you want to use',
'setting.wallet.hdpath.legacy.info': 'Legacy derivation path {path}',
- 'setting.wallet.hdpath.ledgerlive.info': 'Ledger Live derivation path {path}'
+ 'setting.wallet.hdpath.ledgerlive.info': 'Ledger Live derivation path {path}',
+ 'setting.thornode.node.error.unhealthy': 'THORNode API seems to be unhealthy by checking "{endpoint}"',
+ 'setting.thornode.node.error.url': 'Invalid THORNode API URL. Please double check and try again',
+ 'setting.thornode.rpc.error.url': 'Invalid THORNode RPC URL. Please double check and try again',
+ 'setting.thornode.rpc.error.unhealthy': 'THORNode RPC seems to be unhealthy by checking "{endpoint}"',
+ 'setting.thornode.node.valid': 'Valid THORNode API URL',
+ 'setting.thornode.rpc.valid': 'Valid THORNode RPC URL'
}
export default settings
diff --git a/src/renderer/i18n/fr/settings.ts b/src/renderer/i18n/fr/settings.ts
index 0ab16e222..739318e9d 100644
--- a/src/renderer/i18n/fr/settings.ts
+++ b/src/renderer/i18n/fr/settings.ts
@@ -18,7 +18,13 @@ const settings: SettingMessages = {
'setting.wallet.index': 'Index',
'setting.wallet.index.info': "Entrez le numéro d'index de l'adresse Ledger que vous souhaitez utiliser",
'setting.wallet.hdpath.legacy.info': 'Legacy derivation path {path} - FR',
- 'setting.wallet.hdpath.ledgerlive.info': 'Ledger Live derivation path {path} - FR'
+ 'setting.wallet.hdpath.ledgerlive.info': 'Ledger Live derivation path {path} - FR',
+ 'setting.thornode.node.error.unhealthy': 'THORNode API seems to be unhealthy by checking "{endpoint} - FR"',
+ 'setting.thornode.node.error.url': 'Invalid THORNode API URL. Please double check and try again - FR',
+ 'setting.thornode.rpc.error.url': 'Invalid THORNode RPC URL. Please double check and try again - FR',
+ 'setting.thornode.rpc.error.unhealthy': 'THORNode RPC seems to be unhealthy by checking "{endpoint}" - FR',
+ 'setting.thornode.node.valid': 'Valid THORNode API URL - FR',
+ 'setting.thornode.rpc.valid': 'Valid THORNode RPC URL - FR'
}
export default settings
diff --git a/src/renderer/i18n/ru/settings.ts b/src/renderer/i18n/ru/settings.ts
index e3956febb..7417f775f 100644
--- a/src/renderer/i18n/ru/settings.ts
+++ b/src/renderer/i18n/ru/settings.ts
@@ -18,7 +18,13 @@ const settings: SettingMessages = {
'setting.wallet.index': 'Индекс',
'setting.wallet.index.info': 'Введите индекс Ledger адреса, который вы хотите использовать',
'setting.wallet.hdpath.legacy.info': 'Устаревший путь деривации {path}',
- 'setting.wallet.hdpath.ledgerlive.info': 'Путь деривации Ledger Live {path}'
+ 'setting.wallet.hdpath.ledgerlive.info': 'Путь деривации Ledger Live {path}',
+ 'setting.thornode.node.error.unhealthy': 'THORNode API seems to be unhealthy by checking "{endpoint} - RU"',
+ 'setting.thornode.node.error.url': 'Invalid THORNode API URL. Please double check and try again - RU',
+ 'setting.thornode.rpc.error.url': 'Invalid THORNode RPC URL. Please double check and try again - RU',
+ 'setting.thornode.rpc.error.unhealthy': 'THORNode RPC seems to be unhealthy by checking "{endpoint}" - RU',
+ 'setting.thornode.node.valid': 'Valid THORNode API URL - RU',
+ 'setting.thornode.rpc.valid': 'Valid THORNode RPC URL - RU'
}
export default settings
diff --git a/src/renderer/i18n/types.ts b/src/renderer/i18n/types.ts
index c37ab2ccf..de47aa064 100644
--- a/src/renderer/i18n/types.ts
+++ b/src/renderer/i18n/types.ts
@@ -328,6 +328,12 @@ type SettingMessageKey =
| 'setting.wallet.index.info'
| 'setting.wallet.hdpath.legacy.info'
| 'setting.wallet.hdpath.ledgerlive.info'
+ | 'setting.thornode.node.error.url'
+ | 'setting.thornode.node.error.unhealthy'
+ | 'setting.thornode.rpc.error.url'
+ | 'setting.thornode.rpc.error.unhealthy'
+ | 'setting.thornode.node.valid'
+ | 'setting.thornode.rpc.valid'
export type SettingMessages = { [key in SettingMessageKey]: string }
diff --git a/src/renderer/services/thorchain/common.ts b/src/renderer/services/thorchain/common.ts
index 52a7bb533..10b533dbd 100644
--- a/src/renderer/services/thorchain/common.ts
+++ b/src/renderer/services/thorchain/common.ts
@@ -23,7 +23,7 @@ const setClientUrl = ({ url, network, type }: { url: string; network: Network; t
// TODO(@veado) Store data persistent on disc
const current = getClientUrl()
const cNetwork = toClientNetwork(network)
- _setClientUrl({ ...current, [cNetwork]: { ...[cNetwork], [type]: url } })
+ _setClientUrl({ ...current, [cNetwork]: { ...current[cNetwork], [type]: url } })
}
/**
diff --git a/src/renderer/services/thorchain/types.ts b/src/renderer/services/thorchain/types.ts
index d5859fffb..458084b98 100644
--- a/src/renderer/services/thorchain/types.ts
+++ b/src/renderer/services/thorchain/types.ts
@@ -5,6 +5,7 @@ import { Asset, BaseAmount } from '@xchainjs/xchain-util'
import * as O from 'fp-ts/Option'
import * as t from 'io-ts'
import { optionFromNullable } from 'io-ts-types/lib/optionFromNullable'
+import { IntlShape } from 'react-intl'
import * as Rx from 'rxjs'
import { assetIO } from '../../../shared/api/io'
@@ -29,9 +30,16 @@ export type NodeUrl$ = Rx.Observable
export type NodeUrlLD = LiveData
export type NodeUrlRD = RD.RemoteData
+export type CheckThornodeNodeRpcHandler = (url: string, intl: IntlShape) => LiveData
+
type UrlRD = RD.RemoteData
+type CheckUrlHandler = (url: string, intl: IntlShape) => LiveData
+
export type ThornodeNodeUrlRD = UrlRD
+export type CheckThornodeNodeUrlHandler = CheckUrlHandler
+
export type ThornodeRpcUrlRD = UrlRD
+export type CheckThornodeRpcUrlHandler = CheckUrlHandler
export type FeesService = C.FeesService
diff --git a/src/renderer/views/app/AppSettingsView.tsx b/src/renderer/views/app/AppSettingsView.tsx
index 62f883438..b199ae303 100644
--- a/src/renderer/views/app/AppSettingsView.tsx
+++ b/src/renderer/views/app/AppSettingsView.tsx
@@ -12,6 +12,7 @@ import { useMidgardContext } from '../../contexts/MidgardContext'
import { useAppUpdate } from '../../hooks/useAppUpdate'
import { useCollapsedSetting } from '../../hooks/useCollapsedSetting'
import { useNetwork } from '../../hooks/useNetwork'
+import { useThorchainClientUrl } from '../../hooks/useThorchainClientUrl'
export const AppSettingsView: React.FC = (): JSX.Element => {
const { network, changeNetwork } = useNetwork()
@@ -23,6 +24,15 @@ export const AppSettingsView: React.FC = (): JSX.Element => {
const { collapsed, toggle: toggleCollapse } = useCollapsedSetting('app')
+ const {
+ node: thornodeNodeUrl,
+ rpc: thornodeRpcUrl,
+ setRpc: setThornodeRpcUrl,
+ setNode: setThornodeNodeUrl,
+ checkRpc$: checkThornodeRpcUrl$,
+ checkNode$: checkThornodeNodeUrl$
+ } = useThorchainClientUrl()
+
const { changeLocale, locale$ } = useI18nContext()
const currentLocale = useObservableState(locale$, DEFAULT_LOCALE)
@@ -52,7 +62,13 @@ export const AppSettingsView: React.FC = (): JSX.Element => {
toggleCollapse={toggleCollapse}
midgardUrl={midgardUrl}
onChangeMidgardUrl={updateMidgardUrlHandler}
+ onChangeThornodeNodeUrl={setThornodeNodeUrl}
+ onChangeThornodeRpcUrl={setThornodeRpcUrl}
checkMidgardUrl$={checkMidgardUrl$}
+ thornodeRpcUrl={thornodeRpcUrl}
+ thornodeNodeUrl={thornodeNodeUrl}
+ checkThornodeRpcUrl$={checkThornodeRpcUrl$}
+ checkThornodeNodeUrl$={checkThornodeNodeUrl$}
/>
)
}