feat: examples dapps update (#72)
* feat: updates example dapp with ethers to use `universal-provider` * feat: updates example dapp with web3 to use `universal-provider` * fix: sets `paramsString` and `address` to lower case when searching for match * chore: yarn lock * refactor: rm `outdated` warning * refactor: standardize event logging * refactor: rm logs * refactor: adds `next` to `ethers` dapp * chore: rm old files * refactor: adds next to web3 example * chore: test deploy vercel * chore: remove console log * chore: to trigger deployment * Revert "chore: to trigger deployment" This reverts commit bc7712780a101f0152c20ce4eca4d8d117d8eaa7. * feat: updates readmes Co-authored-by: crypblizz <45455218+crypblizz8@users.noreply.github.com>
This commit is contained in:
parent
7721842601
commit
49920d0453
@ -1,4 +1,3 @@
|
||||
REACT_APP_PROJECT_ID=39bc93c...
|
||||
REACT_APP_INFURA_ID=5dc0df...
|
||||
REACT_APP_RELAY_URL=wss://relay.walletconnect.com
|
||||
NEXT_PUBLIC_PROJECT_ID=39bc93c...
|
||||
NEXT_PUBLIC_RELAY_URL=wss://relay.walletconnect.com
|
||||
|
||||
|
@ -1,9 +1,4 @@
|
||||
# React dApp (with v2 EthereumProvider + Ethers.js)
|
||||
|
||||
> **⚠️ This provider-based example is currently reliant on the deprecated `2.0.0-beta.26` SDK. ⚠️**
|
||||
>
|
||||
> This example will be updated to be compatible with the latest v2 SDK (`2.0.0-beta.100+`) in due time.
|
||||
> In the meantime, please use the up-to-date [standalone client example](../react-dapp-v2/).
|
||||
# React dApp (with v2 UniversalProvider + Ethers.js)
|
||||
|
||||
🔗 Live dapp demo - https://react-dapp-v2-with-ethers.vercel.app <br />
|
||||
🔗 Live wallet demo - https://react-wallet.walletconnect.com/ <br />
|
||||
@ -11,7 +6,7 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This is an example implementation of a React dApp (generated via `create-react-app`) using the v2 [`EthereumProvider`](https://docs.walletconnect.com/2.0/quick-start/dapps/ethereum-provider) together with [`Ethers.js`](https://docs.ethers.io/v5/) to:
|
||||
This is an example implementation of a React dApp (generated via `create-react-app`) using the v2 [`UniversalProvider`](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/providers/universal-provider) together with [`Ethers.js`](https://docs.ethers.io/v5/) to:
|
||||
|
||||
- handle pairings
|
||||
- manage sessions
|
||||
@ -33,9 +28,8 @@ cp .env.local.example .env.local
|
||||
|
||||
Your `.env.local` now contains the following environment variables:
|
||||
|
||||
- `REACT_APP_PROJECT_ID` (placeholder) - You can generate your own ProjectId at https://cloud.walletconnect.com
|
||||
- `REACT_APP_INFURA_ID` (placeholder) - You can generate your own Infura ID via https://infura.io/
|
||||
- `REACT_APP_RELAY_URL` (already set)
|
||||
- `NEXT_PUBLIC_PROJECT_ID` (placeholder) - You can generate your own ProjectId at https://cloud.walletconnect.com
|
||||
- `NEXT_PUBLIC_RELAY_URL` (already set)
|
||||
|
||||
## Develop
|
||||
|
||||
|
5
dapps/react-dapp-v2-with-ethers/next-env.d.ts
vendored
Normal file
5
dapps/react-dapp-v2-with-ethers/next-env.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
16
dapps/react-dapp-v2-with-ethers/next.config.js
Normal file
16
dapps/react-dapp-v2-with-ethers/next.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
distDir: "build",
|
||||
webpack(config) {
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback,
|
||||
fs: false,
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
@ -11,10 +11,10 @@
|
||||
"author": "WalletConnect, Inc. <walletconnect.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"prettier": "prettier --check '**/*.{js,ts,jsx,tsx}'"
|
||||
},
|
||||
"repository": {
|
||||
@ -29,11 +29,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ethereumjs/tx": "^3.5.0",
|
||||
"@walletconnect/client": "2.0.0-beta.26",
|
||||
"@walletconnect/ethereum-provider": "2.0.0-beta.26",
|
||||
"@walletconnect/encoding": "^1.0.1",
|
||||
"@walletconnect/qrcode-modal": "^1.7.1",
|
||||
"@walletconnect/types": "2.0.0-beta.26",
|
||||
"@walletconnect/utils": "2.0.0-beta.26",
|
||||
"@walletconnect/types": "^2.1.1",
|
||||
"@walletconnect/universal-provider": "^2.1.1",
|
||||
"@walletconnect/utils": "^2.1.1",
|
||||
"axios": "^0.21.1",
|
||||
"blockies-ts": "^1.0.0",
|
||||
"caip-api": "^2.0.0-beta.1",
|
||||
@ -43,6 +43,7 @@
|
||||
"ethers": "^5.3.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"qr-image": "^3.2.0",
|
||||
"next": "12.2.4",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "^4.0.3",
|
||||
@ -51,6 +52,7 @@
|
||||
"web-vitals": "^0.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.19.4",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
@ -61,8 +63,8 @@
|
||||
"@types/pino": "^7.0.5",
|
||||
"@types/prop-types": "^15.7.4",
|
||||
"@types/qr-image": "^3.2.5",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/react": "18.0.15",
|
||||
"@types/react-dom": "18.0.6",
|
||||
"@types/styled-components": "^5.1.21",
|
||||
"prettier": "^2.5.1"
|
||||
},
|
||||
@ -72,16 +74,9 @@
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@ -1,6 +1,5 @@
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import logo from "../assets/walletconnect.png";
|
||||
|
||||
const SBannerWrapper = styled.div`
|
||||
display: flex;
|
||||
@ -11,7 +10,7 @@ const SBannerWrapper = styled.div`
|
||||
const SBanner = styled.div`
|
||||
width: 275px;
|
||||
height: 45px;
|
||||
background: url(${logo}) no-repeat;
|
||||
background: url(/assets/walletconnect.png) no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
`;
|
||||
|
@ -50,7 +50,7 @@ const SActiveSession = styled(SActiveAccount as any)`
|
||||
interface HeaderProps {
|
||||
ping: () => Promise<void>;
|
||||
disconnect: () => Promise<void>;
|
||||
session: SessionTypes.Created | undefined;
|
||||
session: SessionTypes.Struct | undefined;
|
||||
}
|
||||
|
||||
const Header = (props: HeaderProps) => {
|
||||
|
@ -6,7 +6,7 @@ import { PairingTypes } from "@walletconnect/types";
|
||||
import Peer from "./Peer";
|
||||
|
||||
interface PairingProps {
|
||||
pairing: PairingTypes.Settled;
|
||||
pairing: PairingTypes.Struct;
|
||||
onClick?: any;
|
||||
}
|
||||
|
||||
@ -16,14 +16,12 @@ const SPairingContainer = styled.div`
|
||||
`;
|
||||
|
||||
const Pairing = (props: PairingProps) => {
|
||||
const {
|
||||
state: { metadata },
|
||||
} = props.pairing;
|
||||
const { peerMetadata } = props.pairing;
|
||||
return (
|
||||
<SPairingContainer onClick={props.onClick}>
|
||||
<div>
|
||||
{typeof metadata !== "undefined" ? (
|
||||
<Peer oneLiner metadata={metadata} />
|
||||
{typeof peerMetadata !== "undefined" ? (
|
||||
<Peer oneLiner metadata={peerMetadata} />
|
||||
) : (
|
||||
<div>{`Unknown`}</div>
|
||||
)}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { CoreTypes } from "@walletconnect/types";
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import { AppMetadata } from "@walletconnect/types";
|
||||
import { colors, fonts } from "../styles";
|
||||
|
||||
const SPeerOneLiner = styled.div`
|
||||
@ -53,7 +53,7 @@ const SName = styled(SCenter as any)`
|
||||
|
||||
interface PeerProps {
|
||||
oneLiner?: boolean;
|
||||
metadata: AppMetadata;
|
||||
metadata: CoreTypes.Metadata;
|
||||
}
|
||||
|
||||
const Peer = (props: PeerProps) =>
|
||||
|
@ -19,14 +19,17 @@ export const DEFAULT_TEST_CHAINS = [
|
||||
|
||||
export const DEFAULT_CHAINS = [...DEFAULT_MAIN_CHAINS, ...DEFAULT_TEST_CHAINS];
|
||||
|
||||
export const DEFAULT_PROJECT_ID = process.env.REACT_APP_PROJECT_ID;
|
||||
export const DEFAULT_PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID;
|
||||
|
||||
export const DEFAULT_INFURA_ID = process.env.REACT_APP_INFURA_ID;
|
||||
|
||||
export const DEFAULT_RELAY_URL = process.env.REACT_APP_RELAY_URL;
|
||||
export const DEFAULT_RELAY_URL = process.env.NEXT_PUBLIC_RELAY_URL;
|
||||
|
||||
export const DEFAULT_EIP155_METHODS = ["eth_sendTransaction", "personal_sign", "eth_signTypedData"];
|
||||
|
||||
export enum DEFAULT_EIP_155_EVENTS {
|
||||
ETH_CHAIN_CHANGED = "chainChanged",
|
||||
ETH_ACCOUNTS_CHANGED = "accountsChanged",
|
||||
}
|
||||
|
||||
export const DEFAULT_LOGGER = "debug";
|
||||
|
||||
export const DEFAULT_APP_METADATA = {
|
||||
|
@ -1,7 +1,3 @@
|
||||
import Client, { CLIENT_EVENTS } from "@walletconnect/client";
|
||||
import EthereumProvider from "@walletconnect/ethereum-provider";
|
||||
import { PairingTypes, SessionTypes } from "@walletconnect/types";
|
||||
import QRCodeModal from "@walletconnect/qrcode-modal";
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
@ -11,31 +7,31 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
DEFAULT_INFURA_ID,
|
||||
DEFAULT_LOGGER,
|
||||
DEFAULT_PROJECT_ID,
|
||||
DEFAULT_RELAY_URL,
|
||||
} from "../constants";
|
||||
|
||||
import QRCodeModal from "@walletconnect/qrcode-modal";
|
||||
import { apiGetChainNamespace, ChainsMap } from "caip-api";
|
||||
import UniversalProvider from "@walletconnect/universal-provider";
|
||||
import { PairingTypes, SessionTypes } from "@walletconnect/types";
|
||||
import Client from "@walletconnect/sign-client";
|
||||
|
||||
import { DEFAULT_LOGGER, DEFAULT_PROJECT_ID, DEFAULT_RELAY_URL } from "../constants";
|
||||
import { providers, utils } from "ethers";
|
||||
import { AccountBalances, ChainNamespaces, getAllChainNamespaces } from "../helpers";
|
||||
import { apiGetChainNamespace, ChainsMap } from "caip-api";
|
||||
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
interface IContext {
|
||||
client: Client | undefined;
|
||||
session: SessionTypes.Created | undefined;
|
||||
session: SessionTypes.Struct | undefined;
|
||||
connect: (caipChainId: string, pairing?: { topic: string }) => Promise<void>;
|
||||
disconnect: () => Promise<void>;
|
||||
isInitializing: boolean;
|
||||
chain: string;
|
||||
pairings: string[];
|
||||
pairings: PairingTypes.Struct[];
|
||||
accounts: string[];
|
||||
balances: AccountBalances;
|
||||
isFetchingBalances: boolean;
|
||||
chainData: ChainNamespaces;
|
||||
onEnable: (chainId: string) => Promise<void>;
|
||||
web3Provider?: providers.Web3Provider;
|
||||
}
|
||||
|
||||
@ -44,15 +40,18 @@ interface IContext {
|
||||
*/
|
||||
export const ClientContext = createContext<IContext>({} as IContext);
|
||||
|
||||
/**
|
||||
* Provider
|
||||
*/
|
||||
/**
|
||||
* Provider
|
||||
*/
|
||||
export function ClientContextProvider({ children }: { children: ReactNode | ReactNode[] }) {
|
||||
const [client, setClient] = useState<Client>();
|
||||
const [pairings, setPairings] = useState<string[]>([]);
|
||||
const [session, setSession] = useState<SessionTypes.Created>();
|
||||
const [pairings, setPairings] = useState<PairingTypes.Struct[]>([]);
|
||||
const [session, setSession] = useState<SessionTypes.Struct>();
|
||||
|
||||
const [ethereumProvider, setEthereumProvider] = useState<EthereumProvider>();
|
||||
const [ethereumProvider, setEthereumProvider] = useState<UniversalProvider>();
|
||||
const [web3Provider, setWeb3Provider] = useState<providers.Web3Provider>();
|
||||
|
||||
const [isFetchingBalances, setIsFetchingBalances] = useState(false);
|
||||
@ -96,33 +95,46 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
throw new Error("ethereumProvider is not initialized");
|
||||
}
|
||||
await ethereumProvider.disconnect();
|
||||
resetApp();
|
||||
}, [ethereumProvider]);
|
||||
|
||||
const _subscribeToClientEvents = useCallback(async (_client: Client) => {
|
||||
const _subscribeToProviderEvents = useCallback(async (_client: UniversalProvider) => {
|
||||
if (typeof _client === "undefined") {
|
||||
throw new Error("WalletConnect is not initialized");
|
||||
}
|
||||
|
||||
_client.on(CLIENT_EVENTS.pairing.proposal, async (proposal: PairingTypes.Proposal) => {
|
||||
const { uri } = proposal.signal.params;
|
||||
_client.on("display_uri", async (uri: string) => {
|
||||
console.log("EVENT", "QR Code Modal open");
|
||||
QRCodeModal.open(uri, () => {
|
||||
console.log("EVENT", "QR Code Modal closed");
|
||||
});
|
||||
});
|
||||
|
||||
_client.on(CLIENT_EVENTS.pairing.created, async () => {
|
||||
setPairings(_client.pairing.topics);
|
||||
// Subscribe to session ping
|
||||
_client.on("session_ping", ({ id, topic }: { id: number; topic: string }) => {
|
||||
console.log("EVENT", "session_ping");
|
||||
console.log(id, topic);
|
||||
});
|
||||
|
||||
_client.on(CLIENT_EVENTS.session.updated, (updatedSession: SessionTypes.Settled) => {
|
||||
console.log("EVENT", "session_updated");
|
||||
setAccounts(updatedSession.state.accounts);
|
||||
setSession(updatedSession);
|
||||
// Subscribe to session event
|
||||
_client.on("session_event", ({ event, chainId }: { event: any; chainId: string }) => {
|
||||
console.log("EVENT", "session_event");
|
||||
console.log(event, chainId);
|
||||
});
|
||||
|
||||
_client.on(CLIENT_EVENTS.session.deleted, () => {
|
||||
// Subscribe to session update
|
||||
_client.on(
|
||||
"session_update",
|
||||
({ topic, session }: { topic: string; session: SessionTypes.Struct }) => {
|
||||
console.log("EVENT", "session_updated");
|
||||
setSession(session);
|
||||
},
|
||||
);
|
||||
|
||||
// Subscribe to session delete
|
||||
_client.on("session_delete", ({ id, topic }: { id: number; topic: string }) => {
|
||||
console.log("EVENT", "session_deleted");
|
||||
console.log(id, topic);
|
||||
resetApp();
|
||||
});
|
||||
}, []);
|
||||
@ -131,24 +143,30 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
try {
|
||||
setIsInitializing(true);
|
||||
|
||||
const _client = await Client.init({
|
||||
const provider = await UniversalProvider.init({
|
||||
projectId: DEFAULT_PROJECT_ID,
|
||||
logger: DEFAULT_LOGGER,
|
||||
relayUrl: DEFAULT_RELAY_URL,
|
||||
projectId: DEFAULT_PROJECT_ID,
|
||||
});
|
||||
|
||||
setClient(_client);
|
||||
await _subscribeToClientEvents(_client);
|
||||
setEthereumProvider(provider);
|
||||
setClient(provider.client);
|
||||
await _subscribeToProviderEvents(provider);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
setIsInitializing(false);
|
||||
}
|
||||
}, [_subscribeToClientEvents]);
|
||||
}, [_subscribeToProviderEvents]);
|
||||
|
||||
const onEnable = useCallback(
|
||||
async (caipChainId: string) => {
|
||||
if (!client) {
|
||||
const createWeb3Provider = useCallback((ethereumProvider: UniversalProvider) => {
|
||||
const web3Provider = new providers.Web3Provider(ethereumProvider);
|
||||
setWeb3Provider(web3Provider);
|
||||
}, []);
|
||||
|
||||
const connect = useCallback(
|
||||
async (caipChainId: string, pairing?: { topic: string }) => {
|
||||
if (!ethereumProvider) {
|
||||
throw new ReferenceError("WalletConnect Client is not initialized.");
|
||||
}
|
||||
|
||||
@ -164,33 +182,97 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
{},
|
||||
);
|
||||
|
||||
// Create WalletConnect Provider
|
||||
const ethereumProvider = new EthereumProvider({
|
||||
chainId: Number(chainId),
|
||||
rpc: {
|
||||
infuraId: DEFAULT_INFURA_ID,
|
||||
custom: customRpcs,
|
||||
const session = await ethereumProvider.connect({
|
||||
namespaces: {
|
||||
eip155: {
|
||||
methods: [
|
||||
"eth_sendTransaction",
|
||||
"eth_signTransaction",
|
||||
"eth_sign",
|
||||
"personal_sign",
|
||||
"eth_signTypedData",
|
||||
],
|
||||
chains: [`eip155:${chainId}`],
|
||||
events: ["chainChanged", "accountsChanged"],
|
||||
rpcMap: customRpcs,
|
||||
},
|
||||
},
|
||||
client,
|
||||
pairingTopic: pairing?.topic,
|
||||
});
|
||||
const web3Provider = new providers.Web3Provider(ethereumProvider);
|
||||
|
||||
console.log(ethereumProvider);
|
||||
|
||||
setEthereumProvider(ethereumProvider);
|
||||
setWeb3Provider(web3Provider);
|
||||
|
||||
createWeb3Provider(ethereumProvider);
|
||||
const _accounts = await ethereumProvider.enable();
|
||||
const _session = await client.session.get(client.session.topics[0]);
|
||||
|
||||
console.log("_accounts", _accounts);
|
||||
setAccounts(_accounts);
|
||||
setSession(_session);
|
||||
setSession(session);
|
||||
setChain(caipChainId);
|
||||
|
||||
QRCodeModal.close();
|
||||
},
|
||||
[ethereumProvider, chainData.eip155, createWeb3Provider],
|
||||
);
|
||||
|
||||
const onSessionConnected = useCallback(
|
||||
async (_session: SessionTypes.Struct) => {
|
||||
if (!ethereumProvider) {
|
||||
throw new ReferenceError("EthereumProvider is not initialized.");
|
||||
}
|
||||
const allNamespaceAccounts = Object.values(_session.namespaces)
|
||||
.map(namespace => namespace.accounts)
|
||||
.flat();
|
||||
const allNamespaceChains = Object.keys(_session.namespaces);
|
||||
|
||||
const chainData = allNamespaceAccounts[0].split(":");
|
||||
const caipChainId = `${chainData[0]}:${chainData[1]}`;
|
||||
console.log("restored caipChainId", caipChainId);
|
||||
setChain(caipChainId);
|
||||
setSession(_session);
|
||||
setAccounts(allNamespaceAccounts.map(account => account.split(":")[2]));
|
||||
console.log("RESTORED", allNamespaceChains, allNamespaceAccounts);
|
||||
createWeb3Provider(ethereumProvider);
|
||||
},
|
||||
[ethereumProvider, createWeb3Provider],
|
||||
);
|
||||
|
||||
const _checkForPersistedSession = useCallback(
|
||||
async (provider: UniversalProvider) => {
|
||||
if (typeof provider === "undefined") {
|
||||
throw new Error("WalletConnect is not initialized");
|
||||
}
|
||||
const pairings = provider.client.pairing.getAll({ active: true });
|
||||
// populates existing pairings to state
|
||||
setPairings(pairings);
|
||||
console.log("RESTORED PAIRINGS: ", pairings);
|
||||
if (typeof session !== "undefined") return;
|
||||
// populates (the last) existing session to state
|
||||
if (ethereumProvider?.session) {
|
||||
const _session = ethereumProvider?.session;
|
||||
console.log("RESTORED SESSION:", _session);
|
||||
await onSessionConnected(_session);
|
||||
return _session;
|
||||
}
|
||||
},
|
||||
[session, ethereumProvider, onSessionConnected],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadChainData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!client) {
|
||||
createClient();
|
||||
}
|
||||
}, [client, createClient]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchBalances = async () => {
|
||||
if (!web3Provider || !accounts) return;
|
||||
|
||||
try {
|
||||
setIsFetchingBalances(true);
|
||||
const _balances = await Promise.all(
|
||||
_accounts.map(async account => {
|
||||
accounts.map(async account => {
|
||||
const balance = await web3Provider.getBalance(account);
|
||||
return {
|
||||
account,
|
||||
@ -212,51 +294,22 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
} finally {
|
||||
setIsFetchingBalances(false);
|
||||
}
|
||||
};
|
||||
|
||||
QRCodeModal.close();
|
||||
},
|
||||
[client, chainData.eip155],
|
||||
);
|
||||
|
||||
const _checkForPersistedSession = useCallback(
|
||||
async (_client: Client) => {
|
||||
if (typeof _client === "undefined") {
|
||||
throw new Error("WalletConnect is not initialized");
|
||||
}
|
||||
// populates existing pairings to state
|
||||
setPairings(_client.pairing.topics);
|
||||
if (typeof session !== "undefined") return;
|
||||
// populates existing session to state (assume only the top one)
|
||||
if (_client.session.topics.length) {
|
||||
const _session = await _client.session.get(_client.session.topics[0]);
|
||||
const [namespace, chainId] = _session.state.accounts[0].split(":");
|
||||
const caipChainId = `${namespace}:${chainId}`;
|
||||
onEnable(caipChainId);
|
||||
}
|
||||
},
|
||||
[session, onEnable],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadChainData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!client) {
|
||||
createClient();
|
||||
}
|
||||
}, [client, createClient]);
|
||||
fetchBalances();
|
||||
}, [web3Provider, accounts]);
|
||||
|
||||
useEffect(() => {
|
||||
const getPersistedSession = async () => {
|
||||
if (client && !hasCheckedPersistedSession) {
|
||||
await _checkForPersistedSession(client);
|
||||
setHasCheckedPersistedSession(true);
|
||||
}
|
||||
if (!ethereumProvider) return;
|
||||
await _checkForPersistedSession(ethereumProvider);
|
||||
setHasCheckedPersistedSession(true);
|
||||
};
|
||||
|
||||
getPersistedSession();
|
||||
}, [client, _checkForPersistedSession, hasCheckedPersistedSession]);
|
||||
if (ethereumProvider && chainData && !hasCheckedPersistedSession) {
|
||||
getPersistedSession();
|
||||
}
|
||||
}, [ethereumProvider, chainData, _checkForPersistedSession, hasCheckedPersistedSession]);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
@ -269,8 +322,8 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
client,
|
||||
session,
|
||||
disconnect,
|
||||
connect,
|
||||
chainData,
|
||||
onEnable,
|
||||
web3Provider,
|
||||
}),
|
||||
[
|
||||
@ -283,8 +336,8 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
client,
|
||||
session,
|
||||
disconnect,
|
||||
connect,
|
||||
chainData,
|
||||
onEnable,
|
||||
web3Provider,
|
||||
],
|
||||
);
|
||||
|
@ -190,6 +190,7 @@ export function setLocaleStorageTestnetFlag(value: boolean): void {
|
||||
}
|
||||
|
||||
export function getLocalStorageTestnetFlag(): boolean {
|
||||
if (typeof window === "undefined") return false;
|
||||
let value = INITIAL_STATE_TESTNET_DEFAULT;
|
||||
const persisted = window.localStorage.getItem(LOCALSTORAGE_KEY_TESTNET);
|
||||
if (!persisted) {
|
||||
|
@ -1,27 +0,0 @@
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import { createGlobalStyle } from "styled-components";
|
||||
import { ClientContextProvider } from "./contexts/ClientContext";
|
||||
|
||||
import App from "./App";
|
||||
import { globalStyle } from "./styles";
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
${globalStyle}
|
||||
`;
|
||||
|
||||
declare global {
|
||||
// tslint:disable-next-line
|
||||
interface Window {
|
||||
blockies: any;
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<ClientContextProvider>
|
||||
<App />
|
||||
</ClientContextProvider>
|
||||
</>,
|
||||
document.getElementById("root"),
|
||||
);
|
@ -9,7 +9,7 @@ import { STable } from "../components/shared";
|
||||
import { SModalContainer, SModalTitle } from "./shared";
|
||||
|
||||
interface PairingModalProps {
|
||||
pairings: PairingTypes.Settled[];
|
||||
pairings: PairingTypes.Struct[];
|
||||
connect: (pairing?: { topic: string }) => Promise<void>;
|
||||
}
|
||||
|
||||
|
3
dapps/react-dapp-v2-with-ethers/src/pages/404.tsx
Normal file
3
dapps/react-dapp-v2-with-ethers/src/pages/404.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function FourOhFour() {
|
||||
return <h1>404 Page Not Found</h1>;
|
||||
}
|
22
dapps/react-dapp-v2-with-ethers/src/pages/_app.tsx
Normal file
22
dapps/react-dapp-v2-with-ethers/src/pages/_app.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import type { AppProps } from "next/app";
|
||||
import { createGlobalStyle } from "styled-components";
|
||||
|
||||
import { ClientContextProvider } from "../contexts/ClientContext";
|
||||
|
||||
import { globalStyle } from "../styles";
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
${globalStyle}
|
||||
`;
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<ClientContextProvider>
|
||||
<Component {...pageProps} />
|
||||
</ClientContextProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyApp;
|
3
dapps/react-dapp-v2-with-ethers/src/pages/_error.tsx
Normal file
3
dapps/react-dapp-v2-with-ethers/src/pages/_error.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function Error() {
|
||||
return <div>An error as occured</div>;
|
||||
}
|
@ -1,26 +1,27 @@
|
||||
import type { NextPage } from "next";
|
||||
import React, { useState } from "react";
|
||||
import { version } from "@walletconnect/client/package.json";
|
||||
import { version } from "@walletconnect/universal-provider/package.json";
|
||||
import * as encoding from "@walletconnect/encoding";
|
||||
import { BigNumber, utils } from "ethers";
|
||||
import { TypedDataField } from "@ethersproject/abstract-signer";
|
||||
import { Transaction } from "@ethereumjs/tx";
|
||||
|
||||
import Banner from "./components/Banner";
|
||||
import Blockchain from "./components/Blockchain";
|
||||
import Column from "./components/Column";
|
||||
import Header from "./components/Header";
|
||||
import Modal from "./components/Modal";
|
||||
import { DEFAULT_MAIN_CHAINS, DEFAULT_TEST_CHAINS } from "./constants";
|
||||
import Banner from "./../components/Banner";
|
||||
import Blockchain from "./../components/Blockchain";
|
||||
import Column from "./../components/Column";
|
||||
import Header from "./../components/Header";
|
||||
import Modal from "./../components/Modal";
|
||||
import { DEFAULT_MAIN_CHAINS, DEFAULT_TEST_CHAINS } from "./../constants";
|
||||
import {
|
||||
AccountAction,
|
||||
eip712,
|
||||
formatTestTransaction,
|
||||
getLocalStorageTestnetFlag,
|
||||
setLocaleStorageTestnetFlag,
|
||||
} from "./helpers";
|
||||
import Toggle from "./components/Toggle";
|
||||
import RequestModal from "./modals/RequestModal";
|
||||
import PingModal from "./modals/PingModal";
|
||||
} from "./../helpers";
|
||||
import Toggle from "./../components/Toggle";
|
||||
import RequestModal from "./../modals/RequestModal";
|
||||
import PingModal from "./../modals/PingModal";
|
||||
import {
|
||||
SAccounts,
|
||||
SAccountsContainer,
|
||||
@ -29,8 +30,8 @@ import {
|
||||
SLanding,
|
||||
SLayout,
|
||||
SToggleContainer,
|
||||
} from "./components/app";
|
||||
import { useWalletConnectClient } from "./contexts/ClientContext";
|
||||
} from "./../components/app";
|
||||
import { useWalletConnectClient } from "./../contexts/ClientContext";
|
||||
|
||||
interface IFormattedRpcResponse {
|
||||
method: string;
|
||||
@ -39,7 +40,7 @@ interface IFormattedRpcResponse {
|
||||
result: string;
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const Home: NextPage = () => {
|
||||
const [isTestnet, setIsTestnet] = useState(getLocalStorageTestnetFlag());
|
||||
const [isRpcRequestPending, setIsRpcRequestPending] = useState(false);
|
||||
const [rpcResult, setRpcResult] = useState<IFormattedRpcResponse | null>();
|
||||
@ -61,7 +62,7 @@ export default function App() {
|
||||
chainData,
|
||||
isFetchingBalances,
|
||||
isInitializing,
|
||||
onEnable,
|
||||
connect,
|
||||
web3Provider,
|
||||
} = useWalletConnectClient();
|
||||
|
||||
@ -73,10 +74,13 @@ export default function App() {
|
||||
throw new Error("WalletConnect Client is not initialized");
|
||||
}
|
||||
|
||||
if (typeof session === "undefined") {
|
||||
throw new Error("Session is not connected");
|
||||
}
|
||||
|
||||
try {
|
||||
setIsRpcRequestPending(true);
|
||||
const _session = await client.session.get(client.session.topics[0]);
|
||||
await client.session.ping(_session.topic);
|
||||
await client.ping({ topic: session.topic });
|
||||
setRpcResult({
|
||||
address: "",
|
||||
method: "ping",
|
||||
@ -274,16 +278,6 @@ export default function App() {
|
||||
<Banner />
|
||||
<h6>
|
||||
<span>{`Using v${version || "2.0.0-beta"}`}</span>
|
||||
<sup>
|
||||
(
|
||||
<a
|
||||
style={{ textDecoration: "underline" }}
|
||||
href="https://github.com/WalletConnect/web-examples/tree/main/dapps/react-dapp-v2-with-ethers"
|
||||
>
|
||||
outdated
|
||||
</a>{" "}
|
||||
⚠️)
|
||||
</sup>
|
||||
</h6>
|
||||
<SButtonContainer>
|
||||
<h6>Select an Ethereum chain:</h6>
|
||||
@ -292,7 +286,7 @@ export default function App() {
|
||||
<Toggle active={isTestnet} onClick={toggleTestnets} />
|
||||
</SToggleContainer>
|
||||
{chainOptions.map(chainId => (
|
||||
<Blockchain key={chainId} chainId={chainId} chainData={chainData} onClick={onEnable} />
|
||||
<Blockchain key={chainId} chainId={chainId} chainData={chainData} onClick={connect} />
|
||||
))}
|
||||
</SButtonContainer>
|
||||
</SLanding>
|
||||
@ -330,4 +324,6 @@ export default function App() {
|
||||
</Modal>
|
||||
</SLayout>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Home;
|
@ -1,26 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,3 @@
|
||||
REACT_APP_PROJECT_ID=39bc93...
|
||||
REACT_APP_INFURA_ID=5dc0df...
|
||||
REACT_APP_RELAY_URL=wss://relay.dev.walletconnect.com
|
||||
NEXT_PUBLIC_PROJECT_ID=39bc93c...
|
||||
NEXT_PUBLIC_RELAY_URL=wss://relay.walletconnect.com
|
||||
|
||||
|
@ -1,9 +1,4 @@
|
||||
# React dApp (with v2 EthereumProvider + web3.js)
|
||||
|
||||
> **⚠️ This provider-based example is currently reliant on the deprecated `2.0.0-beta.26` SDK. ⚠️**
|
||||
>
|
||||
> This example will be updated to be compatible with the latest v2 SDK (`2.0.0-beta.100+`) in due time.
|
||||
> In the meantime, please use the up-to-date [standalone client example](../react-dapp-v2/).
|
||||
# React dApp (with v2 UniversalProvider + web3.js)
|
||||
|
||||
🔗 Live dapp demo - https://react-dapp-v2-with-web3js.vercel.app <br />
|
||||
🔗 Live wallet demo - https://react-wallet.walletconnect.com/ <br />
|
||||
@ -11,7 +6,7 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This is an example implementation of a React dApp (generated via `create-react-app`) using the v2 [`EthereumProvider`](https://docs.walletconnect.com/2.0/quick-start/dapps/ethereum-provider) together with [`web3.js`](https://web3js.readthedocs.io/) to:
|
||||
This is an example implementation of a React dApp (generated via `create-react-app`) using the v2 [`UniversalProvider`](https://github.com/WalletConnect/walletconnect-monorepo/tree/v2.0/providers/universal-provider) together with [`web3.js`](https://web3js.readthedocs.io/) to:
|
||||
|
||||
- handle pairings
|
||||
- manage sessions
|
||||
@ -33,9 +28,8 @@ cp .env.local.example .env.local
|
||||
|
||||
Your `.env.local` now contains the following environment variables:
|
||||
|
||||
- `REACT_APP_PROJECT_ID` (placeholder) - You can generate your own ProjectId at https://cloud.walletconnect.com
|
||||
- `REACT_APP_INFURA_ID` (placeholder) - You can generate your own Infura ID via https://infura.io/
|
||||
- `REACT_APP_RELAY_URL` (already set)
|
||||
- `NEXT_PUBLIC_PROJECT_ID` (placeholder) - You can generate your own ProjectId at https://cloud.walletconnect.com
|
||||
- `NEXT_PUBLIC_RELAY_URL` (already set)
|
||||
|
||||
## Develop
|
||||
|
||||
|
5
dapps/react-dapp-v2-with-web3js/next-env.d.ts
vendored
Normal file
5
dapps/react-dapp-v2-with-web3js/next-env.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
16
dapps/react-dapp-v2-with-web3js/next.config.js
Normal file
16
dapps/react-dapp-v2-with-web3js/next.config.js
Normal file
@ -0,0 +1,16 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
distDir: "build",
|
||||
webpack(config) {
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback,
|
||||
fs: false,
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
@ -11,10 +11,10 @@
|
||||
"author": "WalletConnect, Inc. <walletconnect.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"prettier": "prettier --check '**/*.{js,ts,jsx,tsx}'"
|
||||
},
|
||||
"repository": {
|
||||
@ -29,11 +29,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ethereumjs/tx": "^3.5.0",
|
||||
"@walletconnect/client": "2.0.0-beta.26",
|
||||
"@walletconnect/ethereum-provider": "2.0.0-beta.26",
|
||||
"@walletconnect/encoding": "^1.0.1",
|
||||
"@walletconnect/universal-provider": "2.1.1",
|
||||
"@walletconnect/qrcode-modal": "^1.7.1",
|
||||
"@walletconnect/types": "2.0.0-beta.26",
|
||||
"@walletconnect/utils": "2.0.0-beta.26",
|
||||
"@walletconnect/types": "2.1.1",
|
||||
"@walletconnect/utils": "2.1.1",
|
||||
"axios": "^0.21.1",
|
||||
"blockies-ts": "^1.0.0",
|
||||
"caip-api": "^2.0.0-beta.1",
|
||||
@ -43,6 +43,7 @@
|
||||
"ethers": "^5.3.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"qr-image": "^3.2.0",
|
||||
"next": "12.2.4",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "^4.0.3",
|
||||
@ -52,6 +53,7 @@
|
||||
"web3": "^1.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.19.4",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
@ -62,8 +64,8 @@
|
||||
"@types/pino": "^7.0.5",
|
||||
"@types/prop-types": "^15.7.4",
|
||||
"@types/qr-image": "^3.2.5",
|
||||
"@types/react": "^17.0.38",
|
||||
"@types/react-dom": "^17.0.11",
|
||||
"@types/react": "18.0.15",
|
||||
"@types/react-dom": "18.0.6",
|
||||
"@types/styled-components": "^5.1.21",
|
||||
"prettier": "^2.5.1"
|
||||
},
|
||||
@ -73,16 +75,9 @@
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
"browserslist": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
]
|
||||
}
|
||||
|
BIN
dapps/react-dapp-v2-with-web3js/public/assets/walletconnect.png
Normal file
BIN
dapps/react-dapp-v2-with-web3js/public/assets/walletconnect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
@ -1,6 +1,5 @@
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import logo from "../assets/walletconnect.png";
|
||||
|
||||
const SBannerWrapper = styled.div`
|
||||
display: flex;
|
||||
@ -11,7 +10,7 @@ const SBannerWrapper = styled.div`
|
||||
const SBanner = styled.div`
|
||||
width: 275px;
|
||||
height: 45px;
|
||||
background: url(${logo}) no-repeat;
|
||||
background: url(/assets/walletconnect.png) no-repeat;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
`;
|
||||
|
@ -50,7 +50,7 @@ const SActiveSession = styled(SActiveAccount as any)`
|
||||
interface HeaderProps {
|
||||
ping: () => Promise<void>;
|
||||
disconnect: () => Promise<void>;
|
||||
session: SessionTypes.Created | undefined;
|
||||
session: SessionTypes.Struct | undefined;
|
||||
}
|
||||
|
||||
const Header = (props: HeaderProps) => {
|
||||
|
@ -6,7 +6,7 @@ import { PairingTypes } from "@walletconnect/types";
|
||||
import Peer from "./Peer";
|
||||
|
||||
interface PairingProps {
|
||||
pairing: PairingTypes.Settled;
|
||||
pairing: PairingTypes.Struct;
|
||||
onClick?: any;
|
||||
}
|
||||
|
||||
@ -16,14 +16,12 @@ const SPairingContainer = styled.div`
|
||||
`;
|
||||
|
||||
const Pairing = (props: PairingProps) => {
|
||||
const {
|
||||
state: { metadata },
|
||||
} = props.pairing;
|
||||
const { peerMetadata } = props.pairing;
|
||||
return (
|
||||
<SPairingContainer onClick={props.onClick}>
|
||||
<div>
|
||||
{typeof metadata !== "undefined" ? (
|
||||
<Peer oneLiner metadata={metadata} />
|
||||
{typeof peerMetadata !== "undefined" ? (
|
||||
<Peer oneLiner metadata={peerMetadata} />
|
||||
) : (
|
||||
<div>{`Unknown`}</div>
|
||||
)}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import styled from "styled-components";
|
||||
import { AppMetadata } from "@walletconnect/types";
|
||||
import { CoreTypes } from "@walletconnect/types";
|
||||
import { colors, fonts } from "../styles";
|
||||
|
||||
const SPeerOneLiner = styled.div`
|
||||
@ -53,7 +53,7 @@ const SName = styled(SCenter as any)`
|
||||
|
||||
interface PeerProps {
|
||||
oneLiner?: boolean;
|
||||
metadata: AppMetadata;
|
||||
metadata: CoreTypes.Metadata;
|
||||
}
|
||||
|
||||
const Peer = (props: PeerProps) =>
|
||||
|
@ -19,11 +19,9 @@ export const DEFAULT_TEST_CHAINS = [
|
||||
|
||||
export const DEFAULT_CHAINS = [...DEFAULT_MAIN_CHAINS, ...DEFAULT_TEST_CHAINS];
|
||||
|
||||
export const DEFAULT_PROJECT_ID = process.env.REACT_APP_PROJECT_ID;
|
||||
export const DEFAULT_PROJECT_ID = process.env.NEXT_PUBLIC_PROJECT_ID;
|
||||
|
||||
export const DEFAULT_INFURA_ID = process.env.REACT_APP_INFURA_ID;
|
||||
|
||||
export const DEFAULT_RELAY_URL = process.env.REACT_APP_RELAY_URL;
|
||||
export const DEFAULT_RELAY_URL = process.env.NEXT_PUBLIC_RELAY_URL;
|
||||
|
||||
export const DEFAULT_EIP155_METHODS = ["eth_sendTransaction", "personal_sign", "eth_signTypedData"];
|
||||
|
||||
|
@ -1,7 +1,3 @@
|
||||
import Client, { CLIENT_EVENTS } from "@walletconnect/client";
|
||||
import EthereumProvider from "@walletconnect/ethereum-provider";
|
||||
import { PairingTypes, SessionTypes } from "@walletconnect/types";
|
||||
import QRCodeModal from "@walletconnect/qrcode-modal";
|
||||
import {
|
||||
createContext,
|
||||
ReactNode,
|
||||
@ -11,33 +7,31 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import Web3 from "web3";
|
||||
|
||||
import QRCodeModal from "@walletconnect/qrcode-modal";
|
||||
import { apiGetChainNamespace, ChainsMap } from "caip-api";
|
||||
|
||||
import {
|
||||
DEFAULT_INFURA_ID,
|
||||
DEFAULT_LOGGER,
|
||||
DEFAULT_PROJECT_ID,
|
||||
DEFAULT_RELAY_URL,
|
||||
} from "../constants";
|
||||
import { AccountBalances, ChainNamespaces, getAllChainNamespaces } from "../helpers";
|
||||
import UniversalProvider from "@walletconnect/universal-provider";
|
||||
import Client from "@walletconnect/sign-client";
|
||||
import Web3 from "web3";
|
||||
import { DEFAULT_LOGGER, DEFAULT_PROJECT_ID, DEFAULT_RELAY_URL } from "../constants";
|
||||
import { utils } from "ethers";
|
||||
|
||||
import { AccountBalances, ChainNamespaces, getAllChainNamespaces } from "../helpers";
|
||||
import { PairingTypes, SessionTypes } from "@walletconnect/types";
|
||||
/**
|
||||
* Types
|
||||
*/
|
||||
interface IContext {
|
||||
client: Client | undefined;
|
||||
session: SessionTypes.Created | undefined;
|
||||
session: SessionTypes.Struct | undefined;
|
||||
connect: (caipChainId: string, pairing?: { topic: string }) => Promise<void>;
|
||||
disconnect: () => Promise<void>;
|
||||
isInitializing: boolean;
|
||||
chain: string;
|
||||
pairings: string[];
|
||||
pairings: PairingTypes.Struct[];
|
||||
accounts: string[];
|
||||
balances: AccountBalances;
|
||||
isFetchingBalances: boolean;
|
||||
chainData: ChainNamespaces;
|
||||
onEnable: (chainId: string) => Promise<void>;
|
||||
web3Provider?: Web3;
|
||||
}
|
||||
|
||||
@ -51,10 +45,10 @@ export const ClientContext = createContext<IContext>({} as IContext);
|
||||
*/
|
||||
export function ClientContextProvider({ children }: { children: ReactNode | ReactNode[] }) {
|
||||
const [client, setClient] = useState<Client>();
|
||||
const [pairings, setPairings] = useState<string[]>([]);
|
||||
const [session, setSession] = useState<SessionTypes.Created>();
|
||||
const [pairings, setPairings] = useState<PairingTypes.Struct[]>([]);
|
||||
const [session, setSession] = useState<SessionTypes.Struct>();
|
||||
|
||||
const [ethereumProvider, setEthereumProvider] = useState<EthereumProvider>();
|
||||
const [ethereumProvider, setEthereumProvider] = useState<UniversalProvider>();
|
||||
const [web3Provider, setWeb3Provider] = useState<Web3>();
|
||||
|
||||
const [isFetchingBalances, setIsFetchingBalances] = useState(false);
|
||||
@ -98,33 +92,46 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
throw new Error("ethereumProvider is not initialized");
|
||||
}
|
||||
await ethereumProvider.disconnect();
|
||||
resetApp();
|
||||
}, [ethereumProvider]);
|
||||
|
||||
const _subscribeToClientEvents = useCallback(async (_client: Client) => {
|
||||
const _subscribeToProviderEvents = useCallback(async (_client: UniversalProvider) => {
|
||||
if (typeof _client === "undefined") {
|
||||
throw new Error("WalletConnect is not initialized");
|
||||
}
|
||||
|
||||
_client.on(CLIENT_EVENTS.pairing.proposal, async (proposal: PairingTypes.Proposal) => {
|
||||
const { uri } = proposal.signal.params;
|
||||
_client.on("display_uri", async (uri: string) => {
|
||||
console.log("EVENT", "QR Code Modal open");
|
||||
QRCodeModal.open(uri, () => {
|
||||
console.log("EVENT", "QR Code Modal closed");
|
||||
});
|
||||
});
|
||||
|
||||
_client.on(CLIENT_EVENTS.pairing.created, async () => {
|
||||
setPairings(_client.pairing.topics);
|
||||
// Subscribe to session ping
|
||||
_client.on("session_ping", ({ id, topic }: { id: number; topic: string }) => {
|
||||
console.log("EVENT", "session_ping");
|
||||
console.log(id, topic);
|
||||
});
|
||||
|
||||
_client.on(CLIENT_EVENTS.session.updated, (updatedSession: SessionTypes.Settled) => {
|
||||
console.log("EVENT", "session_updated");
|
||||
setAccounts(updatedSession.state.accounts);
|
||||
setSession(updatedSession);
|
||||
// Subscribe to session event
|
||||
_client.on("session_event", ({ event, chainId }: { event: any; chainId: string }) => {
|
||||
console.log("EVENT", "session_event");
|
||||
console.log(event, chainId);
|
||||
});
|
||||
|
||||
_client.on(CLIENT_EVENTS.session.deleted, () => {
|
||||
// Subscribe to session update
|
||||
_client.on(
|
||||
"session_update",
|
||||
({ topic, session }: { topic: string; session: SessionTypes.Struct }) => {
|
||||
console.log("EVENT", "session_updated");
|
||||
setSession(session);
|
||||
},
|
||||
);
|
||||
|
||||
// Subscribe to session delete
|
||||
_client.on("session_delete", ({ id, topic }: { id: number; topic: string }) => {
|
||||
console.log("EVENT", "session_deleted");
|
||||
console.log(id, topic);
|
||||
resetApp();
|
||||
});
|
||||
}, []);
|
||||
@ -133,24 +140,30 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
try {
|
||||
setIsInitializing(true);
|
||||
|
||||
const _client = await Client.init({
|
||||
const provider = await UniversalProvider.init({
|
||||
projectId: DEFAULT_PROJECT_ID,
|
||||
logger: DEFAULT_LOGGER,
|
||||
relayUrl: DEFAULT_RELAY_URL,
|
||||
projectId: DEFAULT_PROJECT_ID,
|
||||
});
|
||||
|
||||
setClient(_client);
|
||||
await _subscribeToClientEvents(_client);
|
||||
setEthereumProvider(provider);
|
||||
setClient(provider.client);
|
||||
await _subscribeToProviderEvents(provider);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
} finally {
|
||||
setIsInitializing(false);
|
||||
}
|
||||
}, [_subscribeToClientEvents]);
|
||||
}, [_subscribeToProviderEvents]);
|
||||
|
||||
const onEnable = useCallback(
|
||||
async (caipChainId: string) => {
|
||||
if (!client) {
|
||||
const createWeb3Provider = useCallback((ethereumProvider: UniversalProvider) => {
|
||||
const web3Provider = new Web3(ethereumProvider);
|
||||
setWeb3Provider(web3Provider);
|
||||
}, []);
|
||||
|
||||
const connect = useCallback(
|
||||
async (caipChainId: string, pairing?: { topic: string }) => {
|
||||
if (!ethereumProvider) {
|
||||
throw new ReferenceError("WalletConnect Client is not initialized.");
|
||||
}
|
||||
|
||||
@ -166,35 +179,97 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
{},
|
||||
);
|
||||
|
||||
// Create WalletConnect Provider
|
||||
const ethereumProvider = new EthereumProvider({
|
||||
chainId: Number(chainId),
|
||||
rpc: {
|
||||
infuraId: DEFAULT_INFURA_ID,
|
||||
custom: customRpcs,
|
||||
const session = await ethereumProvider.connect({
|
||||
namespaces: {
|
||||
eip155: {
|
||||
methods: [
|
||||
"eth_sendTransaction",
|
||||
"eth_signTransaction",
|
||||
"eth_sign",
|
||||
"personal_sign",
|
||||
"eth_signTypedData",
|
||||
],
|
||||
chains: [`eip155:${chainId}`],
|
||||
events: ["chainChanged", "accountsChanged"],
|
||||
rpcMap: customRpcs,
|
||||
},
|
||||
},
|
||||
client,
|
||||
pairingTopic: pairing?.topic,
|
||||
});
|
||||
|
||||
const web3Provider = new Web3(ethereumProvider);
|
||||
|
||||
console.log(ethereumProvider);
|
||||
console.log(web3Provider);
|
||||
|
||||
setEthereumProvider(ethereumProvider);
|
||||
setWeb3Provider(web3Provider);
|
||||
|
||||
createWeb3Provider(ethereumProvider);
|
||||
const _accounts = await ethereumProvider.enable();
|
||||
const _session = await client.session.get(client.session.topics[0]);
|
||||
|
||||
console.log("_accounts", _accounts);
|
||||
setAccounts(_accounts);
|
||||
setSession(_session);
|
||||
setSession(session);
|
||||
setChain(caipChainId);
|
||||
|
||||
QRCodeModal.close();
|
||||
},
|
||||
[ethereumProvider, chainData.eip155, createWeb3Provider],
|
||||
);
|
||||
|
||||
const onSessionConnected = useCallback(
|
||||
async (_session: SessionTypes.Struct) => {
|
||||
if (!ethereumProvider) {
|
||||
throw new ReferenceError("EthereumProvider is not initialized.");
|
||||
}
|
||||
const allNamespaceAccounts = Object.values(_session.namespaces)
|
||||
.map(namespace => namespace.accounts)
|
||||
.flat();
|
||||
const allNamespaceChains = Object.keys(_session.namespaces);
|
||||
|
||||
const chainData = allNamespaceAccounts[0].split(":");
|
||||
const caipChainId = `${chainData[0]}:${chainData[1]}`;
|
||||
console.log("restored caipChainId", caipChainId);
|
||||
setChain(caipChainId);
|
||||
setSession(_session);
|
||||
setAccounts(allNamespaceAccounts.map(account => account.split(":")[2]));
|
||||
console.log("RESTORED", allNamespaceChains, allNamespaceAccounts);
|
||||
createWeb3Provider(ethereumProvider);
|
||||
},
|
||||
[ethereumProvider, createWeb3Provider],
|
||||
);
|
||||
|
||||
const _checkForPersistedSession = useCallback(
|
||||
async (provider: UniversalProvider) => {
|
||||
if (typeof provider === "undefined") {
|
||||
throw new Error("WalletConnect is not initialized");
|
||||
}
|
||||
const pairings = provider.client.pairing.getAll({ active: true });
|
||||
// populates existing pairings to state
|
||||
setPairings(pairings);
|
||||
console.log("RESTORED PAIRINGS: ", pairings);
|
||||
if (typeof session !== "undefined") return;
|
||||
// populates (the last) existing session to state
|
||||
if (ethereumProvider?.session) {
|
||||
const _session = ethereumProvider?.session;
|
||||
console.log("RESTORED SESSION:", _session);
|
||||
await onSessionConnected(_session);
|
||||
return _session;
|
||||
}
|
||||
},
|
||||
[session, ethereumProvider, onSessionConnected],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadChainData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!client) {
|
||||
createClient();
|
||||
}
|
||||
}, [client, createClient]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchBalances = async () => {
|
||||
if (!web3Provider || !accounts) return;
|
||||
|
||||
try {
|
||||
setIsFetchingBalances(true);
|
||||
const _balances = await Promise.all(
|
||||
_accounts.map(async account => {
|
||||
accounts.map(async account => {
|
||||
const balance = await web3Provider.eth.getBalance(account);
|
||||
return {
|
||||
account,
|
||||
@ -216,51 +291,22 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
} finally {
|
||||
setIsFetchingBalances(false);
|
||||
}
|
||||
};
|
||||
|
||||
QRCodeModal.close();
|
||||
},
|
||||
[client, chainData.eip155],
|
||||
);
|
||||
|
||||
const _checkForPersistedSession = useCallback(
|
||||
async (_client: Client) => {
|
||||
if (typeof _client === "undefined") {
|
||||
throw new Error("WalletConnect is not initialized");
|
||||
}
|
||||
// populates existing pairings to state
|
||||
setPairings(_client.pairing.topics);
|
||||
if (typeof session !== "undefined") return;
|
||||
// populates existing session to state (assume only the top one)
|
||||
if (_client.session.topics.length) {
|
||||
const _session = await _client.session.get(_client.session.topics[0]);
|
||||
const [namespace, chainId] = _session.state.accounts[0].split(":");
|
||||
const caipChainId = `${namespace}:${chainId}`;
|
||||
onEnable(caipChainId);
|
||||
}
|
||||
},
|
||||
[session, onEnable],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
loadChainData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!client) {
|
||||
createClient();
|
||||
}
|
||||
}, [client, createClient]);
|
||||
fetchBalances();
|
||||
}, [web3Provider, accounts]);
|
||||
|
||||
useEffect(() => {
|
||||
const getPersistedSession = async () => {
|
||||
if (client && !hasCheckedPersistedSession) {
|
||||
await _checkForPersistedSession(client);
|
||||
setHasCheckedPersistedSession(true);
|
||||
}
|
||||
if (!ethereumProvider) return;
|
||||
await _checkForPersistedSession(ethereumProvider);
|
||||
setHasCheckedPersistedSession(true);
|
||||
};
|
||||
|
||||
getPersistedSession();
|
||||
}, [client, _checkForPersistedSession, hasCheckedPersistedSession]);
|
||||
if (ethereumProvider && chainData && !hasCheckedPersistedSession) {
|
||||
getPersistedSession();
|
||||
}
|
||||
}, [ethereumProvider, chainData, _checkForPersistedSession, hasCheckedPersistedSession]);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
@ -273,8 +319,8 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
client,
|
||||
session,
|
||||
disconnect,
|
||||
connect,
|
||||
chainData,
|
||||
onEnable,
|
||||
web3Provider,
|
||||
}),
|
||||
[
|
||||
@ -287,8 +333,8 @@ export function ClientContextProvider({ children }: { children: ReactNode | Reac
|
||||
client,
|
||||
session,
|
||||
disconnect,
|
||||
connect,
|
||||
chainData,
|
||||
onEnable,
|
||||
web3Provider,
|
||||
],
|
||||
);
|
||||
|
@ -190,6 +190,7 @@ export function setLocaleStorageTestnetFlag(value: boolean): void {
|
||||
}
|
||||
|
||||
export function getLocalStorageTestnetFlag(): boolean {
|
||||
if (typeof window === "undefined") return false;
|
||||
let value = INITIAL_STATE_TESTNET_DEFAULT;
|
||||
const persisted = window.localStorage.getItem(LOCALSTORAGE_KEY_TESTNET);
|
||||
if (!persisted) {
|
||||
|
@ -1,27 +0,0 @@
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import { createGlobalStyle } from "styled-components";
|
||||
import { ClientContextProvider } from "./contexts/ClientContext";
|
||||
|
||||
import App from "./App";
|
||||
import { globalStyle } from "./styles";
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
${globalStyle}
|
||||
`;
|
||||
|
||||
declare global {
|
||||
// tslint:disable-next-line
|
||||
interface Window {
|
||||
blockies: any;
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<ClientContextProvider>
|
||||
<App />
|
||||
</ClientContextProvider>
|
||||
</>,
|
||||
document.getElementById("root"),
|
||||
);
|
@ -9,7 +9,7 @@ import { STable } from "../components/shared";
|
||||
import { SModalContainer, SModalTitle } from "./shared";
|
||||
|
||||
interface PairingModalProps {
|
||||
pairings: PairingTypes.Settled[];
|
||||
pairings: PairingTypes.Struct[];
|
||||
connect: (pairing?: { topic: string }) => Promise<void>;
|
||||
}
|
||||
|
||||
|
3
dapps/react-dapp-v2-with-web3js/src/pages/404.tsx
Normal file
3
dapps/react-dapp-v2-with-web3js/src/pages/404.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function FourOhFour() {
|
||||
return <h1>404 Page Not Found</h1>;
|
||||
}
|
22
dapps/react-dapp-v2-with-web3js/src/pages/_app.tsx
Normal file
22
dapps/react-dapp-v2-with-web3js/src/pages/_app.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import type { AppProps } from "next/app";
|
||||
import { createGlobalStyle } from "styled-components";
|
||||
|
||||
import { ClientContextProvider } from "../contexts/ClientContext";
|
||||
|
||||
import { globalStyle } from "../styles";
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
${globalStyle}
|
||||
`;
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<ClientContextProvider>
|
||||
<Component {...pageProps} />
|
||||
</ClientContextProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyApp;
|
3
dapps/react-dapp-v2-with-web3js/src/pages/_error.tsx
Normal file
3
dapps/react-dapp-v2-with-web3js/src/pages/_error.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function Error() {
|
||||
return <div>An error as occured</div>;
|
||||
}
|
@ -1,25 +1,25 @@
|
||||
import React, { useState } from "react";
|
||||
import { version } from "@walletconnect/client/package.json";
|
||||
import { version } from "@walletconnect/universal-provider/package.json";
|
||||
import * as encoding from "@walletconnect/encoding";
|
||||
import { utils } from "ethers";
|
||||
import { TypedDataField } from "@ethersproject/abstract-signer";
|
||||
import { Transaction } from "@ethereumjs/tx";
|
||||
|
||||
import Banner from "./components/Banner";
|
||||
import Blockchain from "./components/Blockchain";
|
||||
import Column from "./components/Column";
|
||||
import Header from "./components/Header";
|
||||
import Modal from "./components/Modal";
|
||||
import { DEFAULT_MAIN_CHAINS, DEFAULT_TEST_CHAINS } from "./constants";
|
||||
import Banner from "./../components/Banner";
|
||||
import Blockchain from "./../components/Blockchain";
|
||||
import Column from "./../components/Column";
|
||||
import Header from "./../components/Header";
|
||||
import Modal from "./../components/Modal";
|
||||
import { DEFAULT_MAIN_CHAINS, DEFAULT_TEST_CHAINS } from "./../constants";
|
||||
import {
|
||||
AccountAction,
|
||||
eip712,
|
||||
getLocalStorageTestnetFlag,
|
||||
setLocaleStorageTestnetFlag,
|
||||
} from "./helpers";
|
||||
import Toggle from "./components/Toggle";
|
||||
import RequestModal from "./modals/RequestModal";
|
||||
import PingModal from "./modals/PingModal";
|
||||
} from "./../helpers";
|
||||
import Toggle from "./../components/Toggle";
|
||||
import RequestModal from "./../modals/RequestModal";
|
||||
import PingModal from "./../modals/PingModal";
|
||||
import {
|
||||
SAccounts,
|
||||
SAccountsContainer,
|
||||
@ -28,8 +28,8 @@ import {
|
||||
SLanding,
|
||||
SLayout,
|
||||
SToggleContainer,
|
||||
} from "./components/app";
|
||||
import { useWalletConnectClient } from "./contexts/ClientContext";
|
||||
} from "./../components/app";
|
||||
import { useWalletConnectClient } from "./../contexts/ClientContext";
|
||||
|
||||
interface IFormattedRpcResponse {
|
||||
method: string;
|
||||
@ -60,7 +60,7 @@ export default function App() {
|
||||
chainData,
|
||||
isFetchingBalances,
|
||||
isInitializing,
|
||||
onEnable,
|
||||
connect,
|
||||
web3Provider,
|
||||
} = useWalletConnectClient();
|
||||
|
||||
@ -72,10 +72,13 @@ export default function App() {
|
||||
throw new Error("WalletConnect Client is not initialized");
|
||||
}
|
||||
|
||||
if (typeof session === "undefined") {
|
||||
throw new Error("Session is not connected");
|
||||
}
|
||||
|
||||
try {
|
||||
setIsRpcRequestPending(true);
|
||||
const _session = await client.session.get(client.session.topics[0]);
|
||||
await client.session.ping(_session.topic);
|
||||
await client.ping({ topic: session.topic });
|
||||
setRpcResult({
|
||||
address: "",
|
||||
method: "ping",
|
||||
@ -124,7 +127,6 @@ export default function App() {
|
||||
}
|
||||
|
||||
const [address] = await web3Provider.eth.getAccounts();
|
||||
|
||||
const tx = {
|
||||
from: address,
|
||||
to: address,
|
||||
@ -283,16 +285,6 @@ export default function App() {
|
||||
<Banner />
|
||||
<h6>
|
||||
<span>{`Using v${version || "2.0.0-beta"}`}</span>
|
||||
<sup>
|
||||
(
|
||||
<a
|
||||
style={{ textDecoration: "underline" }}
|
||||
href="https://github.com/WalletConnect/web-examples/tree/main/dapps/react-dapp-v2-with-web3js"
|
||||
>
|
||||
outdated
|
||||
</a>{" "}
|
||||
⚠️)
|
||||
</sup>
|
||||
</h6>
|
||||
<SButtonContainer>
|
||||
<h6>Select an Ethereum chain:</h6>
|
||||
@ -301,7 +293,7 @@ export default function App() {
|
||||
<Toggle active={isTestnet} onClick={toggleTestnets} />
|
||||
</SToggleContainer>
|
||||
{chainOptions.map(chainId => (
|
||||
<Blockchain key={chainId} chainId={chainId} chainData={chainData} onClick={onEnable} />
|
||||
<Blockchain key={chainId} chainId={chainId} chainData={chainData} onClick={connect} />
|
||||
))}
|
||||
</SButtonContainer>
|
||||
</SLanding>
|
@ -1,26 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user