fix: dapps metadata (#96)

* fix: metadata to react-dapp-v2

* fix: metadata react app with ethers

* fix: metadata react app with web3

* feat: adds web3modal to web3 example dapp

* feat: adds web3modal to ethers example dapp
This commit is contained in:
Gancho Radkov 2022-12-22 11:34:36 +02:00 committed by GitHub
parent 7a3886073b
commit c7890ae0b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 165 additions and 88 deletions

View File

@ -30,10 +30,10 @@
"dependencies": { "dependencies": {
"@ethereumjs/tx": "^3.5.0", "@ethereumjs/tx": "^3.5.0",
"@walletconnect/encoding": "^1.0.1", "@walletconnect/encoding": "^1.0.1",
"@walletconnect/qrcode-modal": "^1.7.1", "@walletconnect/types": "^2.1.4",
"@walletconnect/types": "^2.1.1", "@walletconnect/universal-provider": "^2.1.4",
"@walletconnect/universal-provider": "^2.1.1", "@walletconnect/utils": "^2.1.4",
"@walletconnect/utils": "^2.1.1", "@web3modal/standalone": "^2.0.0-beta.10",
"axios": "^0.21.1", "axios": "^0.21.1",
"blockies-ts": "^1.0.0", "blockies-ts": "^1.0.0",
"caip-api": "^2.0.0-beta.1", "caip-api": "^2.0.0-beta.1",

View File

@ -0,0 +1,17 @@
import Head from "next/head";
import * as React from "react";
import { DEFAULT_APP_METADATA } from "../constants";
const Metadata = () => (
<Head>
<title>{DEFAULT_APP_METADATA.name}</title>
<meta name="description" content={DEFAULT_APP_METADATA.description} />
<meta name="url" content={DEFAULT_APP_METADATA.url} />
{DEFAULT_APP_METADATA.icons.map((icon, index) => (
<link key={index} rel="icon" href={icon} />
))}
</Head>
);
export default Metadata;

View File

@ -33,7 +33,7 @@ export enum DEFAULT_EIP_155_EVENTS {
export const DEFAULT_LOGGER = "debug"; export const DEFAULT_LOGGER = "debug";
export const DEFAULT_APP_METADATA = { export const DEFAULT_APP_METADATA = {
name: "React App", name: "React App with ethers",
description: "React App for WalletConnect", description: "React App for WalletConnect",
url: "https://walletconnect.com/", url: "https://walletconnect.com/",
icons: ["https://avatars.githubusercontent.com/u/37784886"], icons: ["https://avatars.githubusercontent.com/u/37784886"],

View File

@ -8,7 +8,7 @@ import {
useState, useState,
} from "react"; } from "react";
import QRCodeModal from "@walletconnect/qrcode-modal"; import { Web3Modal } from "@web3modal/standalone";
import { apiGetChainNamespace, ChainsMap } from "caip-api"; import { apiGetChainNamespace, ChainsMap } from "caip-api";
import UniversalProvider from "@walletconnect/universal-provider"; import UniversalProvider from "@walletconnect/universal-provider";
import { PairingTypes, SessionTypes } from "@walletconnect/types"; import { PairingTypes, SessionTypes } from "@walletconnect/types";
@ -62,6 +62,7 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
const [accounts, setAccounts] = useState<string[]>([]); const [accounts, setAccounts] = useState<string[]>([]);
const [chainData, setChainData] = useState<ChainNamespaces>({}); const [chainData, setChainData] = useState<ChainNamespaces>({});
const [chain, setChain] = useState<string>(""); const [chain, setChain] = useState<string>("");
const [web3Modal, setWeb3Modal] = useState<Web3Modal>();
const resetApp = () => { const resetApp = () => {
setPairings([]); setPairings([]);
@ -98,46 +99,47 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
resetApp(); resetApp();
}, [ethereumProvider]); }, [ethereumProvider]);
const _subscribeToProviderEvents = useCallback(async (_client: UniversalProvider) => { const _subscribeToProviderEvents = useCallback(
if (typeof _client === "undefined") { async (_client: UniversalProvider) => {
throw new Error("WalletConnect is not initialized"); if (typeof _client === "undefined") {
} throw new Error("WalletConnect is not initialized");
}
_client.on("display_uri", async (uri: string) => { _client.on("display_uri", async (uri: string) => {
console.log("EVENT", "QR Code Modal open"); console.log("EVENT", "QR Code Modal open");
QRCodeModal.open(uri, () => { web3Modal?.openModal({ uri });
console.log("EVENT", "QR Code Modal closed");
}); });
});
// Subscribe to session ping // Subscribe to session ping
_client.on("session_ping", ({ id, topic }: { id: number; topic: string }) => { _client.on("session_ping", ({ id, topic }: { id: number; topic: string }) => {
console.log("EVENT", "session_ping"); console.log("EVENT", "session_ping");
console.log(id, topic); console.log(id, topic);
}); });
// Subscribe to session event // Subscribe to session event
_client.on("session_event", ({ event, chainId }: { event: any; chainId: string }) => { _client.on("session_event", ({ event, chainId }: { event: any; chainId: string }) => {
console.log("EVENT", "session_event"); console.log("EVENT", "session_event");
console.log(event, chainId); console.log(event, chainId);
}); });
// Subscribe to session update // Subscribe to session update
_client.on( _client.on(
"session_update", "session_update",
({ topic, session }: { topic: string; session: SessionTypes.Struct }) => { ({ topic, session }: { topic: string; session: SessionTypes.Struct }) => {
console.log("EVENT", "session_updated"); console.log("EVENT", "session_updated");
setSession(session); setSession(session);
}, },
); );
// Subscribe to session delete // Subscribe to session delete
_client.on("session_delete", ({ id, topic }: { id: number; topic: string }) => { _client.on("session_delete", ({ id, topic }: { id: number; topic: string }) => {
console.log("EVENT", "session_deleted"); console.log("EVENT", "session_deleted");
console.log(id, topic); console.log(id, topic);
resetApp(); resetApp();
}); });
}, []); },
[web3Modal],
);
const createClient = useCallback(async () => { const createClient = useCallback(async () => {
try { try {
@ -149,15 +151,19 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
relayUrl: DEFAULT_RELAY_URL, relayUrl: DEFAULT_RELAY_URL,
}); });
const web3Modal = new Web3Modal({
projectId: DEFAULT_PROJECT_ID,
});
setEthereumProvider(provider); setEthereumProvider(provider);
setClient(provider.client); setClient(provider.client);
await _subscribeToProviderEvents(provider); setWeb3Modal(web3Modal);
} catch (err) { } catch (err) {
throw err; throw err;
} finally { } finally {
setIsInitializing(false); setIsInitializing(false);
} }
}, [_subscribeToProviderEvents]); }, []);
const createWeb3Provider = useCallback((ethereumProvider: UniversalProvider) => { const createWeb3Provider = useCallback((ethereumProvider: UniversalProvider) => {
const web3Provider = new providers.Web3Provider(ethereumProvider); const web3Provider = new providers.Web3Provider(ethereumProvider);
@ -207,9 +213,9 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
setSession(session); setSession(session);
setChain(caipChainId); setChain(caipChainId);
QRCodeModal.close(); web3Modal?.closeModal();
}, },
[ethereumProvider, chainData.eip155, createWeb3Provider], [ethereumProvider, chainData.eip155, createWeb3Provider, web3Modal],
); );
const onSessionConnected = useCallback( const onSessionConnected = useCallback(
@ -265,6 +271,10 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
} }
}, [client, createClient]); }, [client, createClient]);
useEffect(() => {
if (ethereumProvider && web3Modal) _subscribeToProviderEvents(ethereumProvider);
}, [_subscribeToProviderEvents, ethereumProvider, web3Modal]);
useEffect(() => { useEffect(() => {
const fetchBalances = async () => { const fetchBalances = async () => {
if (!web3Provider || !accounts) return; if (!web3Provider || !accounts) return;

View File

@ -1,5 +1,6 @@
import type { AppProps } from "next/app"; import type { AppProps } from "next/app";
import { createGlobalStyle } from "styled-components"; import { createGlobalStyle } from "styled-components";
import Metadata from "../components/Metadata";
import { ClientContextProvider } from "../contexts/ClientContext"; import { ClientContextProvider } from "../contexts/ClientContext";
@ -11,6 +12,7 @@ const GlobalStyle = createGlobalStyle`
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
return ( return (
<> <>
<Metadata />
<GlobalStyle /> <GlobalStyle />
<ClientContextProvider> <ClientContextProvider>
<Component {...pageProps} /> <Component {...pageProps} />

View File

@ -30,10 +30,10 @@
"dependencies": { "dependencies": {
"@ethereumjs/tx": "^3.5.0", "@ethereumjs/tx": "^3.5.0",
"@walletconnect/encoding": "^1.0.1", "@walletconnect/encoding": "^1.0.1",
"@walletconnect/universal-provider": "2.1.1",
"@walletconnect/qrcode-modal": "^1.7.1",
"@walletconnect/types": "2.1.1", "@walletconnect/types": "2.1.1",
"@walletconnect/utils": "2.1.1", "@walletconnect/universal-provider": "2.1.4",
"@walletconnect/utils": "2.1.4",
"@web3modal/standalone": "^2.0.0-beta.10",
"axios": "^0.21.1", "axios": "^0.21.1",
"blockies-ts": "^1.0.0", "blockies-ts": "^1.0.0",
"caip-api": "^2.0.0-beta.1", "caip-api": "^2.0.0-beta.1",
@ -41,9 +41,9 @@
"eth-sig-util": "^2.5.3", "eth-sig-util": "^2.5.3",
"ethereumjs-util": "^7.0.6", "ethereumjs-util": "^7.0.6",
"ethers": "^5.3.0", "ethers": "^5.3.0",
"next": "12.2.4",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",
"qr-image": "^3.2.0", "qr-image": "^3.2.0",
"next": "12.2.4",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-scripts": "^4.0.3", "react-scripts": "^4.0.3",

View File

@ -0,0 +1,17 @@
import Head from "next/head";
import * as React from "react";
import { DEFAULT_APP_METADATA } from "../constants";
const Metadata = () => (
<Head>
<title>{DEFAULT_APP_METADATA.name}</title>
<meta name="description" content={DEFAULT_APP_METADATA.description} />
<meta name="url" content={DEFAULT_APP_METADATA.url} />
{DEFAULT_APP_METADATA.icons.map((icon, index) => (
<link key={index} rel="icon" href={icon} />
))}
</Head>
);
export default Metadata;

View File

@ -28,7 +28,7 @@ export const DEFAULT_EIP155_METHODS = ["eth_sendTransaction", "personal_sign", "
export const DEFAULT_LOGGER = "debug"; export const DEFAULT_LOGGER = "debug";
export const DEFAULT_APP_METADATA = { export const DEFAULT_APP_METADATA = {
name: "React App", name: "React App with web3",
description: "React App for WalletConnect", description: "React App for WalletConnect",
url: "https://walletconnect.com/", url: "https://walletconnect.com/",
icons: ["https://avatars.githubusercontent.com/u/37784886"], icons: ["https://avatars.githubusercontent.com/u/37784886"],

View File

@ -8,7 +8,7 @@ import {
useState, useState,
} from "react"; } from "react";
import QRCodeModal from "@walletconnect/qrcode-modal"; import { Web3Modal } from "@web3modal/standalone";
import { apiGetChainNamespace, ChainsMap } from "caip-api"; import { apiGetChainNamespace, ChainsMap } from "caip-api";
import UniversalProvider from "@walletconnect/universal-provider"; import UniversalProvider from "@walletconnect/universal-provider";
import Client from "@walletconnect/sign-client"; import Client from "@walletconnect/sign-client";
@ -59,6 +59,7 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
const [accounts, setAccounts] = useState<string[]>([]); const [accounts, setAccounts] = useState<string[]>([]);
const [chainData, setChainData] = useState<ChainNamespaces>({}); const [chainData, setChainData] = useState<ChainNamespaces>({});
const [chain, setChain] = useState<string>(""); const [chain, setChain] = useState<string>("");
const [web3Modal, setWeb3Modal] = useState<Web3Modal>();
const resetApp = () => { const resetApp = () => {
setPairings([]); setPairings([]);
@ -95,46 +96,47 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
resetApp(); resetApp();
}, [ethereumProvider]); }, [ethereumProvider]);
const _subscribeToProviderEvents = useCallback(async (_client: UniversalProvider) => { const _subscribeToProviderEvents = useCallback(
if (typeof _client === "undefined") { async (_client: UniversalProvider) => {
throw new Error("WalletConnect is not initialized"); if (typeof _client === "undefined") {
} throw new Error("WalletConnect is not initialized");
}
_client.on("display_uri", async (uri: string) => { _client.on("display_uri", async (uri: string) => {
console.log("EVENT", "QR Code Modal open"); console.log("EVENT", "QR Code Modal open");
QRCodeModal.open(uri, () => { web3Modal?.openModal({ uri });
console.log("EVENT", "QR Code Modal closed");
}); });
});
// Subscribe to session ping // Subscribe to session ping
_client.on("session_ping", ({ id, topic }: { id: number; topic: string }) => { _client.on("session_ping", ({ id, topic }: { id: number; topic: string }) => {
console.log("EVENT", "session_ping"); console.log("EVENT", "session_ping");
console.log(id, topic); console.log(id, topic);
}); });
// Subscribe to session event // Subscribe to session event
_client.on("session_event", ({ event, chainId }: { event: any; chainId: string }) => { _client.on("session_event", ({ event, chainId }: { event: any; chainId: string }) => {
console.log("EVENT", "session_event"); console.log("EVENT", "session_event");
console.log(event, chainId); console.log(event, chainId);
}); });
// Subscribe to session update // Subscribe to session update
_client.on( _client.on(
"session_update", "session_update",
({ topic, session }: { topic: string; session: SessionTypes.Struct }) => { ({ topic, session }: { topic: string; session: SessionTypes.Struct }) => {
console.log("EVENT", "session_updated"); console.log("EVENT", "session_updated");
setSession(session); setSession(session);
}, },
); );
// Subscribe to session delete // Subscribe to session delete
_client.on("session_delete", ({ id, topic }: { id: number; topic: string }) => { _client.on("session_delete", ({ id, topic }: { id: number; topic: string }) => {
console.log("EVENT", "session_deleted"); console.log("EVENT", "session_deleted");
console.log(id, topic); console.log(id, topic);
resetApp(); resetApp();
}); });
}, []); },
[web3Modal],
);
const createClient = useCallback(async () => { const createClient = useCallback(async () => {
try { try {
@ -146,15 +148,19 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
relayUrl: DEFAULT_RELAY_URL, relayUrl: DEFAULT_RELAY_URL,
}); });
const web3Modal = new Web3Modal({
projectId: DEFAULT_PROJECT_ID,
});
setEthereumProvider(provider); setEthereumProvider(provider);
setClient(provider.client); setClient(provider.client);
await _subscribeToProviderEvents(provider); setWeb3Modal(web3Modal);
} catch (err) { } catch (err) {
throw err; throw err;
} finally { } finally {
setIsInitializing(false); setIsInitializing(false);
} }
}, [_subscribeToProviderEvents]); }, []);
const createWeb3Provider = useCallback((ethereumProvider: UniversalProvider) => { const createWeb3Provider = useCallback((ethereumProvider: UniversalProvider) => {
const web3Provider = new Web3(ethereumProvider); const web3Provider = new Web3(ethereumProvider);
@ -204,9 +210,9 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
setSession(session); setSession(session);
setChain(caipChainId); setChain(caipChainId);
QRCodeModal.close(); web3Modal?.closeModal();
}, },
[ethereumProvider, chainData.eip155, createWeb3Provider], [ethereumProvider, chainData.eip155, createWeb3Provider, web3Modal],
); );
const onSessionConnected = useCallback( const onSessionConnected = useCallback(
@ -262,6 +268,10 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
} }
}, [client, createClient]); }, [client, createClient]);
useEffect(() => {
if (ethereumProvider && web3Modal) _subscribeToProviderEvents(ethereumProvider);
}, [_subscribeToProviderEvents, ethereumProvider, web3Modal]);
useEffect(() => { useEffect(() => {
const fetchBalances = async () => { const fetchBalances = async () => {
if (!web3Provider || !accounts) return; if (!web3Provider || !accounts) return;

View File

@ -1,5 +1,6 @@
import type { AppProps } from "next/app"; import type { AppProps } from "next/app";
import { createGlobalStyle } from "styled-components"; import { createGlobalStyle } from "styled-components";
import Metadata from "../components/Metadata";
import { ClientContextProvider } from "../contexts/ClientContext"; import { ClientContextProvider } from "../contexts/ClientContext";
@ -11,6 +12,7 @@ const GlobalStyle = createGlobalStyle`
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
return ( return (
<> <>
<Metadata />
<GlobalStyle /> <GlobalStyle />
<ClientContextProvider> <ClientContextProvider>
<Component {...pageProps} /> <Component {...pageProps} />

View File

@ -0,0 +1,17 @@
import Head from "next/head";
import * as React from "react";
import { DEFAULT_APP_METADATA } from "../constants";
const Metadata = () => (
<Head>
<title>{DEFAULT_APP_METADATA.name}</title>
<meta name="description" content={DEFAULT_APP_METADATA.description} />
<meta name="url" content={DEFAULT_APP_METADATA.url} />
{DEFAULT_APP_METADATA.icons.map((icon, index) => (
<link key={index} rel="icon" href={icon} />
))}
</Head>
);
export default Metadata;

View File

@ -5,6 +5,7 @@ import { createGlobalStyle } from "styled-components";
import { ClientContextProvider } from "../contexts/ClientContext"; import { ClientContextProvider } from "../contexts/ClientContext";
import { JsonRpcContextProvider } from "../contexts/JsonRpcContext"; import { JsonRpcContextProvider } from "../contexts/JsonRpcContext";
import { ChainDataContextProvider } from "../contexts/ChainDataContext"; import { ChainDataContextProvider } from "../contexts/ChainDataContext";
import Metadata from "../components/Metadata";
import { globalStyle } from "../styles"; import { globalStyle } from "../styles";
const GlobalStyle = createGlobalStyle` const GlobalStyle = createGlobalStyle`
@ -14,6 +15,7 @@ const GlobalStyle = createGlobalStyle`
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
return ( return (
<> <>
<Metadata />
<GlobalStyle /> <GlobalStyle />
<ChainDataContextProvider> <ChainDataContextProvider>
<ClientContextProvider> <ClientContextProvider>