diff --git a/packages/admin-ui/src/__mock-backend__/index.ts b/packages/admin-ui/src/__mock-backend__/index.ts index 4791705d5..f134c29bb 100644 --- a/packages/admin-ui/src/__mock-backend__/index.ts +++ b/packages/admin-ui/src/__mock-backend__/index.ts @@ -224,7 +224,39 @@ export const otherCalls: Omit = { runHostSecurityUpdates: async () => "Security updates have been executed successfully, no reboot needed", natRenewalEnable: async () => {}, - natRenewalIsEnabled: async () => true + natRenewalIsEnabled: async () => true, + ethClientsGet: async () => [ + { + ok: true, + url: "http://geth.dappnode:8545", + dnpName: "geth.dnp.dappnode.eth", + chainId: "0x1" + }, + { + ok: true, + url: "http://goerli-geth.dappnode:8545", + dnpName: "goerli-geth.dnp.dappnode.eth", + chainId: "0x5" + }, + { + ok: true, + url: "http://nethermind-xdai.dappnode:8545", + dnpName: "nethermind-xdai.dnp.dappnode.eth", + chainId: "0x64" + }, + { + ok: true, + url: "http://ropsten.dappnode:8545", + dnpName: "ropsten.dnp.dappnode.eth", + chainId: "0x3" + }, + { + ok: true, + url: "http://rinkeby.dappnode:8545", + dnpName: "rinkeby.dnp.dappnode.eth", + chainId: "0x4" + } + ] }; export const calls: Routes = { diff --git a/packages/admin-ui/src/common/routes.ts b/packages/admin-ui/src/common/routes.ts index 75c8b6a67..4b3c454c2 100644 --- a/packages/admin-ui/src/common/routes.ts +++ b/packages/admin-ui/src/common/routes.ts @@ -38,7 +38,7 @@ import { WifiReport, CurrentWifiCredentials, LocalProxyingStatus, - EthClient + EthClientWallet } from "./types"; export interface Routes { @@ -213,7 +213,7 @@ export interface Routes { /** * Return array of available clients to connect a wallet (i.e metmask) */ - ethClientsGet: () => Promise; + ethClientsGet: () => Promise; /** * Return formated core update data diff --git a/packages/admin-ui/src/common/types.ts b/packages/admin-ui/src/common/types.ts index 29984ca76..fa13b6ace 100644 --- a/packages/admin-ui/src/common/types.ts +++ b/packages/admin-ui/src/common/types.ts @@ -1189,13 +1189,6 @@ export interface VolumeData extends VolumeOwnershipData { fileSystem?: MountpointData; } -export type EthClients = "geth" | "erigon" | "nethermind" | "turbogeth"; -export interface EthClient { - ethclient: Partial; - status: EthClientStatus; - rpcEndpoint: string; -} - /** * Eth provider / client types * Manage the Ethereum multi-client setup @@ -1221,10 +1214,14 @@ export type EthClientFallback = "on" | "off"; export type EthClientStatus = EthClientStatusOk | EthClientStatusError; +export type EthClientWallet = EthClientWalletOk | EthClientStatusError; + export type EthClientStatusOk = // All okay, client is functional { ok: true; url: string; dnpName: string }; +export type EthClientWalletOk = EthClientStatusOk & { chainId: string }; + export type EthClientStatusError = // Unexpected error | { ok: false; code: "UNKNOWN_ERROR"; error: ErrorSerialized } diff --git a/packages/admin-ui/src/pages/dashboard/components/ConnectWallet.tsx b/packages/admin-ui/src/pages/dashboard/components/ConnectWallet.tsx index a5203d086..4f549f3de 100644 --- a/packages/admin-ui/src/pages/dashboard/components/ConnectWallet.tsx +++ b/packages/admin-ui/src/pages/dashboard/components/ConnectWallet.tsx @@ -1,11 +1,12 @@ import React from "react"; -import { ethers } from "ethers"; import CardList from "components/CardList"; import { useApi } from "api"; import ErrorView from "components/ErrorView"; import Ok from "components/Ok"; import Alert from "react-bootstrap/esm/Alert"; - +import { EthClientWalletOk } from "common"; +import Button from "components/Button"; +import { prettyDnpName } from "utils/format"; declare global { interface Window { ethereum: any; @@ -15,9 +16,35 @@ declare global { export default function ConnectWallet() { const ethClients = useApi.ethClientsGet(); - async function connectWallet() { - await window.ethereum.enable(); - const provider = new ethers.providers.Web3Provider(window.ethereum); + async function walletConnect(ethClient: EthClientWalletOk) { + // TODO: add testnets ethclients. Chain ID will change: https://chainlist.org/ + if (ethClient.ok) { + try { + // https://eips.ethereum.org/EIPS/eip-3326 + await window.ethereum.request({ + method: "wallet_switchEthereumChain", + params: [{ chainId: "0xf00" }] + }); + } catch (switchError) { + // This error code indicates that the chain has not been added to MetaMask. + if (switchError.code === 4902) { + try { + // https://eips.ethereum.org/EIPS/eip-3085 + await window.ethereum.request({ + method: "wallet_addEthereumChain", + // IMPORTANT! RPC without HTTPs is not allowed + // IMPORTANT! Add new chains with a default chain ID in metamask is not allowed + params: [{ chainId: ethClient.chainId, rpcUrls: [ethClient.url] }] + }); + } catch (addError) { + // handle "add" error + throw addError; + } + } + // handle other "switch" errors + throw switchError; + } + } } if (ethClients.error) @@ -34,9 +61,20 @@ export default function ConnectWallet() { ) : ( - {ethClients.data.map(ethClient => ( - - ))} + {ethClients.data.map( + ethClient => + ethClient.ok && ( +
+ {prettyDnpName(ethClient.dnpName)} + +
+ ) + )}
)} diff --git a/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx b/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx index 1a3ca2101..13e1b37a8 100644 --- a/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx +++ b/packages/admin-ui/src/pages/dashboard/components/Dashboard.tsx @@ -9,6 +9,7 @@ import SubTitle from "components/SubTitle"; import Title from "components/Title"; import "./dashboard.scss"; +import ConnectWallet from "./ConnectWallet"; export default function Dashboard() { return ( @@ -19,6 +20,9 @@ export default function Dashboard() {
Package updates + + Connect wallet +
diff --git a/packages/admin-ui/src/pages/dashboard/components/dashboard.scss b/packages/admin-ui/src/pages/dashboard/components/dashboard.scss index 95c845d98..c55e5506a 100644 --- a/packages/admin-ui/src/pages/dashboard/components/dashboard.scss +++ b/packages/admin-ui/src/pages/dashboard/components/dashboard.scss @@ -27,7 +27,8 @@ grid-template-columns: repeat(auto-fill, minmax(7.5em, 1fr)); } - .package-updates { + .package-updates, + .connect-wallet { // Give the single card a max width that matches the other cards grid-column: 1 / 4; } @@ -76,7 +77,8 @@ // Package updates card -.package-update-item { +.package-update-item, +.connect-wallet-item { display: flex; justify-content: space-between; align-items: center; diff --git a/packages/dappmanager/src/calls/ethClientsGet.ts b/packages/dappmanager/src/calls/ethClientsGet.ts index 34bd101ff..29f0c1a42 100644 --- a/packages/dappmanager/src/calls/ethClientsGet.ts +++ b/packages/dappmanager/src/calls/ethClientsGet.ts @@ -1,3 +1,5 @@ -import { EthClient } from "../types"; +import { EthClientWallet } from "../types"; -export async function ethClientsGet(): Promise {} +export async function ethClientsGet(): Promise { + return []; +}