Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: make blockchain JSON RPC configurable from settings #494

Merged
merged 3 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/pages/gift-code/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Loading } from '../../components/Loading'
import { SwarmButton } from '../../components/SwarmButton'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as TopUpContext } from '../../providers/TopUp'
import { Context as SettingsContext } from '../../providers/Settings'
import { createGiftWallet } from '../../utils/desktop'
import { ResolvedWallet } from '../../utils/wallet'
import { Token } from '../../models/Token'
Expand All @@ -21,7 +22,8 @@ const GIFT_WALLET_FUND_DAI_AMOUNT = Token.fromDecimal('0.1', 18)
const GIFT_WALLET_FUND_BZZ_AMOUNT = Token.fromDecimal('0.5', 16)

export default function Index(): ReactElement {
const { giftWallets, addGiftWallet, provider } = useContext(TopUpContext)
const { giftWallets, addGiftWallet } = useContext(TopUpContext)
const { provider } = useContext(SettingsContext)
const { balance } = useContext(BeeContext)

const [loading, setLoading] = useState(false)
Expand Down
85 changes: 58 additions & 27 deletions src/pages/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,27 @@ import CircularProgress from '@material-ui/core/CircularProgress'
import { ReactElement, useContext } from 'react'
import ExpandableList from '../../components/ExpandableList'
import ExpandableListItemInput from '../../components/ExpandableListItemInput'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as SettingsContext } from '../../providers/Settings'
import config from '../../config'
import { useSnackbar } from 'notistack'

export default function SettingsPage(): ReactElement {
const { apiUrl, apiDebugUrl, setApiUrl, setDebugApiUrl, lockedApiSettings, config, isLoading } =
useContext(SettingsContext)
const {
apiUrl,
apiDebugUrl,
setApiUrl,
setDebugApiUrl,
lockedApiSettings,
cors,
dataDir,
ensResolver,
providerUrl,
isLoading,
setAndPersistJsonRpcProvider,
} = useContext(SettingsContext)
const { refresh } = useContext(BeeContext)
const { enqueueSnackbar } = useSnackbar()

if (isLoading) {
return (
Expand All @@ -16,31 +32,46 @@ export default function SettingsPage(): ReactElement {
)
}

// Run within Bee Desktop, display read only config
if (config) {
return (
<ExpandableList label="Bee Desktop Settings" defaultOpen>
<ExpandableListItemInput label="Bee API" value={config['api-addr']} locked />
<ExpandableListItemInput label="Bee Debug API" value={config['debug-api-addr']} locked />
<ExpandableListItemInput label="CORS" value={config['cors-allowed-origins']} locked />
<ExpandableListItemInput label="Data DIR" value={config['data-dir']} locked />
<ExpandableListItemInput label="ENS resolver URL" value={config['resolver-options']} locked />
{config['swap-endpoint'] && (
<ExpandableListItemInput label="SWAP endpoint" value={config['swap-endpoint']} locked />
)}
</ExpandableList>
)
}

return (
<ExpandableList label="API Settings" defaultOpen>
<ExpandableListItemInput label="Bee API" value={apiUrl} onConfirm={setApiUrl} locked={lockedApiSettings} />
<ExpandableListItemInput
label="Bee Debug API"
value={apiDebugUrl}
onConfirm={setDebugApiUrl}
locked={lockedApiSettings}
/>
</ExpandableList>
<>
<ExpandableList label="API Settings" defaultOpen>
<ExpandableListItemInput
label="Bee API"
value={apiUrl}
onConfirm={setApiUrl}
locked={lockedApiSettings || config.BEE_DESKTOP_ENABLED}
/>
<ExpandableListItemInput
label="Bee Debug API"
value={apiDebugUrl}
onConfirm={setDebugApiUrl}
locked={lockedApiSettings || config.BEE_DESKTOP_ENABLED}
/>
<ExpandableListItemInput
label="Blockchain RPC URL"
value={providerUrl}
helperText="Changing the value will restart your bee node."
confirmLabel="Save and restart"
onConfirm={value => {
setAndPersistJsonRpcProvider(value)
.then(() => {
refresh()
enqueueSnackbar('Settings changed, restarting bee node...', { variant: 'success' })
})
.catch(error => {
console.log(error) //eslint-disable-line
enqueueSnackbar(`Failed to change RPC endpoint. Error: ${error}`, { variant: 'success' })
})
}}
/>
</ExpandableList>
{config.BEE_DESKTOP_ENABLED && (
<ExpandableList label="Desktop Settings" defaultOpen>
<ExpandableListItemInput label="CORS" value={cors ?? '-'} locked />
<ExpandableListItemInput label="Data DIR" value={dataDir ?? '-'} locked />
<ExpandableListItemInput label="ENS resolver URL" value={ensResolver ?? '-'} locked />
</ExpandableList>
)}
</>
)
}
4 changes: 2 additions & 2 deletions src/pages/top-up/GiftCardFund.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { ProgressIndicator } from '../../components/ProgressIndicator'
import { SwarmButton } from '../../components/SwarmButton'
import { SwarmDivider } from '../../components/SwarmDivider'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as TopUpContext } from '../../providers/TopUp'
import { Context as SettingsContext } from '../../providers/Settings'
import { ROUTES } from '../../routes'
import { sleepMs } from '../../utils'
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
Expand All @@ -23,7 +23,7 @@ import { BeeModes } from '@ethersphere/bee-js'
export function GiftCardFund(): ReactElement {
const isBeeDesktop = config.BEE_DESKTOP_ENABLED
const { nodeAddresses, balance, nodeInfo } = useContext(BeeContext)
const { provider, providerUrl } = useContext(TopUpContext)
const { provider, providerUrl } = useContext(SettingsContext)

const [loading, setLoading] = useState(false)
const [wallet, setWallet] = useState<ResolvedWallet | null>(null)
Expand Down
4 changes: 2 additions & 2 deletions src/pages/top-up/GiftCardTopUpIndex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useSnackbar } from 'notistack'
import { ReactElement, useContext, useState } from 'react'
import ArrowRight from 'remixicon-react/ArrowRightLineIcon'
import { useNavigate } from 'react-router'
import { Context as TopUpContext } from '../../providers/TopUp'
import { Context as SettingsContext } from '../../providers/Settings'
import { HistoryHeader } from '../../components/HistoryHeader'
import { ProgressIndicator } from '../../components/ProgressIndicator'
import { SwarmButton } from '../../components/SwarmButton'
Expand All @@ -16,7 +16,7 @@ import { ROUTES } from '../../routes'
import { Rpc } from '../../utils/rpc'

export function GiftCardTopUpIndex(): ReactElement {
const { provider } = useContext(TopUpContext)
const { provider } = useContext(SettingsContext)
const [loading, setLoading] = useState(false)
const [giftCode, setGiftCode] = useState('')

Expand Down
4 changes: 2 additions & 2 deletions src/pages/top-up/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { SwarmTextInput } from '../../components/SwarmTextInput'
import { BzzToken } from '../../models/BzzToken'
import { DaiToken } from '../../models/DaiToken'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as TopUpContext } from '../../providers/TopUp'
import { Context as SettingsContext } from '../../providers/Settings'
import { ROUTES } from '../../routes'
import { sleepMs } from '../../utils'
import { getBzzPriceAsDai, performSwap, restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
Expand All @@ -37,7 +37,7 @@ export function Swap({ header }: Props): ReactElement {
const [userInputSwap, setUserInputSwap] = useState<string | null>(null)
const [price, setPrice] = useState(DaiToken.fromDecimal('0.6', 18))

const { providerUrl } = useContext(TopUpContext)
const { providerUrl } = useContext(SettingsContext)
const { balance, nodeAddresses, nodeInfo } = useContext(BeeContext)
const isBeeDesktop = config.BEE_DESKTOP_ENABLED

Expand Down
4 changes: 2 additions & 2 deletions src/pages/top-up/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { HistoryHeader } from '../../components/HistoryHeader'
import { SwarmButton } from '../../components/SwarmButton'
import { ROUTES } from '../../routes'
import { Context as BeeContext } from '../../providers/Bee'
import { Context as TopUpContext } from '../../providers/TopUp'
import { Context as SettingsContext } from '../../providers/Settings'
import config from '../../config'
import { BeeModes } from '@ethersphere/bee-js'
import { restartBeeNode, upgradeToLightNode } from '../../utils/desktop'
Expand Down Expand Up @@ -40,7 +40,7 @@ export default function TopUp(): ReactElement {
const styles = useStyles()
const isBeeDesktop = config.BEE_DESKTOP_ENABLED
const { balance, nodeInfo } = useContext(BeeContext)
const { providerUrl } = useContext(TopUpContext)
const { providerUrl } = useContext(SettingsContext)
const [loading, setLoading] = useState(false)
const { enqueueSnackbar } = useSnackbar()

Expand Down
4 changes: 1 addition & 3 deletions src/providers/Bee.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { Token } from '../models/Token'
import type { Balance, ChequebookBalance, Settlements } from '../types'
import { WalletAddress } from '../utils/wallet'
import { Context as SettingsContext } from './Settings'
import { Context as TopUpContext } from './TopUp'

const REFRESH_WHEN_OK = 30_000
const REFRESH_WHEN_ERROR = 5_000
Expand Down Expand Up @@ -189,8 +188,7 @@ function getStatus(
let isRefreshing = false

export function Provider({ children }: Props): ReactElement {
const { beeApi, beeDebugApi } = useContext(SettingsContext)
const { provider } = useContext(TopUpContext)
const { beeApi, beeDebugApi, provider } = useContext(SettingsContext)
const [apiHealth, setApiHealth] = useState<boolean>(false)
const [debugApiHealth, setDebugApiHealth] = useState<Health | null>(null)
const [nodeAddresses, setNodeAddresses] = useState<NodeAddresses | null>(null)
Expand Down
61 changes: 50 additions & 11 deletions src/providers/Settings.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,50 @@
import { Bee, BeeDebug } from '@ethersphere/bee-js'
import { providers } from 'ethers'
import { createContext, ReactChild, ReactElement, useEffect, useState } from 'react'
import { config } from '../config'
import { BeeConfig, useGetBeeConfig } from '../hooks/apiHooks'
import { config as appConfig } from '../config'
import { useGetBeeConfig } from '../hooks/apiHooks'
import { restartBeeNode, setJsonRpcInDesktop } from '../utils/desktop'

const LocalStorageKeys = {
providerUrl: 'json-rpc-provider',
}

const providerUrl = localStorage.getItem('json-rpc-provider') || appConfig.DEFAULT_RPC_URL

interface ContextInterface {
apiUrl: string
apiDebugUrl: string
beeApi: Bee | null
beeDebugApi: BeeDebug | null
setApiUrl: (url: string) => void
setDebugApiUrl: (url: string) => void
lockedApiSettings: boolean
desktopApiKey: string
config: BeeConfig | null
providerUrl: string
provider: providers.JsonRpcProvider
cors: string | null
dataDir: string | null
ensResolver: string | null
setApiUrl: (url: string) => void
setDebugApiUrl: (url: string) => void
setAndPersistJsonRpcProvider: (url: string) => Promise<void>
isLoading: boolean
error: Error | null
}

const initialValues: ContextInterface = {
apiUrl: config.BEE_API_HOST,
apiDebugUrl: config.BEE_DEBUG_API_HOST,
apiUrl: appConfig.BEE_API_HOST,
apiDebugUrl: appConfig.BEE_DEBUG_API_HOST,
beeApi: null,
beeDebugApi: null,
setApiUrl: () => {}, // eslint-disable-line
setDebugApiUrl: () => {}, // eslint-disable-line
lockedApiSettings: false,
desktopApiKey: '',
config: null,
setAndPersistJsonRpcProvider: async () => {}, // eslint-disable-line
providerUrl,
provider: new providers.JsonRpcProvider(providerUrl),
cors: null,
dataDir: null,
ensResolver: null,
isLoading: true,
error: null,
}
Expand All @@ -51,10 +69,26 @@ export function Provider({
const [apiDebugUrl, setDebugApiUrl] = useState<string>(initialValues.apiDebugUrl)
const [beeApi, setBeeApi] = useState<Bee | null>(null)
const [beeDebugApi, setBeeDebugApi] = useState<BeeDebug | null>(null)
const [lockedApiSettings] = useState<boolean>(Boolean(extLockedApiSettings))
const [desktopApiKey, setDesktopApiKey] = useState<string>(initialValues.desktopApiKey)
const [providerUrl, setProviderUrl] = useState(initialValues.providerUrl)
const [provider, setProvider] = useState(initialValues.provider)
const { config, isLoading, error } = useGetBeeConfig()

async function setAndPersistJsonRpcProvider(providerUrl: string) {
try {
localStorage.setItem(LocalStorageKeys.providerUrl, providerUrl)
setProviderUrl(providerUrl)
setProvider(new providers.JsonRpcProvider(providerUrl))

if (appConfig.BEE_DESKTOP_ENABLED) {
await setJsonRpcInDesktop(providerUrl)
await restartBeeNode()
}
} catch (error) {
console.error(error) // eslint-disable-line
}
}

function makeHttpUrl(string: string): string {
if (!string.startsWith('http')) {
return `http://${string}`
Expand Down Expand Up @@ -104,9 +138,14 @@ export function Provider({
beeDebugApi,
setApiUrl,
setDebugApiUrl,
lockedApiSettings,
lockedApiSettings: Boolean(extLockedApiSettings),
desktopApiKey,
config,
provider,
providerUrl,
cors: config?.['cors-allowed-origins'] ?? null,
dataDir: config?.['data-dir'] ?? null,
ensResolver: config?.['resolver-options'] ?? null,
setAndPersistJsonRpcProvider,
isLoading,
error,
}}
Expand Down
30 changes: 4 additions & 26 deletions src/providers/TopUp.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
import { providers, Wallet } from 'ethers'
import { createContext, ReactElement, useEffect, useState } from 'react'
import config from '../config'
import { setJsonRpcInDesktop } from '../utils/desktop'
import { Wallet } from 'ethers'
import { createContext, ReactElement, useContext, useEffect, useState } from 'react'
import { Context as SettingsContext } from './Settings'

const LocalStorageKeys = {
providerUrl: 'json-rpc-provider',
depositWallet: 'deposit-wallet',
giftWallets: 'gift-wallets',
invitation: 'invitation',
}

interface ContextInterface {
providerUrl: string
provider: providers.JsonRpcProvider
giftWallets: Wallet[]
setProviderUrl: (providerUrl: string) => void
addGiftWallet: (wallet: Wallet) => void
}

const providerUrl = localStorage.getItem('json-rpc-provider') || config.DEFAULT_RPC_URL

const initialValues: ContextInterface = {
providerUrl,
provider: new providers.JsonRpcProvider(providerUrl),
giftWallets: [],
setProviderUrl: () => {}, // eslint-disable-line
addGiftWallet: () => {}, // eslint-disable-line
}

Expand All @@ -36,9 +26,8 @@ interface Props {
}

export function Provider({ children }: Props): ReactElement {
const [providerUrl, setProviderUrl] = useState(initialValues.providerUrl)
const [provider, setProvider] = useState(initialValues.provider)
const [giftWallets, setGiftWallets] = useState(initialValues.giftWallets)
const { provider } = useContext(SettingsContext)

useEffect(() => {
if (provider === null) return
Expand All @@ -50,14 +39,6 @@ export function Provider({ children }: Props): ReactElement {
}
}, [provider])

function setAndPersistJsonRpcProvider(providerUrl: string) {
localStorage.setItem(LocalStorageKeys.providerUrl, providerUrl)
setProviderUrl(providerUrl)
setProvider(new providers.JsonRpcProvider(providerUrl))
// eslint-disable-next-line no-console
setJsonRpcInDesktop(providerUrl).catch(console.error)
}

function addGiftWallet(wallet: Wallet) {
const newArray = [...giftWallets, wallet]
localStorage.setItem(LocalStorageKeys.giftWallets, JSON.stringify(newArray.map(x => x.privateKey)))
Expand All @@ -67,10 +48,7 @@ export function Provider({ children }: Props): ReactElement {
return (
<Context.Provider
value={{
providerUrl,
provider,
giftWallets,
setProviderUrl: setAndPersistJsonRpcProvider,
addGiftWallet,
}}
>
Expand Down
4 changes: 2 additions & 2 deletions src/utils/net.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export function getJson<T extends Record<string, any>>(url: string): Promise<T>
return sendRequest(url, 'GET') as Promise<T>
}

export function postJson(url: string, data?: Record<string, any>): Promise<Record<string, unknown>> {
return sendRequest(url, 'POST', data)
export function postJson<T extends Record<string, any>>(url: string, data?: T): Promise<T> {
return sendRequest(url, 'POST', data) as Promise<T>
}

export async function sendRequest(
Expand Down