Add WalletConnect support

Co-authored-by: Njoku Emmanuel <kalunjoku123@gmail.com>
Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier
2021-08-03 16:25:16 +02:00
co-authored by Njoku Emmanuel
parent 767355b782
commit 400a2df0a4
15 changed files with 2272 additions and 105 deletions
+140 -36
View File
@@ -1,9 +1,13 @@
import { writable } from "svelte/store";
import type { Writable } from "svelte/store";
import { ethers } from "ethers";
import type { TypedDataSigner } from '@ethersproject/abstract-signer';
import SafeServiceClient from "@gnosis.pm/safe-service-client";
import CeramicClient from "@ceramicnetwork/http-client";
import { IDX } from "@ceramicstudio/idx";
import WalletConnect from "@walletconnect/client";
import config from "@app/config.json";
import { WalletConnectSigner } from "./WalletConnectSigner";
declare global {
interface Window {
@@ -12,6 +16,15 @@ declare global {
}
}
/// Gas limits for various transactions.
const gasLimits = {
createOrg: 1_200_000,
};
export type WalletConnectState =
{ state: "close" }
| { state: "open"; uri: string; onClose: any };
export class Config {
network: { name: string; chainId: number };
registrar: { address: string; domain: string };
@@ -21,7 +34,14 @@ export class Config {
orgs: { subgraph: string; contractHash: string };
gasLimits: { createOrg: number };
provider: ethers.providers.JsonRpcProvider;
signer: ethers.Signer & TypedDataSigner | null;
signer: ethers.Signer & TypedDataSigner | WalletConnectSigner | null;
walletConnect: {
client: WalletConnect;
bridge: string;
signer: WalletConnectSigner;
state: Writable<WalletConnectState>;
};
metamaskSigner: ethers.Signer & TypedDataSigner | null;
safe: {
api?: string;
client?: SafeServiceClient;
@@ -36,29 +56,31 @@ export class Config {
constructor(
network: { name: string; chainId: number },
provider: ethers.providers.JsonRpcProvider | null,
signer: ethers.Signer & TypedDataSigner | null,
provider: ethers.providers.JsonRpcProvider,
metamaskSigner: ethers.Signer & TypedDataSigner | null,
) {
const cfg = (<Record<string, any>> config)[network.name];
const api = config.radicle.api;
if (! cfg) {
throw `Network ${network.name} is not supported`;
}
// Use Alchemy in production, on mainnet.
provider = network.name === "homestead" && import.meta.env.PROD
? new ethers.providers.AlchemyProvider(network.name, config.alchemy.key)
: provider;
if (! provider) {
throw `No Web3 provider available.`;
}
const cfg = (<Record<string, any>> config)[network.name];
const ceramic = new CeramicClient(config.ceramic.api);
const idx = new IDX({ ceramic });
const walletConnectState = writable<WalletConnectState>(
{ state: "close" }
);
const wc = Config.initializeWalletConnect(
config.walletConnect.bridge,
walletConnectState,
provider
);
this.network = network;
this.metamaskSigner = metamaskSigner;
this.walletConnect = {
bridge: config.walletConnect.bridge,
client: wc.connector,
signer: wc.signer,
state: walletConnectState,
};
this.seed = { api };
this.registrar = cfg.registrar;
this.radToken = cfg.radToken;
@@ -70,7 +92,7 @@ export class Config {
? new SafeServiceClient(this.safe.api)
: undefined;
this.provider = provider;
this.signer = signer;
this.signer = null;
this.gasLimits = gasLimits;
this.abi = config.abi;
this.idx = { client: idx };
@@ -83,6 +105,10 @@ export class Config {
);
}
setSigner(signer: ethers.Signer & TypedDataSigner | WalletConnectSigner): void {
this.signer = signer;
}
// Return the config with an overwritten seed URL.
withSeed(seed: string): Config {
const cfg = {} as Config;
@@ -91,31 +117,109 @@ export class Config {
return cfg;
}
}
/// Gas limits for various transactions.
const gasLimits = {
createOrg: 1_200_000,
};
getWalletConnectSigner(): WalletConnectSigner {
if (this.walletConnect.client.connected) {
this.setSigner(this.walletConnect.signer);
return this.walletConnect.signer;
}
const wc = Config.initializeWalletConnect(
this.walletConnect.bridge,
this.walletConnect.state,
this.provider
);
this.walletConnect.client = wc.connector;
this.walletConnect.signer = wc.signer;
this.setSigner(wc.signer);
return wc.signer;
}
static initializeWalletConnect(
bridge: string,
state: Writable<WalletConnectState>,
provider: ethers.providers.JsonRpcProvider
): {
connector: WalletConnect;
signer: WalletConnectSigner;
} {
const walletConnect = new WalletConnect({
bridge: config.walletConnect.bridge,
qrcodeModal: {
open: (uri: string, onClose) => {
state.set({ state: "open", uri, onClose });
},
close: () => {
state.set({ state: "close" });
}
},
clientMeta: {
name: "Radicle",
description: "Radicle Interface",
url: window.location.host,
icons: [
`${window.location.protocol}//${window.location.host}/favicon.ico`,
`${window.location.protocol}//${window.location.host}/favicon.svg`
],
}
});
walletConnect.on("modal_closed", () => {
state.set({ state: "close" });
});
const walletConnectSigner = new WalletConnectSigner(walletConnect, provider);
return {
connector: walletConnect,
signer: walletConnectSigner,
};
}
}
function isMetamaskInstalled(): boolean {
const { ethereum } = window;
return Boolean(ethereum && ethereum.isMetaMask);
}
export async function getConfig(): Promise<Config> {
let config: Config;
if (isMetamaskInstalled()) {
const metamask = new ethers.providers.Web3Provider(window.ethereum);
const network = await metamask.ready;
config = new Config(network, metamask, metamask.getSigner());
function getProvider(
network: { name: string },
config: Record<string, any>,
metamask: ethers.providers.JsonRpcProvider | null,
): ethers.providers.JsonRpcProvider {
// Use Alchemy in production, on mainnet. Otherwise use Metamask if installed.
if (network.name === "homestead" && import.meta.env.PROD) {
return new ethers.providers.AlchemyProvider(network.name, config.alchemy.key);
} else if (metamask) {
return metamask;
} else {
// If we don't have Metamask, we default to Homestead.
const network = { name: "homestead", chainId: 1 };
config = new Config(network, null, null);
throw new Error("No Web3 provider available.");
}
console.log(config);
}
return config;
export async function getConfig(): Promise<Config> {
const metamask = isMetamaskInstalled()
? new ethers.providers.Web3Provider(window.ethereum)
: null;
const metamaskSigner = metamask?.getSigner() || null;
let network = { name: "homestead", chainId: 1 };
if (metamask) {
// If Metamask is detected, we use the network configured there.
network = await metamask.ready;
}
const networkConfig = (<Record<string, any>> config)[network.name];
if (! networkConfig) {
throw new Error(`Network ${network.name} is not supported`);
}
const provider = getProvider(network, networkConfig, metamask);
const cfg = new Config(
network,
provider,
metamaskSigner,
);
console.log(cfg, "config");
return cfg;
}