Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,135 changes: 1,735 additions & 400 deletions demo/wagmi-react-app/package-lock.json

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions packages/no-modal/src/base/chain/IChainInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,12 @@ export type ConnectorNamespaceType = (typeof CONNECTOR_NAMESPACES)[keyof typeof

export { CHAIN_NAMESPACES, type ChainNamespaceType };
export type CustomChainConfig = ProviderConfig & { fallbackRpcTargets?: string[]; fallbackWsTargets?: string[] };

export type AddEthereumChainConfig = {
chainId: string;
chainName: string;
rpcUrls: string[];
blockExplorerUrls: string[];
nativeCurrency: { name: string; symbol: string; decimals: number };
iconUrls: string[];
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type EIP6963ProviderDetail } from "mipd";

import {
AddEthereumChainConfig,
BaseConnectorLoginParams,
BaseConnectorSettings,
CHAIN_NAMESPACES,
Expand Down Expand Up @@ -155,7 +156,7 @@ class InjectedEvmConnector extends BaseEvmConnector<void> {

public async addChain(chainConfig: CustomChainConfig, _init = false): Promise<void> {
if (!this.injectedProvider) throw WalletLoginError.connectionError("Injected provider is not available");
await this.injectedProvider.request({
await this.injectedProvider.request<AddEthereumChainConfig[], void>({
method: "wallet_addEthereumChain",
params: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getErrorAnalyticsProperties } from "@toruslabs/base-controllers";
import deepmerge from "deepmerge";

import {
AddEthereumChainConfig,
type Analytics,
ANALYTICS_EVENTS,
BaseConnectorLoginParams,
Expand Down Expand Up @@ -261,7 +262,7 @@ class MetaMaskConnector extends BaseEvmConnector<void> {

private async addChain(chainConfig: CustomChainConfig): Promise<void> {
if (!this.metamaskProvider) throw WalletLoginError.connectionError("Injected provider is not available");
await this.metamaskProvider.request({
await this.metamaskProvider.request<AddEthereumChainConfig[], void>({
method: "wallet_addEthereumChain",
params: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { ISignClient, SignClientTypes } from "@walletconnect/types";
import { getAccountsFromNamespaces, parseAccountId } from "@walletconnect/utils";
import { JRPCEngine, JRPCMiddleware, providerErrors, providerFromEngine } from "@web3auth/auth";

import { CHAIN_NAMESPACES, CustomChainConfig, log, WalletLoginError } from "../../base";
import { AddEthereumChainConfig, CHAIN_NAMESPACES, CustomChainConfig, log, WalletLoginError } from "../../base";
import { BaseProvider, BaseProviderConfig, BaseProviderState } from "../../providers/base-provider";
import {
createEthChainSwitchMiddleware,
Expand All @@ -12,7 +12,7 @@ import {
} from "../../providers/ethereum-provider";
import { createSolanaJsonRpcClient as createSolJsonRpcClient, createSolanaMiddleware } from "../../providers/solana-provider";
import { formatChainId } from "./utils";
import { getAccounts, getEthProviderHandlers, getSolProviderHandlers, switchChain } from "./walletConnectV2Utils";
import { addChain, getAccounts, getEthProviderHandlers, getSolProviderHandlers, switchChain } from "./walletConnectV2Utils";

export type WalletConnectV2ProviderConfig = BaseProviderConfig;

Expand Down Expand Up @@ -59,10 +59,10 @@ export class WalletConnectV2Provider extends BaseProvider<BaseProviderConfig, Wa
public async switchChain({ chainId }: { chainId: string }): Promise<void> {
if (!this.connector)
throw providerErrors.custom({ message: "Connector is not initialized, pass wallet connect connector in constructor", code: 4902 });
const currentChainConfig = this.getChain(chainId);
const newChainConfig = this.getChain(chainId);
if (!newChainConfig) throw WalletLoginError.connectionError("Chain config is not available");

const { chainId: currentChainId } = currentChainConfig;
const currentNumChainId = parseInt(currentChainId, 16);
const currentNumChainId = parseInt(this.state.chainId, 16);

await switchChain({ connector: this.connector, chainId: currentNumChainId, newChainId: chainId });

Expand All @@ -72,6 +72,14 @@ export class WalletConnectV2Provider extends BaseProvider<BaseProviderConfig, Wa
this.update({ chainId });
}

public async addChain(chainConfig: AddEthereumChainConfig): Promise<void> {
if (!this.connector)
throw providerErrors.custom({ message: "Connector is not initialized, pass wallet connect connector in constructor", code: 4902 });

const currentNumChainId = parseInt(this.state.chainId, 16);
await addChain({ connector: this.connector, chainId: currentNumChainId, chainConfig });
}

// no need to implement this method in wallet connect v2.
protected async lookupNetwork(_: ISignClient, chainId: string): Promise<string> {
return chainId;
Expand Down Expand Up @@ -136,6 +144,9 @@ export class WalletConnectV2Provider extends BaseProvider<BaseProviderConfig, Wa
const { chainId } = params;
await this.switchChain({ chainId });
},
addChain: async (params: AddEthereumChainConfig): Promise<void> => {
await this.addChain(params);
},
};
const chainSwitchMiddleware = createEthChainSwitchMiddleware(chainSwitchHandlers);
return chainSwitchMiddleware;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,16 @@ class WalletConnectV2Connector extends BaseConnector<void> {
// Session was deleted -> reset the dapp state, clean up from user session, etc.
this.disconnect({ sessionRemovedByWallet: true });
});

this.connector.events.on("session_expire", ({ topic }) => {
Comment thread
chaitanyapotti marked this conversation as resolved.
// Session has expired -> clean up the session
log.info("Session expired event received for topic:", topic);
if (this.activeSession?.topic === topic) {
this.disconnect({ sessionRemovedByWallet: true }).catch((error) => {
log.error("Failed to disconnect expired session", error);
});
}
});
}

private async _getSignedMessage(challenge: string, accounts: string[], chainNamespace: ChainNamespaceType): Promise<string> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { getAccountsFromNamespaces, parseAccountId } from "@walletconnect/utils"
import { type JRPCRequest, providerErrors, rpcErrors } from "@web3auth/auth";
import { EVM_METHOD_TYPES, SOLANA_METHOD_TYPES } from "@web3auth/ws-embed";

import { SOLANA_CAIP_CHAIN_MAP, WalletLoginError } from "../../base";
import { AddEthereumChainConfig, SOLANA_CAIP_CHAIN_MAP, WalletLoginError } from "../../base";
import type { IEthProviderHandlers, MessageParams, TransactionParams, TypedMessageParams } from "../../providers/ethereum-provider";
import type { ISolanaProviderHandlers } from "../../providers/solana-provider";
import { formatChainId } from "./utils";

async function getLastActiveSession(signClient: ISignClient): Promise<SessionTypes.Struct | null> {
if (signClient.session.length) {
Expand Down Expand Up @@ -173,3 +174,20 @@ export async function switchChain({
}): Promise<void> {
await sendJrpcRequest<string, { chainId: string }[]>(connector, `eip155:${chainId}`, "wallet_switchEthereumChain", [{ chainId: newChainId }]);
}

export async function addChain({
connector,
chainId,
chainConfig,
}: {
connector: ISignClient;
chainId: number;
chainConfig: AddEthereumChainConfig;
}): Promise<void> {
const formattedChainId = formatChainId(chainId);
const formattedChainConfig = {
...chainConfig,
chainId: formattedChainId,
};
await sendJrpcRequest<string, AddEthereumChainConfig[]>(connector, `eip155:${chainId}`, "wallet_addEthereumChain", [formattedChainConfig]);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isHexString } from "@ethereumjs/util";
import { JRPCEngine, JRPCMiddleware, providerErrors, providerFromEngine, rpcErrors } from "@web3auth/auth";

import { CHAIN_NAMESPACES, CustomChainConfig, WalletInitializationError } from "../../../../base";
import { AddEthereumChainConfig, CHAIN_NAMESPACES, CustomChainConfig, WalletInitializationError } from "../../../../base";
import { BaseProvider, BaseProviderConfig, BaseProviderState } from "../../../../providers/base-provider";
import {
createEthChainSwitchMiddleware,
Expand Down Expand Up @@ -148,6 +148,30 @@ export class EthereumSigningProvider extends BaseProvider<
await this.setupProvider(this.state.signMethods, params.chainId);
}

public async addChain(chainConfig: AddEthereumChainConfig): Promise<void> {
if (!this._providerEngineProxy) throw providerErrors.custom({ message: "Provider is not initialized", code: 4902 });
if (!this.state.signMethods) {
throw providerErrors.custom({ message: "sign methods are undefined", code: 4902 });
}
// find existing chain config with the same chainId
const existingChain = this.config.chains.find((chain) => chain.chainId === chainConfig.chainId);
if (existingChain) {
return;
}
// add the chain config to the config
this.config.chains.push({
chainId: chainConfig.chainId,
displayName: chainConfig.chainName,
rpcTarget: chainConfig.rpcUrls[0],
chainNamespace: CHAIN_NAMESPACES.EIP155,
blockExplorerUrl: chainConfig.blockExplorerUrls[0],
logo: chainConfig.iconUrls[0],
tickerName: chainConfig.nativeCurrency.name,
ticker: chainConfig.nativeCurrency.symbol,
decimals: chainConfig.nativeCurrency.decimals,
});
}

protected async lookupNetwork(_: ProviderParams, chainId: string): Promise<string> {
if (!this._providerEngineProxy) throw providerErrors.custom({ message: "Provider is not initialized", code: 4902 });
if (!chainId) throw rpcErrors.invalidParams("chainId is required while lookupNetwork");
Expand All @@ -168,6 +192,9 @@ export class EthereumSigningProvider extends BaseProvider<
const { chainId } = params;
await this.switchChain({ chainId });
},
addChain: async (params: AddEthereumChainConfig): Promise<void> => {
await this.addChain(params);
},
};
const chainSwitchMiddleware = createEthChainSwitchMiddleware(chainSwitchHandlers);
return chainSwitchMiddleware;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
rpcErrors,
} from "@web3auth/auth";

import { AddEthereumChainConfig } from "../../../base";
import { IEthChainSwitchHandlers, IEthProviderHandlers } from "./interfaces";
import { createWalletMiddleware } from "./walletMidddleware";

Expand Down Expand Up @@ -40,14 +41,21 @@ export function createEthMiddleware(providerHandlers: IEthProviderHandlers): JRP
return ethMiddleware;
}

export function createEthChainSwitchMiddleware({ switchChain }: IEthChainSwitchHandlers): JRPCMiddleware<unknown, unknown> {
export function createEthChainSwitchMiddleware({ switchChain, addChain }: IEthChainSwitchHandlers): JRPCMiddleware<unknown, unknown> {
async function updateChain(req: JRPCRequest<{ chainId: string }[]>, res: JRPCResponse<unknown>): Promise<void> {
const chainParams = req.params?.length ? req.params[0] : undefined;
if (!chainParams) throw rpcErrors.invalidParams("Missing chainId");
res.result = await switchChain(chainParams);
}

async function addChainConfig(req: JRPCRequest<AddEthereumChainConfig[]>, res: JRPCResponse<unknown>): Promise<void> {
const chainConfig = req.params?.length ? req.params[0] : undefined;
if (!chainConfig) throw rpcErrors.invalidParams("Missing chainConfig");
res.result = await addChain(chainConfig);
}

return createScaffoldMiddleware({
wallet_switchEthereumChain: createAsyncMiddleware(updateChain) as JRPCMiddleware<unknown, unknown>,
wallet_addEthereumChain: createAsyncMiddleware(addChainConfig) as JRPCMiddleware<unknown, unknown>,
});
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import type { JRPCRequest } from "@web3auth/auth";
import type { TransactionLike, TypedDataDomain, TypedDataField } from "ethers";

import { AddEthereumChainConfig } from "../../../base";
export interface IEthAccountHandlers {
updatePrivatekey: (params: { privateKey: string }) => Promise<void>;
}

export interface IEthChainSwitchHandlers {
switchChain: (params: { chainId: string }) => Promise<void>;
addChain: (params: AddEthereumChainConfig) => Promise<void>;
}

export type TransactionParams<A = string> = TransactionLike<A> & { input?: string };
Expand Down