Integrating web3 from Metamask in React - reactjs

I am new to ReactJS.
Seem to be having trouble integrating web3 from Metamask in React.
Metamask version: web3#1.0.0-beta.34
import Web3 from 'web3'
let web3;
window.addEventListener('load', function () {
if (typeof window.web3 !== 'undefined') {
web3 = new Web3(window.web3.currentProvider);
} else {
// No web 3 provider
console.log("Please install Metamask");
}
});
export default web3;
Getting the following error:
window is not defined
ReferenceError: window is not defined
at Object../lib/getWeb3.js (lib/getWeb3.js:5:0)

window is not defined on Server, only in client's browser, hence you can't use MetaMask server-side. However, you can connect to INFURA when you want to use web3 in your React component server-side or without MetaMask support.
The simplest way is to use react-web3-provider component.
Add the Web3Provider to your root React component:
import Web3Provider from 'react-web3-provider';
ReactDOM.render(
<Web3Provider
defaultWeb3Provider="https://mainnet.infura.io/YOUR_API_KEY"
loading="Loading..."
>
<App />
</Web3Provider>
)
Then in component where you want to use Web3:
import { withWeb3 } from 'react-web3-provider';
class MyComponent {
render() {
const { web3 } = this.props;
web3.eth.getAccounts(console.log);
// Version 1.0.0-beta.35
return "Web3 version: {web3.version}";
}
}
export default withWeb3(MyComponent);

Related

Set ethers provider to use any injected web3 providers

Using MetaMask provider to detect MetaMask as provider and Etherprojects provider to set provider.
import { ExternalProvider, Web3Provider } from '#ethersproject/providers';
import detectEthereumProvider from '#metamask/detect-provider';
Checking for MetaMask provider only and setting metamask as provider.
export default class Dapp extends React.Component<Props, State> {
provider!: Web3Provider;
componentDidMount = async () => {
const browserProvider = await detectEthereumProvider() as ExternalProvider;
if (browserProvider?.isMetaMask !== true) {
this.setError(
);
}
this.provider = new ethers.providers.Web3Provider(browserProvider);
this.registerWalletEvents(browserProvider);
await this.initWallet();
}
I had resolved this issue to detect any injected web3 provider last week accidentally I couldn't save the code now I cannot figure how I got it to work with any web3 provider wallets like Trust Wallet or Brave Wallet.

Why is the context provider state not shared between micro-frontends?

I am trying to split a monolithic React application into micro-frontends using Webpack Module Federation
The application relies on Context objects that are provided by the Host application. The context works as expected inside of the Host application, but not in the Remote application.
My code looks like this:
Host Application
Context and Context provider:
// TestContext.js
import React from 'libs/react';
export const TestContext = React.createContext("Not Initialized :(");
// LocalSample.js
import React from 'libs/react';
import { TestContext } from './TestContext';
export default function () {
const context = React.useContext(TestContext);
return <div>Local: {context}</div>
}
// App.js
import React, { Suspense } from 'libs/react';
import { TestContext } from './TestContext';
import RemoteSample from 'remote1/RemoteSample';
import LocalSample from './LocalSample';
export default function () {
return (
<TestContext.Provider value="Initialized :)">
<LocalSample />
<Suspense fallback={'loading...'}>
<RemoteSample />
</Suspense>
</TestContext.Provider>
);
};
Remote Application
// RemoteSample.js
import React from 'libs/react';
import { TestContext } from 'host/TestContext';
export default function () {
const context = React.useContext(TestContext);
return <div>Remote: {context}</div>
}
Sample code is also available at https://github.com/christianheld/module-federation-context-repro
The output of the application is:
Local: Initialized :)
Remote: Not Initialized :(
How can I share the context value from Host to the Remote application?
The trick is to put your contexts in into the shared object in the Module Federation configuration.
In my application this looks like:
shared: {
"./src/MyContext": {}
}
I found the workaround in the following video: https://www.youtube.com/watch?v=-LNcpralkjM&t=540
Please watch the video for the explanation.

How Do I Call An Authenticated HTTP Trigger Google Cloud Function Via A Next.js (with Typescript) App?

I created a Google Cloud Platform account, and made a simple hello_world type Python "Cloud Function" that just spits out some simple text. I made this function "HTTP" accessible and only able to be called/authenticated by a "Service Account" that I made for the purpose of calling this very function. I generated a key for this "Service Account" and downloaded the json file for the key.
The problem is that I can't find any documentation on how to call this function with my service account in a next.js app. I tried this:
import React from 'react';
import { Button } from 'react-bootstrap';
import { GoogleAuth } from 'google-auth-library';
const projectId = 'gtwitone';
const keyFilename = '/Users/<myusername>/path/to/cloudfunction/credentials.json';
class Middle extends React.Component {
handleClick() {
console.log('this is:', this);
}
// This syntax ensures `this` is bound within handleClick. // Warning: this is *experimental* syntax. handleClick = () => { console.log('this is:', this); }
/* async listFunctions() {
const [functions] = await client.listFunctions();
console.info(functions);
} */
async runGoogleCloudFunctionTest() {
// Define your URL, here with Cloud Run but the security is exactly the same with Cloud Functions (same underlying infrastructure)
const url = "https://us-central1-<projectname>.cloudfunctions.net/<functionname>"
//Example with the key file, not recommended on GCP environment.
const auth = new GoogleAuth({keyFilename: keyFilename})
//Create your client with an Identity token.
const client = await auth.getIdTokenClient(url);
const res = await client.request({url});
console.log(res.data);
}
render() {
return (
<div className="col-md-12 text-center">
<Button variant='primary' onClick={this.runGoogleCloudFunctionTest}>
Click me
</Button>
</div>
);
}
}
export default Middle;
But I got this error in my terminal:
<myusername>#<mycomputername> <thisnextjsappdirectory> % yarn dev
yarn run v1.22.17
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
wait - compiling...
event - compiled client and server successfully in 267 ms (124 modules)
wait - compiling / (client and server)...
wait - compiling...
error - ./node_modules/google-auth-library/build/src/auth/googleauth.js:17:0
Module not found: Can't resolve 'child_process'
Import trace for requested module:
./node_modules/google-auth-library/build/src/index.js
./components/Middle.tsx
./pages/index.tsx
https://nextjs.org/docs/messages/module-not-found
Native Node.js APIs are not supported in the Edge Runtime. Found `child_process` imported.
Could not find files for / in .next/build-manifest.json
Could not find files for / in .next/build-manifest.json
^C
<myusername>#<mycomputername> <thisnextjsappdirectory> %
I know that this is problem with server side rendering in my Next.js app and people recommend using a client side package like this https://github.com/google/google-api-javascript-client. But google-api-javascript-client doesn't have any documentation on authenticating with a .json credentials file instead of an API KEY which I do not have.
In short how do I get my app to work and run the Google Cloud function with a .json credentials file for am authenticated service account?
I fixed it by simply moving the GoogleAuth api call to the pages/api route.
pages/api/google.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next"
import { GoogleAuth } from "google-auth-library"
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const url = process.env.FUNCTION_URL as string
//Example with the key file, not recommended on GCP environment.
const auth = new GoogleAuth({ keyFilename: process.env.KEYSTORE_PATH })
//Create your client with an Identity token.
const client = await auth.getIdTokenClient(url)
const result = await client.request({ url })
console.log(result.data)
res.json({ data: result.data })
}
components/Middle.tsx
import React from "react"
import { Button } from "react-bootstrap"
class Middle extends React.Component {
handleClick() {
console.log("this is:", this)
}
// this talks with /pages/api/google
async imCallingAnAPI() {
const result = await fetch("/api/google")
console.log({ result })
}
render() {
return (
<div className="col-md-12 text-center">
<Button variant="primary" onClick={this.imCallingAnAPI}>
Click me
</Button>
</div>
)
}
}
export default Middle
pages/index.tsx
import type { NextPage } from 'next'
import Header from '../components/Header';
import Footer from '../components/Footer';
import Middle from '../components/Middle';
const Home: NextPage = () => {
return (
<><main className='d-flex flex-column min-vh-100'>
<Header />
<br></br>
<br></br>
<Middle />
</main>
<footer>
<Footer />
</footer>
</>
)
}
export default Home
I think that next.js has trouble loading GoogleAuth in a component. I'm not 100% sure why, but I think it has to do with next.js not knowing exactly how to handle GoogleAuth with server-side rendering.

TypeError: Cannot read properties of undefined (reading 'Contract') - Next.js, web3.0

I'm trying to create a small web3.0 app, using next.js.
I have deployed my contract on rinkeby test network, and I am able to check the functionalities using Remix, work properly. I have also worked on similar projects on react, they also work as exepected.
But when I try to connect I am currently facing the following issue (maybe this has something to do with Server side rendering of next.js or HttpProvider, I'm not sure):
TypeError: Cannot read properties of undefined (reading 'Contract')
3 | import address from './build/campaignHubAddress.json';
4 |
> 5 | const instance = new web3.eth.Contract(contract.abi, address.campaignHubAddress);
| ^
6 | export default instance;
web3.js
import Web3 from 'web3';
let web3;
if (typeof window !== 'undefined' && typeof window.ethereum !== 'undefined') {
window.ethereum.request({ method: "eth_requestAccounts" });
web3 = new Web3(window.ethereum);
} else {
const provider = new Web3.providers.HttpProvider(
'https://rinkeby.infura.io/v3/3ed112ee6c4d42a09e485ddb5eec5fa2'
);
web3 = new Web3(provider);
}
export default web3;
instance.js
import web3 from 'web3';
import contract from './build/CampaignHub.json';
import address from './build/campaignHubAddress.json';
const instance = new web3.eth.Contract(contract.abi, address.campaignHubAddress);
export default instance;
index.js
import React from 'react';
import web3 from '../web3';
import instance from '../instance';
class Index extends React.Component{
static async getInitialProps(){
const campaigns = await instance.methods.getCampaigns().call();
return { campaigns };
}
render() {
return (
<>
<p>Hello, this is the test page for now.</p>
<p>Web3 version: {web3.version}</p>
<p>List of all the running campaigns: {this.props.campaigns} </p>
</>
);
}
}
export default Index;
This seems basic, but I have been stuck at it for some time now.
You already create instance of your web3 in web3.js
you can use it directly without using the "new"
import web3 from 'web3';
import contract from './build/CampaignHub.json';
import address from './build/campaignHubAddress.json';
const instance = web3.eth.Contract(contract.abi, address.campaignHubAddress);
export default instance;
After scratching a lot of head, I found that the error was in instance.js. While importing import web3 from 'web3'; I should have done import web3 from './web3';

Unable to detect window.ethereum on Metamask mobile

I am unable to detect window.ethereum in Metamask mobile but works fine in metamask extension and Trust Wallet. I am using react with vite.
Also I am writing code in repl.it ide with react template.
Here is the full code.
import React from 'react';
import { ethers } from "ethers";
function dosomething(){
if(window.ethereum){
const provider = new providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
}
else{
alert('Please install MetaMask first.');
}
}
function NewFun() {
if (window.ethereum) {
dosomething();
}
else {
window.addEventListener('ethereum#initialized', dosomething, {
once: true,
});
// If the event is not dispatched by the end of the timeout,
// the user probably doesn't have MetaMask installed.
setTimeout(dosomething, 5000); // 5 seconds
}
}
function Test() {
return(
<main>
<button onClick={NewFun}>Hiii</button>
Hii
</main>);
}
export default Test;
Basic Html , js works fine.

Resources