From 77311eeb78701000812d77f41a407e15ef964bed Mon Sep 17 00:00:00 2001 From: Damian Date: Tue, 30 Nov 2021 14:58:00 -0300 Subject: [PATCH 1/4] Change network detection to Web3Context The wrong network alert is shown from Web3Context and a rightNetwork flag is set to be able to know (using the context) if the user has the right network selected. Use readContracts from context to be able to show dev profiles even if the user is not on the right network. --- .../react-app/components/cards/MediaCard.jsx | 24 +++--- packages/react-app/helpers/Web3Context.js | 59 ++++++++++++- packages/react-app/pages/index.js | 84 ++----------------- 3 files changed, 76 insertions(+), 91 deletions(-) diff --git a/packages/react-app/components/cards/MediaCard.jsx b/packages/react-app/components/cards/MediaCard.jsx index b2e0524..e9b41df 100644 --- a/packages/react-app/components/cards/MediaCard.jsx +++ b/packages/react-app/components/cards/MediaCard.jsx @@ -44,10 +44,10 @@ function MediaCard({ secondaryActionOnClick, secondaryAction, privateProfile, - dRecruitContract, }) { const [decryptedData, setDecryptedData] = useState(); const [canView, setCanView] = useState(false); + const context = useContext(Web3Context); useEffect(() => { let isValidRecipient = false; (async () => { @@ -71,7 +71,7 @@ function MediaCard({ const handleRequestPrivateProfileUnlock = async () => { try { - const tx = await dRecruitContract.request(privateProfile.tokenId, { + const tx = await context.writeContracts.DRecruitV1.request(privateProfile.tokenId, { value: ethers.utils.parseEther("0.01"), }); toast({ @@ -192,15 +192,17 @@ function MediaCard({ )} - + {context.rightNetwork && ( + + )} ); diff --git a/packages/react-app/helpers/Web3Context.js b/packages/react-app/helpers/Web3Context.js index b0fbc15..32b556c 100644 --- a/packages/react-app/helpers/Web3Context.js +++ b/packages/react-app/helpers/Web3Context.js @@ -188,6 +188,7 @@ export function Web3Provider({ children, network = "localhost", DEBUG = false, N const localChainId = localProvider && localProvider._network && localProvider._network.chainId; const selectedChainId = userSigner && userSigner.provider && userSigner.provider._network && userSigner.provider._network.chainId; + const rightNetwork = localChainId == selectedChainId; // For more hooks, check out 🔗eth-hooks at: https://www.npmjs.com/package/eth-hooks @@ -268,7 +269,7 @@ export function Web3Provider({ children, network = "localhost", DEBUG = false, N const networkLocal = NETWORK(localChainId); if (selectedChainId === 1337 && localChainId === 31337) { networkDisplay = ( -
+
); + } else { + networkDisplay = ( +
+ + You have {networkSelected && networkSelected.name} selected and you need to be on{" "} + +
+ } + type="error" + closable={false} + /> +
+ ); } } else { networkDisplay = ( @@ -410,6 +466,7 @@ export function Web3Provider({ children, network = "localhost", DEBUG = false, N loadWeb3Modal, logoutOfWeb3Modal, contractConfig, + rightNetwork, }; return {children}; diff --git a/packages/react-app/pages/index.js b/packages/react-app/pages/index.js index c17f8ee..6add910 100644 --- a/packages/react-app/pages/index.js +++ b/packages/react-app/pages/index.js @@ -1,23 +1,7 @@ import React, { useCallback, useContext, useEffect, useState } from "react"; import { Core } from "@self.id/core"; import axios from "axios"; -import { - Button, - Code, - HStack, - InputGroup, - InputLeftElement, - Box, - Heading, - SimpleGrid, - VStack, - AlertDialog, - AlertDialogBody, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogContent, - AlertDialogOverlay, -} from "@chakra-ui/react"; +import { Button, Code, HStack, InputGroup, InputLeftElement, Box, Heading, SimpleGrid, VStack } from "@chakra-ui/react"; import { Web3Context } from "../helpers/Web3Context"; import { CeramicClient } from "@ceramicnetwork/http-client"; import { ModelManager } from "@glazed/devtools"; @@ -44,40 +28,6 @@ import { IPFS_GATEWAY } from "../constants"; function Home() { const context = useContext(Web3Context); - const [isAlertOpen, setIsAlertOpen] = React.useState(false); - const onAlertClose = async () => { - const data = [ - { - chainId: "0x" + context.targetNetwork.chainId.toString(16), - chainName: context.targetNetwork.name, - nativeCurrency: context.targetNetwork.nativeCurrency, - rpcUrls: [context.targetNetwork.rpcUrl], - blockExplorerUrls: [context.targetNetwork.blockExplorer], - }, - ]; - try { - await ethereum.request({ - method: "wallet_switchEthereumChain", - params: [{ chainId: data[0].chainId }], - }); - setIsAlertOpen(false); - } catch (switchError) { - // This error code indicates that the chain has not been added to MetaMask. - if (switchError.code === 4902) { - try { - await ethereum.request({ - method: "wallet_addEthereumChain", - params: data, - }); - setIsAlertOpen(false); - } catch (addError) { - console.log(addError); - } - } - // handle other "switch" errors - } - }; - const cancelRef = React.useRef(); const [inputEmail, setInputEmail] = useState(""); const [recipients, setRecipients] = useState([]); const [developerProfiles, setDeveloperProfiles] = useState([]); @@ -94,7 +44,6 @@ function Home() { // The goal is to only have the API call fire when user stops typing ... // ... so that we aren't hitting our API rapidly. const debouncedSearchTerm = useDebounce(searchTerm, 500); - const [dRecruitContract, setDRecruitContract] = useState(); const [store, setStore] = useState(); const [prevNote, setPrevNote] = useState(""); @@ -118,11 +67,9 @@ function Home() { ); const init = async () => { - if (context.injectedProvider && context.injectedProvider.getSigner()) { + if (context.localProvider) { try { - const signer = context.injectedProvider.getSigner(); - const contract = await loadDRecruitV1Contract(context.targetNetwork, signer); - setDRecruitContract(contract); + const contract = context.readContracts.DRecruitV1; const lastTokenId = await contract.tokenId(); const tokenIds = [...Array(parseInt(lastTokenId, 10)).keys()]; const tokenURIs = await Promise.all(tokenIds.map(async id => contract.uri(id))); @@ -140,7 +87,7 @@ function Home() { ); setDeveloperProfiles(devProfiles); } catch (error) { - setIsAlertOpen(true); + console.log(error); } } }; @@ -170,26 +117,7 @@ function Home() { return ( - - - - - Switch network - - - - To use this app you must switch to the {context.targetNetwork.name} network. - - - - - - - - - + setSearchTerm(e.target.value)} /> {isSearching &&
Searching ...
} Found developers: @@ -225,7 +153,6 @@ function Home() { date={`Birthdate: ${basicProfile.birthDate}`} primaryAction="Request contact information" secondaryAction="View contact information" - dRecruitContract={dRecruitContract} hasWebAccount={!!webAccounts} privateProfile={privateProfile} /> @@ -271,7 +198,6 @@ function Home() { date={`Birthdate: ${basicProfile.birthDate}`} primaryAction="Request contact information" secondaryAction="View contact information" - dRecruitContract={dRecruitContract} hasWebAccount={!!webAccounts} privateProfile={privateProfile} /> From 3a98910dcf2787465ba7457aa221597f65e3da31 Mon Sep 17 00:00:00 2001 From: Damian Date: Wed, 1 Dec 2021 16:31:47 -0300 Subject: [PATCH 2/4] Add AlertDialog for wrong network message and fix chainChanged callback --- .../components/WrongNetworkAlertDialog.jsx | 90 ++++++++++++++ packages/react-app/helpers/Web3Context.js | 112 +++++------------- packages/react-app/pages/_app.js | 4 +- 3 files changed, 123 insertions(+), 83 deletions(-) create mode 100644 packages/react-app/components/WrongNetworkAlertDialog.jsx diff --git a/packages/react-app/components/WrongNetworkAlertDialog.jsx b/packages/react-app/components/WrongNetworkAlertDialog.jsx new file mode 100644 index 0000000..1f26da1 --- /dev/null +++ b/packages/react-app/components/WrongNetworkAlertDialog.jsx @@ -0,0 +1,90 @@ +import React, { useContext, useEffect, useState } from "react"; +import { + Button, + AlertDialog, + AlertDialogBody, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogContent, + AlertDialogOverlay, +} from "@chakra-ui/react"; + +import { Web3Context } from "../helpers/Web3Context"; + +function WrongNetworkAlertDialog() { + const [isAlertOpen, setIsAlertOpen] = useState(false); + const context = useContext(Web3Context); + const cancelRef = React.useRef(); + + useEffect(() => { + if (context) { + setIsAlertOpen(!context.rightNetwork); + } + }, [context && context.rightNetwork]); + + const onNetworkSwitch = async () => { + const data = [ + { + chainId: "0x" + context.targetNetwork.chainId.toString(16), + chainName: context.targetNetwork.name, + nativeCurrency: context.targetNetwork.nativeCurrency, + rpcUrls: [context.targetNetwork.rpcUrl], + blockExplorerUrls: [context.targetNetwork.blockExplorer], + }, + ]; + try { + await ethereum.request({ + method: "wallet_switchEthereumChain", + params: [{ chainId: data[0].chainId }], + }); + setIsAlertOpen(false); + } catch (switchError) { + // This error code indicates that the chain has not been added to MetaMask. + if (switchError.code === 4902) { + try { + await ethereum.request({ + method: "wallet_addEthereumChain", + params: data, + }); + setIsAlertOpen(false); + } catch (addError) { + console.log(addError); + } + } + // handle other "switch" errors + } + }; + + return ( + + + + + Switch network + + + + To use this app you must switch to the {context.targetNetwork.name} network. + + + + + + + + + + ); +} + +export default WrongNetworkAlertDialog; diff --git a/packages/react-app/helpers/Web3Context.js b/packages/react-app/helpers/Web3Context.js index 32b556c..97be51d 100644 --- a/packages/react-app/helpers/Web3Context.js +++ b/packages/react-app/helpers/Web3Context.js @@ -284,61 +284,6 @@ export function Web3Provider({ children, network = "localhost", DEBUG = false, N /> ); - } else { - networkDisplay = ( -
- - You have {networkSelected && networkSelected.name} selected and you need to be on{" "} - -
- } - type="error" - closable={false} - /> - - ); } } else { networkDisplay = ( @@ -354,36 +299,39 @@ export function Web3Provider({ children, network = "localhost", DEBUG = false, N const signer = provider.getSigner(); const account = await signer.getAddress(); setInjectedProvider(provider); - const mySelf = await SelfID.authenticate({ - authProvider: new EthereumAuthProvider(provider.provider, account), - ceramic: CERAMIC_TESTNET, - connectNetwork: CERAMIC_TESTNET, - model: modelAliases, - }); - console.log("curr self", mySelf.id); - setSelf(mySelf); - const { data } = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/nonce/${account}`); - const signature = await provider.provider.request({ - method: "personal_sign", - params: [data.message, account], - }); - const verifyResponse = await axios.post( - `${process.env.NEXT_PUBLIC_API_URL}/verify/${account}`, - { - signature, - }, - { - withCredentials: true, - }, - ); - if (verifyResponse.status !== 200) { - throw new Error("Unauthorized"); + if (rightNetwork) { + const mySelf = await SelfID.authenticate({ + authProvider: new EthereumAuthProvider(provider.provider, account), + ceramic: CERAMIC_TESTNET, + connectNetwork: CERAMIC_TESTNET, + model: modelAliases, + }); + console.log("curr self", mySelf.id); + setSelf(mySelf); + + const { data } = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/nonce/${account}`); + const signature = await provider.provider.request({ + method: "personal_sign", + params: [data.message, account], + }); + const verifyResponse = await axios.post( + `${process.env.NEXT_PUBLIC_API_URL}/verify/${account}`, + { + signature, + }, + { + withCredentials: true, + }, + ); + if (verifyResponse.status !== 200) { + throw new Error("Unauthorized"); + } } connection.on("chainChanged", chainId => { console.log(`chain changed to ${chainId}! updating providers`); - setInjectedProvider(provider); + setInjectedProvider(new ethers.providers.Web3Provider(connection)); }); connection.on("accountsChanged", newAccounts => { @@ -404,13 +352,13 @@ export function Web3Provider({ children, network = "localhost", DEBUG = false, N connection.on("disconnect", (code, reason) => { logoutOfWeb3Modal(); }); - }, [setInjectedProvider]); + }, [setInjectedProvider, rightNetwork]); useEffect(() => { if (web3Modal && web3Modal.cachedProvider) { loadWeb3Modal(); } - }, [web3Modal]); + }, [loadWeb3Modal]); let faucetHint = ""; const faucetAvailable = localProvider && localProvider.connection && targetNetwork.name.indexOf("local") !== -1; diff --git a/packages/react-app/pages/_app.js b/packages/react-app/pages/_app.js index 8ae1a52..6c67aaf 100644 --- a/packages/react-app/pages/_app.js +++ b/packages/react-app/pages/_app.js @@ -3,10 +3,11 @@ import "antd/dist/antd.css"; import Head from "next/head"; import Link from "next/link"; import { ChakraProvider } from "@chakra-ui/react"; -import React, { useEffect, useRef, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { ThemeSwitcherProvider } from "react-css-theme-switcher"; import { Header } from "../components"; import DevUI from "../components/DevUI"; +import WrongNetworkAlertDialog from "../components/WrongNetworkAlertDialog"; import { Web3Provider } from "../helpers/Web3Context"; import "../styles/index.css"; @@ -59,6 +60,7 @@ function MyApp({ Component, pageProps }) { + From 3fa2d45c7f0ba4fc0fad86952fb5a5df649626cc Mon Sep 17 00:00:00 2001 From: Damian Date: Fri, 3 Dec 2021 13:22:27 -0300 Subject: [PATCH 3/4] Fix rightNetwork undefined --- packages/react-app/pages/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-app/pages/index.js b/packages/react-app/pages/index.js index e5dac12..e1629fe 100644 --- a/packages/react-app/pages/index.js +++ b/packages/react-app/pages/index.js @@ -71,7 +71,7 @@ function Home() { if (context.localProvider) { try { const contract = context.readContracts.DRecruitV1; - if (rightNetwork && context.injectedProvider && context.injectedProvider.getSigner()) { + if (context.rightNetwork && context.injectedProvider && context.injectedProvider.getSigner()) { const signer = context.injectedProvider.getSigner(); const tokenAddress = await contract.token(); console.log({ tokenAddress }); From 6db299625261895d191b52e8434d7044ac87629f Mon Sep 17 00:00:00 2001 From: Damian Date: Fri, 3 Dec 2021 13:43:15 -0300 Subject: [PATCH 4/4] Fix init profiles before DRecruitV1 contract loaded --- packages/react-app/pages/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-app/pages/index.js b/packages/react-app/pages/index.js index e1629fe..c1a6109 100644 --- a/packages/react-app/pages/index.js +++ b/packages/react-app/pages/index.js @@ -71,6 +71,10 @@ function Home() { if (context.localProvider) { try { const contract = context.readContracts.DRecruitV1; + if (!contract) { + console.log("Contract DRecruitV1 not loaded yet"); + return; + } if (context.rightNetwork && context.injectedProvider && context.injectedProvider.getSigner()) { const signer = context.injectedProvider.getSigner(); const tokenAddress = await contract.token(); @@ -123,7 +127,7 @@ function Home() { useEffect(() => { init(); - }, [context.injectedProvider]); + }, [context.readContracts.DRecruitV1, context.injectedProvider]); return (